fix(web): справочники — выбранная ГК, счётчик ЮЛ, ошибки у полей (ревью #179, AURORA-1671)
- Страница ГК: снова выбирается первая ГК списка; список ЮЛ без выбранной ГК не запрашивается (иначе уходил запрос по всем ГК), «ЮЛ вне ГК» грузится без ГК. - Выбранная ГК обновляется после перезагрузки списка ГК — счётчик ЮЛ в шапке и на вкладке после создания/переноса/вывода ЮЛ. - Загрузка ГСК/ГСЗ: try/finally — ошибка не оставляет вечный спиннер. - Форма ЮЛ: INVALID_INN/INVALID_KPP/INN_EXISTS/CRM_ID_EXISTS — у поля; КПП приводится к верхнему регистру при вводе. - crm_id ГСК/ГСЗ — string | null; колонка «ID в CRM»; LE_PAGE вместо литерала; возвращён комментарий в фильтре шахматки. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
630a616bf1
commit
3aad670510
@ -1,3 +1,4 @@
|
||||
/* Теги выбранных значений в селектах: фильтр шахматки, синонимы ЮЛ */
|
||||
.custom-select-selection-item .ant-select-selection-item {
|
||||
background-color: #ecfdf3 !important;
|
||||
color: #1f5f35 !important;
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Space } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ApiError } from '../api/client';
|
||||
import PageTitle from '../components/PageTitle';
|
||||
import { createCatalogueJur } from '../services/api/directories/catalogue_jur';
|
||||
import { createCompanyGroup, fetchRelatedGroups } from '../services/api/directories/company_groups';
|
||||
import { createRelatedGroup } from '../services/api/directories/related-groups';
|
||||
import { setActiveCompanyGroup } from '../store/slices/catalogueJurSlice/catalogueJurSlice';
|
||||
import { getCatalogueJurThunk } from '../store/slices/catalogueJurSlice/thunks';
|
||||
import { getCompanyGroupsThunk } from '../store/slices/companyGroupsSlice/thunks';
|
||||
import { useAppDispatch, useAppSelector } from '../store/store';
|
||||
@ -12,6 +14,7 @@ import type { RelatedGroupOut } from '../types/api';
|
||||
import type { CatalogueJurFormProps } from '../types/catalogueJur/catalogueJur';
|
||||
import type { CreateCompanyGroupProps } from '../types/companyGroups/companyGroups';
|
||||
import type { CreateEntityKind, RelatedGroupFormType } from '../types/relatedGroup/relatedGroup';
|
||||
import { useNotificationApi } from '../utils/useNotification';
|
||||
import { CataloguGroupTabs, type CatalogueGroupTabKey } from '../widgets/CataloguGroupTabs/CataloguGroupTabs';
|
||||
import { CompanyGroupHeader } from '../widgets/CatalogueGroupHeader/CatalogueGroupHeader';
|
||||
import { CatalogueJurForm } from '../widgets/CatalogueJurForm/CatalogueJurForm';
|
||||
@ -24,6 +27,7 @@ import { RelatedGroupTable } from '../widgets/RelatedGroupTable/RelatedGroupTabl
|
||||
|
||||
export function CatalogueGroup() {
|
||||
const dispatch = useAppDispatch();
|
||||
const notification = useNotificationApi();
|
||||
|
||||
const isGettingArchive = useAppSelector((state) => state.catalogue_jur.isGettingArchive);
|
||||
|
||||
@ -35,6 +39,7 @@ export function CatalogueGroup() {
|
||||
const [gszData, setGszData] = useState<RelatedGroupOut[]>([]);
|
||||
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup);
|
||||
const relatedGroupFilter = useAppSelector((state) => state.catalogue_jur.relatedGroupFilter);
|
||||
const rawRows = useAppSelector((state) => state.company_groups.raw_rows);
|
||||
|
||||
async function createJurHandler(values: CatalogueJurFormProps) {
|
||||
await createCatalogueJur(values);
|
||||
@ -68,12 +73,19 @@ export function CatalogueGroup() {
|
||||
async function getRelatedGroupsHandler() {
|
||||
if (activeCompanyGroup?.id) {
|
||||
setRelatedGroupLoading(true);
|
||||
const res = await fetchRelatedGroups(activeCompanyGroup?.id);
|
||||
const gsks = res.filter((elem) => elem.kind === 'gsk');
|
||||
const gszs = res.filter((elem) => elem.kind === 'gsz');
|
||||
setGskData(gsks);
|
||||
setGszData(gszs);
|
||||
setRelatedGroupLoading(false);
|
||||
try {
|
||||
const res = await fetchRelatedGroups(activeCompanyGroup?.id);
|
||||
const gsks = res.filter((elem) => elem.kind === 'gsk');
|
||||
const gszs = res.filter((elem) => elem.kind === 'gsz');
|
||||
setGskData(gsks);
|
||||
setGszData(gszs);
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
notification.error({ description: e.message });
|
||||
}
|
||||
} finally {
|
||||
setRelatedGroupLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,6 +93,13 @@ export function CatalogueGroup() {
|
||||
getRelatedGroupsHandler();
|
||||
}, [activeCompanyGroup]);
|
||||
|
||||
// Без выбранной ГК таблица ЮЛ не грузится — выбираем первую ГК списка
|
||||
useEffect(() => {
|
||||
if (!activeCompanyGroup && rawRows?.length) {
|
||||
dispatch(setActiveCompanyGroup(rawRows[0]));
|
||||
}
|
||||
}, [rawRows, activeCompanyGroup, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTab('legal_entities');
|
||||
}, [relatedGroupFilter]);
|
||||
|
||||
@ -7,9 +7,10 @@ import type {
|
||||
MoveCatalogueJurProps,
|
||||
WithdrawCatalogueJurProps,
|
||||
} from '../../../types/catalogueJur/catalogueJur';
|
||||
import { LE_PAGE } from './company_groups';
|
||||
|
||||
export async function fetchCatalogueJur(params: GetCatalogueJurPropsI) {
|
||||
const res = await api.get<Page<ICatalogueJur>>('/directories/legal-entities', { limit: 200, offset: 0, ...params });
|
||||
const res = await api.get<Page<ICatalogueJur>>('/directories/legal-entities', { limit: LE_PAGE, offset: 0, ...params });
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { type PayloadAction, createSlice } from '@reduxjs/toolkit';
|
||||
import type { RelatedGroupOut } from '../../../types/api';
|
||||
import type { ICompanyGroup } from '../../../types/companyGroups/companyGroups';
|
||||
import { getCompanyGroupsThunk } from '../companyGroupsSlice/thunks';
|
||||
import { getCatalogueJurThunk } from './thunks';
|
||||
import type { CatalogueJurSliceType } from './types';
|
||||
|
||||
@ -43,6 +44,12 @@ const catalogueJurSlice = createSlice({
|
||||
.addCase(getCatalogueJurThunk.rejected, (state, action) => {
|
||||
state.error = action.payload ?? 'Неизвестная ошибка';
|
||||
state.loading = false;
|
||||
})
|
||||
// Выбранная ГК — копия строки списка ГК: после перезагрузки списка берём свежую (счётчик ЮЛ в шапке и вкладке)
|
||||
.addCase(getCompanyGroupsThunk.fulfilled, (state, action) => {
|
||||
if (state.activeCompanyGroup?.id) {
|
||||
state.activeCompanyGroup = action.payload.find((elem) => elem.id === state.activeCompanyGroup?.id) ?? null;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -375,7 +375,7 @@ export interface ConsolidationCreate {
|
||||
export interface RelatedGroupOut {
|
||||
id: string;
|
||||
code: string;
|
||||
crm_id: string;
|
||||
crm_id: string | null;
|
||||
company_group_id: string;
|
||||
kind: 'gsk' | 'gsz';
|
||||
name: string;
|
||||
@ -390,7 +390,6 @@ export interface RelatedGroupMemberOut {
|
||||
}
|
||||
export interface RelatedGroupDetail extends RelatedGroupOut {
|
||||
members: RelatedGroupMemberOut[];
|
||||
crm_id: string;
|
||||
}
|
||||
export interface LegalEntityBrief {
|
||||
id: string;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
export type RelatedGroupFormType = {
|
||||
name: string;
|
||||
created_date: string | null;
|
||||
crm_id: string;
|
||||
crm_id: string | null;
|
||||
kind?: 'gsk' | 'gsz';
|
||||
company_group_id?: string;
|
||||
};
|
||||
|
||||
@ -17,6 +17,14 @@ type Props = {
|
||||
jurId?: string;
|
||||
};
|
||||
|
||||
// Ошибки сервера, относящиеся к одному полю, показываем у этого поля
|
||||
const FIELD_BY_ERROR_CODE: Record<string, keyof CatalogueJurFormProps> = {
|
||||
INN_EXISTS: 'inn',
|
||||
INVALID_INN: 'inn',
|
||||
INVALID_KPP: 'kpp',
|
||||
CRM_ID_EXISTS: 'crm_id',
|
||||
};
|
||||
|
||||
export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId }) => {
|
||||
const [form] = Form.useForm<CatalogueJurFormProps>();
|
||||
const notification = useNotificationApi();
|
||||
@ -116,21 +124,9 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof ApiError) {
|
||||
if (e.code === 'INN_EXISTS') {
|
||||
form.setFields([
|
||||
{
|
||||
name: 'inn',
|
||||
errors: [e.message],
|
||||
},
|
||||
]);
|
||||
}
|
||||
if (e.code === 'CRM_ID_EXISTS') {
|
||||
form.setFields([
|
||||
{
|
||||
name: 'crm_id',
|
||||
errors: [e.message],
|
||||
},
|
||||
]);
|
||||
const field = e.code ? FIELD_BY_ERROR_CODE[e.code] : undefined;
|
||||
if (field) {
|
||||
form.setFields([{ name: field, errors: [e.message] }]);
|
||||
}
|
||||
notification.error({ description: e.message });
|
||||
}
|
||||
@ -194,7 +190,11 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name='kpp' label='КПП' rules={[{ pattern: /^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$/, message: 'Некорректный формат' }]}>
|
||||
<Form.Item
|
||||
name='kpp'
|
||||
label='КПП'
|
||||
normalize={(value?: string) => value?.toUpperCase()}
|
||||
rules={[{ pattern: /^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$/, message: 'Некорректный формат' }]}>
|
||||
<Input placeholder='000000000' maxLength={9} disabled={type === 'ИП'} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
@ -128,7 +128,10 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getCatalogueJur();
|
||||
// «ЮЛ вне ГК» грузится без выбранной ГК; список ГК — только по выбранной (без неё запрос ушёл бы по всем ГК)
|
||||
if (outside_group || activeCompanyGroup?.id) {
|
||||
getCatalogueJur();
|
||||
}
|
||||
}, [isGettingArchive, activeCompanyGroup, outside_group]);
|
||||
|
||||
const extraColumns: ColumnsType<ICatalogueJur> = useMemo(
|
||||
|
||||
@ -15,6 +15,7 @@ interface ConsolidationChessTableFilterI {
|
||||
credit: string[];
|
||||
onCreditChange: (val: string[]) => void;
|
||||
allCompanies: MatrixCompany[];
|
||||
/** Год и квартал версии — только для показа: версия задаётся адресом. */
|
||||
year?: number;
|
||||
quarter?: Quarter;
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
|
||||
form.setFieldsValue({
|
||||
crm_id: data?.crm_id,
|
||||
name: data?.name,
|
||||
created_date: data?.created_date ?? (relatedGroupId ? null : dayjs().format('YYYY-MM-DD')),
|
||||
created_date: data?.created_date ?? null,
|
||||
company_group_id: data?.company_group_id,
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
@ -15,7 +15,7 @@ export const relateGroupConfig: TableConfig<RelatedGroupOut> = {
|
||||
},
|
||||
{
|
||||
key: 'crm_id',
|
||||
title: 'CRM ID',
|
||||
title: 'ID в CRM',
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user