fix(vsp): архивные ССП в фильтре и выгрузке для «Закрытые»/«Все» — закрытые ВСП архивного ССП выгружаются (AURORA-1883, #330, I-1)
Окно создания и правки ВСП по-прежнему предлагает только активные ССП. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f1fca5d12c
commit
a4a674fe60
@ -13,7 +13,7 @@ import { PageContainer } from '../../components/common/PageContainer';
|
|||||||
import DictVspFilters from './components/DictVspFilters';
|
import DictVspFilters from './components/DictVspFilters';
|
||||||
import DictVspModals from './components/DictVspModals';
|
import DictVspModals from './components/DictVspModals';
|
||||||
import TableDictVsp from './components/TableDictVsp';
|
import TableDictVsp from './components/TableDictVsp';
|
||||||
import { buildVspListParams, vspMatchesStatus } from './utils/vspListParams';
|
import { buildVspListParams, sspsForStatus, vspMatchesStatus } from './utils/vspListParams';
|
||||||
|
|
||||||
const DictVspInfo = () => {
|
const DictVspInfo = () => {
|
||||||
const portalContent = (
|
const portalContent = (
|
||||||
@ -40,6 +40,16 @@ function DictVspPage() {
|
|||||||
const modalsRef = useRef(null);
|
const modalsRef = useRef(null);
|
||||||
|
|
||||||
const listFilter = useMemo(() => ({ status, ...closeDates }), [status, closeDates]);
|
const listFilter = useMemo(() => ({ status, ...closeDates }), [status, closeDates]);
|
||||||
|
const visibleSsps = useMemo(() => sspsForStatus(ssps, status), [ssps, status]);
|
||||||
|
// Создание и правка ВСП — только на активном ССП (бэк отклоняет архивный).
|
||||||
|
const activeSsps = useMemo(() => sspsForStatus(ssps, 'active'), [ssps]);
|
||||||
|
|
||||||
|
// Выбранный архивный ССП исчезает из фильтра при возврате в «Действующие» — сбрасываем выбор.
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedSsp && !visibleSsps.some((ssp) => ssp.id === selectedSsp.id)) {
|
||||||
|
setSelectedSsp(null);
|
||||||
|
}
|
||||||
|
}, [selectedSsp, visibleSsps]);
|
||||||
|
|
||||||
const loadVspList = useCallback(async () => {
|
const loadVspList = useCallback(async () => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
@ -70,7 +80,8 @@ function DictVspPage() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const isAdmin = user.role_id === ROLES_NAME_ID.admin;
|
const isAdmin = user.role_id === ROLES_NAME_ID.admin;
|
||||||
const sspResponse = isAdmin ? await SspApi.getAll({ is_active: true }) : null;
|
// AURORA-1883: все ССП, включая архивные, — для закрытых ВСП; в «Действующих» архивные скрываются (sspsForStatus).
|
||||||
|
const sspResponse = isAdmin ? await SspApi.getAll() : null;
|
||||||
|
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
if (!sspResponse.success) {
|
if (!sspResponse.success) {
|
||||||
@ -176,7 +187,7 @@ function DictVspPage() {
|
|||||||
<Stack sx={{ alignItems: 'stretch', width: '100%' }}>
|
<Stack sx={{ alignItems: 'stretch', width: '100%' }}>
|
||||||
<Box sx={{ width: '100%' }}>
|
<Box sx={{ width: '100%' }}>
|
||||||
<DictVspFilters
|
<DictVspFilters
|
||||||
ssps={ssps}
|
ssps={visibleSsps}
|
||||||
onSspChange={setSelectedSsp}
|
onSspChange={setSelectedSsp}
|
||||||
selectedSsp={selectedSsp}
|
selectedSsp={selectedSsp}
|
||||||
showSsp={user.role_id !== ROLES_NAME_ID.executor_rf}
|
showSsp={user.role_id !== ROLES_NAME_ID.executor_rf}
|
||||||
@ -196,7 +207,8 @@ function DictVspPage() {
|
|||||||
/>
|
/>
|
||||||
<DictVspModals
|
<DictVspModals
|
||||||
ref={modalsRef}
|
ref={modalsRef}
|
||||||
ssps={ssps}
|
ssps={activeSsps}
|
||||||
|
exportSsps={visibleSsps}
|
||||||
onAdd={handleAdd}
|
onAdd={handleAdd}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import ModalEditAddVsp from './Modals/ModalEditAddVsp';
|
|||||||
import ModalExport from './Modals/ModalExport';
|
import ModalExport from './Modals/ModalExport';
|
||||||
|
|
||||||
// Состояние модалок живёт здесь, а не в таблице: открытие/закрытие не перерисовывает MRT.
|
// Состояние модалок живёт здесь, а не в таблице: открытие/закрытие не перерисовывает MRT.
|
||||||
const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps, listFilter }, ref) => {
|
const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps, exportSsps, listFilter }, ref) => {
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const [modalExportOpen, setModalExportOpen] = useState(false);
|
const [modalExportOpen, setModalExportOpen] = useState(false);
|
||||||
const [editingItem, setEditingItem] = useState(null);
|
const [editingItem, setEditingItem] = useState(null);
|
||||||
@ -55,7 +55,7 @@ const DictVspModals = forwardRef(({ onAdd, onEdit, onDelete, ssps, listFilter },
|
|||||||
loading={saveLoading}
|
loading={saveLoading}
|
||||||
ssps={ssps}
|
ssps={ssps}
|
||||||
/>
|
/>
|
||||||
<ModalExport open={modalExportOpen} onClose={closeExport} ssps={ssps} listFilter={listFilter} />
|
<ModalExport open={modalExportOpen} onClose={closeExport} ssps={exportSsps ?? ssps} listFilter={listFilter} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,3 +21,9 @@ export function vspMatchesStatus(vsp, status) {
|
|||||||
if (status === 'closed') return vsp.is_active === false;
|
if (status === 'closed') return vsp.is_active === false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** ССП для фильтра и выгрузки: архивные — только при «Закрытые»/«Все» (закрытые ВСП бывают на архивных ССП). */
|
||||||
|
export function sspsForStatus(ssps, status) {
|
||||||
|
const list = ssps || [];
|
||||||
|
return status === 'active' ? list.filter((ssp) => ssp.is_active !== false) : list;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import { buildVspListParams, vspMatchesStatus } from './vspListParams.js';
|
import { buildVspListParams, sspsForStatus, vspMatchesStatus } from './vspListParams.js';
|
||||||
|
|
||||||
// AURORA-1883: параметры запроса справочника ВСП по статусу и периоду закрытия.
|
// AURORA-1883: параметры запроса справочника ВСП по статусу и периоду закрытия.
|
||||||
test('active status sends no close period', () => {
|
test('active status sends no close period', () => {
|
||||||
@ -31,3 +31,24 @@ test('vspMatchesStatus follows is_active', () => {
|
|||||||
assert.equal(vspMatchesStatus({ is_active: true }, 'closed'), false);
|
assert.equal(vspMatchesStatus({ is_active: true }, 'closed'), false);
|
||||||
assert.equal(vspMatchesStatus({ is_active: false }, 'all'), true);
|
assert.equal(vspMatchesStatus({ is_active: false }, 'all'), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// AURORA-1883, финальное ревью I-1: закрытые ВСП архивного ССП должны фильтроваться и выгружаться.
|
||||||
|
test('sspsForStatus hides archived SSPs only for active VSPs', () => {
|
||||||
|
const ssps = [
|
||||||
|
{ id: 1, is_active: true },
|
||||||
|
{ id: 2, is_active: false },
|
||||||
|
];
|
||||||
|
assert.deepEqual(
|
||||||
|
sspsForStatus(ssps, 'active').map((s) => s.id),
|
||||||
|
[1],
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
sspsForStatus(ssps, 'closed').map((s) => s.id),
|
||||||
|
[1, 2],
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
sspsForStatus(ssps, 'all').map((s) => s.id),
|
||||||
|
[1, 2],
|
||||||
|
);
|
||||||
|
assert.deepEqual(sspsForStatus(undefined, 'active'), []);
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user