Compare commits

..

No commits in common. "ed0052bad0c5bda03bce91cb01925204ce0d7718" and "6433c23cf0fa09934e31988d4fdb96d0f3fcd434" have entirely different histories.

20 changed files with 174 additions and 160 deletions

View File

@ -1,7 +0,0 @@
/* Теги выбранных значений в селектах: фильтр шахматки, синонимы ЮЛ */
.custom-select-selection-item .ant-select-selection-item {
background-color: #ecfdf3 !important;
color: #1f5f35 !important;
box-shadow: inset 0 0 0 1px #2ea64b38 !important;
border-radius: 999px !important;
}

View File

@ -6,8 +6,8 @@ import PageTitle from '../components/PageTitle';
import { createCatalogueJur } from '../services/api/directories/catalogue_jur'; import { createCatalogueJur } from '../services/api/directories/catalogue_jur';
import { createCompanyGroup, fetchRelatedGroups } from '../services/api/directories/company_groups'; import { createCompanyGroup, fetchRelatedGroups } from '../services/api/directories/company_groups';
import { createRelatedGroup } from '../services/api/directories/related-groups'; import { createRelatedGroup } from '../services/api/directories/related-groups';
import { setActiveCompanyGroup } from '../store/slices/catalogueJurSlice/catalogueJurSlice';
import { getCatalogueJurThunk } from '../store/slices/catalogueJurSlice/thunks'; import { getCatalogueJurThunk } from '../store/slices/catalogueJurSlice/thunks';
import { setActiveCompanyGroup } from '../store/slices/companyGroupsSlice/companyGroupsSlice';
import { getCompanyGroupsThunk } from '../store/slices/companyGroupsSlice/thunks'; import { getCompanyGroupsThunk } from '../store/slices/companyGroupsSlice/thunks';
import { useAppDispatch, useAppSelector } from '../store/store'; import { useAppDispatch, useAppSelector } from '../store/store';
import type { RelatedGroupOut } from '../types/api'; import type { RelatedGroupOut } from '../types/api';
@ -37,37 +37,61 @@ export function CatalogueGroup() {
const [activeTab, setActiveTab] = useState<CatalogueGroupTabKey>('legal_entities'); const [activeTab, setActiveTab] = useState<CatalogueGroupTabKey>('legal_entities');
const [gskData, setGskData] = useState<RelatedGroupOut[]>([]); const [gskData, setGskData] = useState<RelatedGroupOut[]>([]);
const [gszData, setGszData] = useState<RelatedGroupOut[]>([]); const [gszData, setGszData] = useState<RelatedGroupOut[]>([]);
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup); const activeCompanyGroup = useAppSelector((state) => state.company_groups.activeCompanyGroup);
const relatedGroupFilter = useAppSelector((state) => state.catalogue_jur.relatedGroupFilter); const relatedGroupFilter = useAppSelector((state) => state.catalogue_jur.relatedGroupFilter);
const rawRows = useAppSelector((state) => state.company_groups.raw_rows); const rawRows = useAppSelector((state) => state.company_groups.raw_rows);
async function createJurHandler(values: CatalogueJurFormProps) { async function createJurHandler(values: CatalogueJurFormProps) {
await createCatalogueJur(values); try {
dispatch(getCompanyGroupsThunk()); await createCatalogueJur(values);
dispatch( dispatch(getCompanyGroupsThunk());
getCatalogueJurThunk({ dispatch(
closed: isGettingArchive, getCatalogueJurThunk({
outside_group: false, closed: isGettingArchive,
company_group_id: activeCompanyGroup?.id, outside_group: false,
}), company_group_id: activeCompanyGroup?.id,
); }),
setKindForm(null); );
setKindForm(null);
} catch (e) {
if (e instanceof ApiError) {
notification.error({
description: e.message,
});
}
}
} }
async function createRelatedGroupHandler(values: RelatedGroupFormType) { async function createRelatedGroupHandler(values: RelatedGroupFormType) {
if (values.company_group_id) { if (values.company_group_id) {
await createRelatedGroup(values.company_group_id, { try {
...values, await createRelatedGroup(values.company_group_id, {
company_group_id: undefined, ...values,
}); company_group_id: undefined,
getRelatedGroupsHandler(); });
setKindForm(null); getRelatedGroupsHandler();
setKindForm(null);
} catch (e) {
if (e instanceof ApiError) {
notification.error({
description: e.message,
});
}
}
} }
} }
async function createCompanyGroupHandler(values: CreateCompanyGroupProps) { async function createCompanyGroupHandler(values: CreateCompanyGroupProps) {
await createCompanyGroup(values); try {
dispatch(getCompanyGroupsThunk()); await createCompanyGroup(values);
getRelatedGroupsHandler(); dispatch(getCompanyGroupsThunk());
setKindForm(null); getRelatedGroupsHandler();
setKindForm(null);
} catch (e) {
if (e instanceof ApiError) {
notification.error({
description: e.message,
});
}
}
} }
async function getRelatedGroupsHandler() { async function getRelatedGroupsHandler() {
@ -81,7 +105,9 @@ export function CatalogueGroup() {
setGszData(gszs); setGszData(gszs);
} catch (e) { } catch (e) {
if (e instanceof ApiError) { if (e instanceof ApiError) {
notification.error({ description: e.message }); notification.error({
description: e.message,
});
} }
} finally { } finally {
setRelatedGroupLoading(false); setRelatedGroupLoading(false);
@ -93,17 +119,16 @@ export function CatalogueGroup() {
getRelatedGroupsHandler(); getRelatedGroupsHandler();
}, [activeCompanyGroup]); }, [activeCompanyGroup]);
// Без выбранной ГК таблица ЮЛ не грузится — выбираем первую ГК списка
useEffect(() => {
if (!activeCompanyGroup && rawRows?.length) {
dispatch(setActiveCompanyGroup(rawRows[0]));
}
}, [rawRows, activeCompanyGroup, dispatch]);
useEffect(() => { useEffect(() => {
setActiveTab('legal_entities'); setActiveTab('legal_entities');
}, [relatedGroupFilter]); }, [relatedGroupFilter]);
useEffect(() => {
if (!activeCompanyGroup && rawRows?.length) {
dispatch(setActiveCompanyGroup(rawRows?.[0]));
}
}, [rawRows, activeCompanyGroup]);
return ( return (
<div <div
style={{ style={{
@ -178,7 +203,9 @@ export function CatalogueGroup() {
onCancel={() => { onCancel={() => {
setKindForm(null); setKindForm(null);
}} }}
onOk={createJurHandler} onOk={async (values) => {
await createJurHandler(values);
}}
/> />
)} )}
{(kindForm === 'gsk' || kindForm === 'gsz') && ( {(kindForm === 'gsk' || kindForm === 'gsz') && (
@ -188,7 +215,9 @@ export function CatalogueGroup() {
onCancel={() => { onCancel={() => {
setKindForm(null); setKindForm(null);
}} }}
onOk={createRelatedGroupHandler} onOk={async (values) => {
await createRelatedGroupHandler(values);
}}
/> />
)} )}
{kindForm === 'gk' && ( {kindForm === 'gk' && (
@ -197,7 +226,9 @@ export function CatalogueGroup() {
onCancel={() => { onCancel={() => {
setKindForm(null); setKindForm(null);
}} }}
onOk={createCompanyGroupHandler} onOk={async (values) => {
await createCompanyGroupHandler(values);
}}
/> />
)} )}
</div> </div>

View File

@ -1,30 +1,41 @@
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { Button, Space } from 'antd'; import { Button, Space } from 'antd';
import { useState } from 'react'; import { useState } from 'react';
import { ApiError } from '../api/client';
import PageTitle from '../components/PageTitle'; import PageTitle from '../components/PageTitle';
import { createCatalogueJur } from '../services/api/directories/catalogue_jur'; import { createCatalogueJur } from '../services/api/directories/catalogue_jur';
import { getCatalogueJurThunk } from '../store/slices/catalogueJurSlice/thunks'; import { getCatalogueJurThunk } from '../store/slices/catalogueJurSlice/thunks';
import { useAppDispatch, useAppSelector } from '../store/store'; import { useAppDispatch, useAppSelector } from '../store/store';
import type { CatalogueJurFormProps } from '../types/catalogueJur/catalogueJur'; import type { CatalogueJurFormProps } from '../types/catalogueJur/catalogueJur';
import { useNotificationApi } from '../utils/useNotification';
import { CatalogueJurForm } from '../widgets/CatalogueJurForm/CatalogueJurForm'; import { CatalogueJurForm } from '../widgets/CatalogueJurForm/CatalogueJurForm';
import { CatalogueJurTable } from '../widgets/CatalogueJurTable/CatalogueJurTable'; import { CatalogueJurTable } from '../widgets/CatalogueJurTable/CatalogueJurTable';
export function CatalogueJur() { export function CatalogueJur() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const notification = useNotificationApi();
const isGettingArchive = useAppSelector((state) => state.catalogue_jur.isGettingArchive); const isGettingArchive = useAppSelector((state) => state.catalogue_jur.isGettingArchive);
const [createFormOpen, setCreateFormOpen] = useState(false); const [createFormOpen, setCreateFormOpen] = useState(false);
const outside_group = true; const outside_group = true;
async function createJurHandler(values: CatalogueJurFormProps) { async function createJurHandler(values: CatalogueJurFormProps) {
await createCatalogueJur(values); try {
dispatch( await createCatalogueJur(values);
getCatalogueJurThunk({ dispatch(
closed: isGettingArchive, getCatalogueJurThunk({
outside_group: outside_group, closed: isGettingArchive,
}), outside_group: outside_group,
); }),
setCreateFormOpen(false); );
setCreateFormOpen(false);
} catch (e) {
if (e instanceof ApiError) {
notification.error({
description: e.message,
});
}
}
} }
return ( return (
@ -48,7 +59,9 @@ export function CatalogueJur() {
onCancel={() => { onCancel={() => {
setCreateFormOpen(false); setCreateFormOpen(false);
}} }}
onOk={createJurHandler} onOk={async (values) => {
await createJurHandler(values);
}}
/> />
)} )}
</div> </div>

View File

@ -1,7 +1,5 @@
import { type PayloadAction, createSlice } from '@reduxjs/toolkit'; import { type PayloadAction, createSlice } from '@reduxjs/toolkit';
import type { RelatedGroupOut } from '../../../types/api'; import type { RelatedGroupOut } from '../../../types/api';
import type { ICompanyGroup } from '../../../types/companyGroups/companyGroups';
import { getCompanyGroupsThunk } from '../companyGroupsSlice/thunks';
import { getCatalogueJurThunk } from './thunks'; import { getCatalogueJurThunk } from './thunks';
import type { CatalogueJurSliceType } from './types'; import type { CatalogueJurSliceType } from './types';
@ -12,7 +10,6 @@ const initialState: CatalogueJurSliceType = {
error: null, error: null,
isGettingArchive: false, isGettingArchive: false,
relatedGroupFilter: null, relatedGroupFilter: null,
activeCompanyGroup: null,
}; };
const catalogueJurSlice = createSlice({ const catalogueJurSlice = createSlice({
@ -25,10 +22,6 @@ const catalogueJurSlice = createSlice({
setRelatedGroupFilter(state, action: PayloadAction<RelatedGroupOut | null>) { setRelatedGroupFilter(state, action: PayloadAction<RelatedGroupOut | null>) {
state.relatedGroupFilter = action.payload; state.relatedGroupFilter = action.payload;
}, },
setActiveCompanyGroup(state, action: PayloadAction<ICompanyGroup | null>) {
state.activeCompanyGroup = action.payload;
state.relatedGroupFilter = null;
},
}, },
extraReducers: (builder) => { extraReducers: (builder) => {
builder builder
@ -44,15 +37,8 @@ const catalogueJurSlice = createSlice({
.addCase(getCatalogueJurThunk.rejected, (state, action) => { .addCase(getCatalogueJurThunk.rejected, (state, action) => {
state.error = action.payload ?? 'Неизвестная ошибка'; state.error = action.payload ?? 'Неизвестная ошибка';
state.loading = false; state.loading = false;
})
// Выбранная ГК — копия строки списка ГК: после перезагрузки списка берём свежую (счётчик ЮЛ в шапке и вкладке)
.addCase(getCompanyGroupsThunk.fulfilled, (state, action) => {
if (state.activeCompanyGroup?.id) {
state.activeCompanyGroup = action.payload.find((elem) => elem.id === state.activeCompanyGroup?.id) ?? null;
}
}); });
}, },
}); });
export const { setIsGettingArchive, setRelatedGroupFilter } = catalogueJurSlice.actions;
export const { setIsGettingArchive, setRelatedGroupFilter, setActiveCompanyGroup } = catalogueJurSlice.actions;
export default catalogueJurSlice.reducer; export default catalogueJurSlice.reducer;

View File

@ -1,6 +1,5 @@
import type { RelatedGroupOut } from '../../../types/api'; import type { RelatedGroupOut } from '../../../types/api';
import type { ICatalogueJur } from '../../../types/catalogueJur/catalogueJur'; import type { ICatalogueJur } from '../../../types/catalogueJur/catalogueJur';
import type { ICompanyGroup } from '../../../types/companyGroups/companyGroups';
export interface CatalogueJurSliceType { export interface CatalogueJurSliceType {
total_groups: number; total_groups: number;
@ -9,7 +8,6 @@ export interface CatalogueJurSliceType {
error: string | null; error: string | null;
isGettingArchive: boolean; isGettingArchive: boolean;
relatedGroupFilter?: RelatedGroupOut | null; relatedGroupFilter?: RelatedGroupOut | null;
activeCompanyGroup?: ICompanyGroup | null;
} }
export interface GetCatalogueJurPropsI { export interface GetCatalogueJurPropsI {

View File

@ -1,4 +1,5 @@
import { createSlice } from '@reduxjs/toolkit'; import { type PayloadAction, createSlice } from '@reduxjs/toolkit';
import type { ICompanyGroup } from '../../../types/companyGroups/companyGroups';
import { getCompanyGroupsThunk } from './thunks'; import { getCompanyGroupsThunk } from './thunks';
import type { CompanyGroupSliceType } from './types'; import type { CompanyGroupSliceType } from './types';
@ -7,12 +8,17 @@ const initialState: CompanyGroupSliceType = {
total_groups: 0, total_groups: 0,
loading: false, loading: false,
error: null, error: null,
activeCompanyGroup: null,
}; };
const comGroupsSlice = createSlice({ const comGroupsSlice = createSlice({
name: 'company_groups', name: 'company_groups',
initialState, initialState,
reducers: {}, reducers: {
setActiveCompanyGroup(state, action: PayloadAction<ICompanyGroup | null>) {
state.activeCompanyGroup = action.payload;
},
},
extraReducers: (builder) => { extraReducers: (builder) => {
builder builder
.addCase(getCompanyGroupsThunk.pending, (state) => { .addCase(getCompanyGroupsThunk.pending, (state) => {
@ -21,6 +27,10 @@ const comGroupsSlice = createSlice({
}) })
.addCase(getCompanyGroupsThunk.fulfilled, (state, action) => { .addCase(getCompanyGroupsThunk.fulfilled, (state, action) => {
state.raw_rows = action.payload; state.raw_rows = action.payload;
if (state.activeCompanyGroup?.id) {
const newActiveCompanyGroup = action.payload.find((elem) => elem.id === state.activeCompanyGroup?.id);
state.activeCompanyGroup = newActiveCompanyGroup;
}
state.total_groups = action.payload.length; state.total_groups = action.payload.length;
state.loading = false; state.loading = false;
}) })
@ -30,4 +40,5 @@ const comGroupsSlice = createSlice({
}); });
}, },
}); });
export const { setActiveCompanyGroup } = comGroupsSlice.actions;
export default comGroupsSlice.reducer; export default comGroupsSlice.reducer;

View File

@ -5,4 +5,5 @@ export interface CompanyGroupSliceType {
raw_rows: ICompanyGroup[]; raw_rows: ICompanyGroup[];
loading: boolean; loading: boolean;
error: string | null; error: string | null;
activeCompanyGroup?: ICompanyGroup | null;
} }

View File

@ -375,7 +375,6 @@ export interface ConsolidationCreate {
export interface RelatedGroupOut { export interface RelatedGroupOut {
id: string; id: string;
code: string; code: string;
crm_id: string | null;
company_group_id: string; company_group_id: string;
kind: 'gsk' | 'gsz'; kind: 'gsk' | 'gsz';
name: string; name: string;
@ -390,6 +389,7 @@ export interface RelatedGroupMemberOut {
} }
export interface RelatedGroupDetail extends RelatedGroupOut { export interface RelatedGroupDetail extends RelatedGroupOut {
members: RelatedGroupMemberOut[]; members: RelatedGroupMemberOut[];
crm_id: string;
} }
export interface LegalEntityBrief { export interface LegalEntityBrief {
id: string; id: string;

View File

@ -1,7 +1,7 @@
export type RelatedGroupFormType = { export type RelatedGroupFormType = {
name: string; name: string;
created_date: string | null; created_date: string | null;
crm_id: string | null; crm_id: string;
kind?: 'gsk' | 'gsz'; kind?: 'gsk' | 'gsz';
company_group_id?: string; company_group_id?: string;
}; };

View File

@ -12,7 +12,7 @@ interface CataloguGroupTabsI {
export type CatalogueGroupTabKey = 'legal_entities' | 'gsk' | 'gsz'; export type CatalogueGroupTabKey = 'legal_entities' | 'gsk' | 'gsz';
export const CataloguGroupTabs = ({ activeTab, setActiveTab, gskData, gszData }: CataloguGroupTabsI) => { export const CataloguGroupTabs = ({ activeTab, setActiveTab, gskData, gszData }: CataloguGroupTabsI) => {
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup); const activeCompanyGroup = useAppSelector((state) => state.company_groups.activeCompanyGroup);
return ( return (
<Tabs <Tabs

View File

@ -5,7 +5,7 @@ import { useAppSelector } from '../../store/store';
import { pluralRu } from '../../utils/labels'; import { pluralRu } from '../../utils/labels';
export const CompanyGroupHeader = () => { export const CompanyGroupHeader = () => {
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup); const activeCompanyGroup = useAppSelector((state) => state.company_groups.activeCompanyGroup);
const count = activeCompanyGroup?.legal_entities_count ?? 0; const count = activeCompanyGroup?.legal_entities_count ?? 0;
const organizationWord = pluralRu(count, ['организация', 'организации', 'организаций']); const organizationWord = pluralRu(count, ['организация', 'организации', 'организаций']);

View File

@ -17,14 +17,6 @@ type Props = {
jurId?: string; 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 }) => { export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId }) => {
const [form] = Form.useForm<CatalogueJurFormProps>(); const [form] = Form.useForm<CatalogueJurFormProps>();
const notification = useNotificationApi(); const notification = useNotificationApi();
@ -35,11 +27,12 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
const [loadingOfficesOptions, setLoadingOfficesOptions] = useState(false); const [loadingOfficesOptions, setLoadingOfficesOptions] = useState(false);
const [confirmLoading, setConfirmLoading] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false);
const [officesOptions, setOfficesOptions] = useState<{ value: string; label: string }[]>([]); const [officesOptions, setOfficesOptions] = useState<{ value: string; label: string }[]>([]);
const companyGrops = useAppSelector((state) => state.company_groups.raw_rows); const companyGrops = useAppSelector((state) => state.company_groups.raw_rows);
const companyGropsLoading = useAppSelector((state) => state.company_groups.loading); const companyGropsLoading = useAppSelector((state) => state.company_groups.loading);
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup);
const type = Form.useWatch('entity_type', form); const type = Form.useWatch('entity_type', form);
const companyGroupId = Form.useWatch('company_group_id', form);
async function getInitialData() { async function getInitialData() {
if (jurId) { if (jurId) {
@ -58,7 +51,6 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
gsk_id: data.memberships?.find((elem) => elem.kind === 'gsk')?.related_group_id, gsk_id: data.memberships?.find((elem) => elem.kind === 'gsk')?.related_group_id,
gsz_id: data.memberships?.find((elem) => elem.kind === 'gsz')?.related_group_id, gsz_id: data.memberships?.find((elem) => elem.kind === 'gsz')?.related_group_id,
}); });
getRelatedGroupsHandler(data.company_group_id as string);
} catch (e) { } catch (e) {
if (e instanceof ApiError) { if (e instanceof ApiError) {
notification.error({ description: e.message }); notification.error({ description: e.message });
@ -66,11 +58,6 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
} finally { } finally {
setInitialDataLoading(false); setInitialDataLoading(false);
} }
} else {
form.setFieldsValue({
company_group_id: activeCompanyGroup?.id ?? undefined,
});
getRelatedGroupsHandler(activeCompanyGroup?.id ?? '');
} }
} }
@ -89,12 +76,12 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
} }
} }
async function getRelatedGroupsHandler(companyId: string) { async function getRelatedGroupsHandler() {
if (!companyId) return; if (!companyGroupId) return;
setLoadingRelatedGroupsOptions(true); setLoadingRelatedGroupsOptions(true);
try { try {
const res = await fetchRelatedGroups(companyId); const res = await fetchRelatedGroups(companyGroupId);
const gsks = res.filter((elem) => elem.kind === 'gsk'); const gsks = res.filter((elem) => elem.kind === 'gsk');
const gszs = res.filter((elem) => elem.kind === 'gsz'); const gszs = res.filter((elem) => elem.kind === 'gsz');
setGskData(gsks); setGskData(gsks);
@ -114,21 +101,18 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
const values = await form.validateFields(); const values = await form.validateFields();
await onOk({ await onOk({
...values, ...values,
kpp: type === 'ИП' ? null : (values.kpp?.toString() ?? null), kpp: type === 'ИП' ? null : values.kpp?.toString(),
inn: jurId ? undefined : values?.inn?.toString(), inn: jurId ? undefined : values?.inn?.toString(),
company_group_id: jurId ? undefined : values.company_group_id, company_group_id: jurId ? undefined : values.company_group_id,
gsz_id: values.gsz_id ? values?.gsz_id : null, gsz_id: values.gsz_id ? values?.gsz_id : null,
gsk_id: values.gsk_id ? values?.gsk_id : null, gsk_id: values.gsk_id ? values?.gsk_id : null,
regional_office_id: values.regional_office_id ? values?.regional_office_id : null, regional_office_id: values.regional_office_id ? values?.regional_office_id : null,
crm_id: values.crm_id ?? null,
}); });
} catch (e) { } catch (e) {
if (e instanceof ApiError) { if (e instanceof ApiError) {
const field = e.code ? FIELD_BY_ERROR_CODE[e.code] : undefined; notification.error({
if (field) { description: e.message,
form.setFields([{ name: field, errors: [e.message] }]); });
}
notification.error({ description: e.message });
} }
} finally { } finally {
setConfirmLoading(false); setConfirmLoading(false);
@ -144,13 +128,21 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
return companyGrops?.map((company) => company && { value: company.id, label: company.name }); return companyGrops?.map((company) => company && { value: company.id, label: company.name });
}, [companyGrops]); }, [companyGrops]);
useEffect(() => {
getInitialData();
}, [jurId, open]);
useEffect(() => { useEffect(() => {
getOfficesOptionsHandler(); getOfficesOptionsHandler();
}, []); }, []);
useEffect(() => { useEffect(() => {
getInitialData(); form.setFieldsValue({
}, [jurId, open]); gsk_id: undefined,
gsz_id: undefined,
});
getRelatedGroupsHandler();
}, [companyGroupId]);
return ( return (
<Modal <Modal
@ -190,11 +182,7 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={6}> <Col span={6}>
<Form.Item <Form.Item name='kpp' label='КПП' rules={[{ pattern: /^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$/, message: 'Некорректный формат' }]}>
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%' }} /> <Input placeholder='000000000' maxLength={9} disabled={type === 'ИП'} style={{ width: '100%' }} />
</Form.Item> </Form.Item>
</Col> </Col>
@ -225,7 +213,6 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
label='Альтернативные наименования' label='Альтернативные наименования'
extra='Введите синоним и нажмите Enter. Можно добавить несколько значений.'> extra='Введите синоним и нажмите Enter. Можно добавить несколько значений.'>
<Select <Select
className='custom-select-selection-item'
mode='tags' mode='tags'
style={{ width: '100%' }} style={{ width: '100%' }}
placeholder='Добавить синоним' placeholder='Добавить синоним'
@ -259,13 +246,6 @@ export const CatalogueJurForm: React.FC<Props> = ({ open, onCancel, onOk, jurId
showSearch showSearch
optionFilterProp='label' optionFilterProp='label'
notFoundContent={companyGropsLoading ? 'Загрузка' : 'Нет вариантов'} notFoundContent={companyGropsLoading ? 'Загрузка' : 'Нет вариантов'}
onChange={(value) => {
form.setFieldsValue({
gsk_id: undefined,
gsz_id: undefined,
});
getRelatedGroupsHandler(value);
}}
/> />
</Form.Item> </Form.Item>
</Col> </Col>

View File

@ -45,7 +45,7 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
const rawRows = useAppSelector((state) => state.catalogue_jur.raw_rows); const rawRows = useAppSelector((state) => state.catalogue_jur.raw_rows);
const loading = useAppSelector((state) => state.catalogue_jur.loading); const loading = useAppSelector((state) => state.catalogue_jur.loading);
const isGettingArchive = useAppSelector((state) => state.catalogue_jur.isGettingArchive); const isGettingArchive = useAppSelector((state) => state.catalogue_jur.isGettingArchive);
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup); const activeCompanyGroup = useAppSelector((state) => state.company_groups.activeCompanyGroup);
const relatedGroupFilter = useAppSelector((state) => state.catalogue_jur.relatedGroupFilter); const relatedGroupFilter = useAppSelector((state) => state.catalogue_jur.relatedGroupFilter);
const [moveCatalogueFormOpen, setMoveCatalogueFormOpen] = useState(false); const [moveCatalogueFormOpen, setMoveCatalogueFormOpen] = useState(false);
@ -122,17 +122,24 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
} }
async function editJurHandler(values: CatalogueJurFormProps) { async function editJurHandler(values: CatalogueJurFormProps) {
await editCatalogueJur(jurId, values); try {
getCatalogueJur(); await editCatalogueJur(jurId, values);
closeEditCatalogueJurHandler(); getCatalogueJur();
closeEditCatalogueJurHandler();
} catch (e) {
if (e instanceof ApiError) {
notification.error({
description: e.message,
});
}
}
} }
useEffect(() => { useEffect(() => {
// «ЮЛ вне ГК» грузится без выбранной ГК; список ГК — только по выбранной (без неё запрос ушёл бы по всем ГК) if (activeCompanyGroup?.id) {
if (outside_group || activeCompanyGroup?.id) {
getCatalogueJur(); getCatalogueJur();
} }
}, [isGettingArchive, activeCompanyGroup, outside_group]); }, [isGettingArchive, activeCompanyGroup]);
const extraColumns: ColumnsType<ICatalogueJur> = useMemo( const extraColumns: ColumnsType<ICatalogueJur> = useMemo(
() => [ () => [
@ -322,7 +329,9 @@ export function CatalogueJurTable({ outside_group, tabsComponent }: CatalogueJur
onCancel={() => { onCancel={() => {
closeEditCatalogueJurHandler(); closeEditCatalogueJurHandler();
}} }}
onOk={editJurHandler} onOk={async (values) => {
await editJurHandler(values);
}}
/> />
)} )}
</div> </div>

View File

@ -19,7 +19,7 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
const { me } = useMe(); const { me } = useMe();
const notification = useNotificationApi(); const notification = useNotificationApi();
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup); const activeCompanyGroup = useAppSelector((state) => state.company_groups.activeCompanyGroup);
const [officesOptions, setOfficesOptions] = useState<{ value: string; label: string }[]>([]); const [officesOptions, setOfficesOptions] = useState<{ value: string; label: string }[]>([]);
const [loadingOptions, setLoadingOptions] = useState(false); const [loadingOptions, setLoadingOptions] = useState(false);
const [confirmLoading, setConfirmLoading] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false);
@ -49,14 +49,6 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
}); });
} catch (e) { } catch (e) {
if (e instanceof ApiError) { if (e instanceof ApiError) {
if (e.code === 'CRM_ID_EXISTS') {
form.setFields([
{
name: 'crm_id',
errors: [e.message],
},
]);
}
notification.error({ notification.error({
description: e.message, description: e.message,
}); });
@ -94,7 +86,7 @@ export const CompanyGroupForm: React.FC<Props> = ({ open, onCancel, onOk }) => {
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item name='crm_id' label='ID ГК в CRM'> <Form.Item name='crm_id' label='ID ГК в CRM'>
<Input /> <Input placeholder='Генерируется автоматически' />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>

View File

@ -2,7 +2,7 @@ import { FolderOpenOutlined, SearchOutlined } from '@ant-design/icons';
import { Empty, Input, List, Spin, Tag, Typography } from 'antd'; import { Empty, Input, List, Spin, Tag, Typography } from 'antd';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { useVirtualList } from '../../hooks/useVirtualList'; import { useVirtualList } from '../../hooks/useVirtualList';
import { setActiveCompanyGroup } from '../../store/slices/catalogueJurSlice/catalogueJurSlice'; import { setActiveCompanyGroup } from '../../store/slices/companyGroupsSlice/companyGroupsSlice';
import { getCompanyGroupsThunk } from '../../store/slices/companyGroupsSlice/thunks'; import { getCompanyGroupsThunk } from '../../store/slices/companyGroupsSlice/thunks';
import { useAppDispatch, useAppSelector } from '../../store/store'; import { useAppDispatch, useAppSelector } from '../../store/store';
@ -13,7 +13,7 @@ export const CompanyGroupsList = () => {
const rawRows = useAppSelector((state) => state.company_groups.raw_rows); const rawRows = useAppSelector((state) => state.company_groups.raw_rows);
const loading = useAppSelector((state) => state.company_groups.loading); const loading = useAppSelector((state) => state.company_groups.loading);
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup); const activeCompanyGroup = useAppSelector((state) => state.company_groups.activeCompanyGroup);
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');

View File

@ -0,0 +1,7 @@
/* Теги выбранных значений в селектах фильтра шахматки */
.custom-select-selection-item .ant-select-selection-item {
background-color: #ecfdf3 !important;
color: #1f5f35 !important;
box-shadow: inset 0 0 0 1px #2ea64b38 !important;
border-radius: 999px !important;
}

View File

@ -6,6 +6,7 @@ import { type OptionType, TreeFilterDropdown } from '../../components/TreeFilter
import type { Account, MatrixCompany, Quarter } from '../../types/api'; import type { Account, MatrixCompany, Quarter } from '../../types/api';
import { quarterShortLabel } from '../../utils/labels'; import { quarterShortLabel } from '../../utils/labels';
import { useNotificationApi } from '../../utils/useNotification'; import { useNotificationApi } from '../../utils/useNotification';
import './ConsolidationChessTableFilter.css';
interface ConsolidationChessTableFilterI { interface ConsolidationChessTableFilterI {
companies: string[]; companies: string[];

View File

@ -26,15 +26,9 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
const [confirmLoading, setConfirmLoading] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false);
const companyGrops = useAppSelector((state) => state.company_groups.raw_rows); const companyGrops = useAppSelector((state) => state.company_groups.raw_rows);
const companyGropsLoading = useAppSelector((state) => state.company_groups.loading); const companyGropsLoading = useAppSelector((state) => state.company_groups.loading);
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup);
async function getInitialData() { async function getInitialData() {
if (!relatedGroupId) { if (!relatedGroupId) return;
form.setFieldsValue({
company_group_id: activeCompanyGroup?.id ?? undefined,
});
return;
}
setInitialDataLoading(true); setInitialDataLoading(true);
try { try {
const data = await fetchRelatedGroup(relatedGroupId); const data = await fetchRelatedGroup(relatedGroupId);
@ -42,7 +36,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
form.setFieldsValue({ form.setFieldsValue({
crm_id: data?.crm_id, crm_id: data?.crm_id,
name: data?.name, name: data?.name,
created_date: data?.created_date ?? null, created_date: data?.created_date ?? dayjs().format('YYYY-MM-DD'),
company_group_id: data?.company_group_id, company_group_id: data?.company_group_id,
}); });
} catch (e) { } catch (e) {
@ -59,7 +53,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
try { try {
const { created_date, name, crm_id, company_group_id } = await form.validateFields(); const { created_date, name, crm_id, company_group_id } = await form.validateFields();
await onOk({ await onOk({
created_date: created_date ?? (relatedGroupId ? null : dayjs().format('YYYY-MM-DD')), created_date: created_date ?? dayjs().format('YYYY-MM-DD'),
name, name,
crm_id, crm_id,
company_group_id: relatedGroupId ? undefined : company_group_id, company_group_id: relatedGroupId ? undefined : company_group_id,
@ -67,14 +61,6 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
}); });
} catch (e) { } catch (e) {
if (e instanceof ApiError) { if (e instanceof ApiError) {
if (e.code === 'CRM_ID_EXISTS') {
form.setFields([
{
name: 'crm_id',
errors: [e.message],
},
]);
}
notification.error({ notification.error({
description: e.message, description: e.message,
}); });
@ -110,7 +96,7 @@ export const RelatedGroupForm: React.FC<Props> = ({ open, onCancel, onOk, relate
confirmLoading={confirmLoading}> confirmLoading={confirmLoading}>
<Form form={form} layout='vertical'> <Form form={form} layout='vertical'>
<FormItem name='crm_id' label='ID в CRM'> <FormItem name='crm_id' label='ID в CRM'>
<Input /> <Input placeholder='Генерируется автоматически' />
</FormItem> </FormItem>
<FormItem name='name' label='Наименование' rules={[{ required: true, message: 'Укажите наименование' }]}> <FormItem name='name' label='Наименование' rules={[{ required: true, message: 'Укажите наименование' }]}>
<Input /> <Input />

View File

@ -62,9 +62,17 @@ export function RelatedGroupTable({ tabsComponent, data, loading, getData, activ
} }
async function editRelatedGroupHandler(values: RelatedGroupFormType) { async function editRelatedGroupHandler(values: RelatedGroupFormType) {
await editRelatedGroup(groupId, values); try {
getData(); await editRelatedGroup(groupId, values);
closeEditRelatedGroupHandler(); getData();
closeEditRelatedGroupHandler();
} catch (e) {
if (e instanceof ApiError) {
notification.error({
description: e.message,
});
}
}
} }
const extraColumns: ColumnsType<RelatedGroupOut> = useMemo( const extraColumns: ColumnsType<RelatedGroupOut> = useMemo(
@ -159,7 +167,9 @@ export function RelatedGroupTable({ tabsComponent, data, loading, getData, activ
onCancel={() => { onCancel={() => {
closeEditRelatedGroupHandler(); closeEditRelatedGroupHandler();
}} }}
onOk={editRelatedGroupHandler} onOk={async (values) => {
await editRelatedGroupHandler(values);
}}
/> />
)} )}
{deleteModalOpen && ( {deleteModalOpen && (

View File

@ -6,17 +6,13 @@ import { store } from '../../store/store';
import type { RelatedGroupOut } from '../../types/api'; import type { RelatedGroupOut } from '../../types/api';
export const relateGroupConfig: TableConfig<RelatedGroupOut> = { export const relateGroupConfig: TableConfig<RelatedGroupOut> = {
rowKey: 'id', rowKey: 'id',
searchableKeys: ['code', 'crm_id', 'name', 'members_count', 'created_date'], searchableKeys: ['code', 'name', 'members_count', 'created_date'],
columns: [ columns: [
{ {
key: 'code', key: 'code',
title: 'ID', title: 'ID',
features: { search: true, multiselect: true }, features: { search: true, multiselect: true },
}, },
{
key: 'crm_id',
title: 'ID в CRM',
},
{ {
key: 'name', key: 'name',
title: 'Наименование', title: 'Наименование',
@ -39,7 +35,7 @@ export const relateGroupConfig: TableConfig<RelatedGroupOut> = {
key: 'created_date', key: 'created_date',
title: 'Дата создания', title: 'Дата создания',
render: (val) => { render: (val) => {
return val ? new Date(val as string).toLocaleDateString('ru-RU') : '—'; return new Date(val as string).toLocaleDateString('ru-RU');
}, },
}, },
], ],