fix: версия в URL, ошибки списка, подпись квартала, Назад по прямой ссылке, код ошибки не в заголовке (I-1, M-3, M-4, M-6, T7.M7, T7.M4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
This commit is contained in:
parent
384301ecbd
commit
0f734e3bf8
@ -16,7 +16,10 @@ export default function PageTitle({ location, header, description, backable = fa
|
||||
<div>
|
||||
<div style={{ fontSize: 12, color: '#667085' }}>
|
||||
{backable && (
|
||||
<Button type='default' onClick={() => navigate(-1)} style={{ marginRight: '5px' }}>
|
||||
<Button
|
||||
type='default'
|
||||
onClick={() => (window.history.state?.idx === 0 ? navigate('/groups') : navigate(-1))}
|
||||
style={{ marginRight: '5px' }}>
|
||||
<ArrowLeftOutlined />
|
||||
Назад
|
||||
</Button>
|
||||
|
||||
@ -8,6 +8,7 @@ import { getVersionCard } from '../services/api/consolidations';
|
||||
import { exportOsv } from '../services/api/osv';
|
||||
import type { VersionCard } from '../types/consolidations/version_members';
|
||||
import { exportFilename, saveBlob } from '../utils/download';
|
||||
import { quarterLabel } from '../utils/labels';
|
||||
import { statusMapping } from '../widgets/ConsoleTable/consts';
|
||||
import { OsvFilters } from '../widgets/ConsolidatedOsv/OsvFilters';
|
||||
import { OsvGaps } from '../widgets/ConsolidatedOsv/OsvGaps';
|
||||
@ -52,7 +53,7 @@ function OsvContent({ versionId }: { versionId: string }) {
|
||||
<PageTitle
|
||||
backable
|
||||
location={`Отчётность › ${card?.company_group.name ?? ''} › Сводная ОСВ`}
|
||||
header={card ? `Сводная ОСВ ${card.year} ${card.quarter} · версия ${card.version_no}` : 'Сводная ОСВ'}
|
||||
header={card ? `Сводная ОСВ ${card.year}, ${quarterLabel(card.quarter)} · версия ${card.version_no}` : 'Сводная ОСВ'}
|
||||
description={card ? (statusMapping[card.status] ?? card.status) : ''}
|
||||
/>
|
||||
<main
|
||||
|
||||
@ -11,6 +11,13 @@ import { ConsoleLegalEntitiesTable } from '../widgets/ConsoleLegalEntities/Conso
|
||||
import { ConsoleTableWidget } from '../widgets/ConsoleTable/ConsoleTable';
|
||||
import { DocumentsDrawer } from '../widgets/FilesDrawer/FilesDrawer';
|
||||
|
||||
// unwrap() бросает rejectValue (строку), fetch группы — ApiError
|
||||
function errorText(e: unknown): string {
|
||||
if (e instanceof ApiError) return e.message;
|
||||
if (typeof e === 'string') return e;
|
||||
return 'Не удалось загрузить группу компаний';
|
||||
}
|
||||
|
||||
export function ConsolidationPage() {
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [reloadKey, setReloadKey] = useState(0);
|
||||
@ -31,17 +38,25 @@ export function ConsolidationPage() {
|
||||
setComGroupName(data.name);
|
||||
setLocationPath(`Отчетность>${data.name}>Консолидации`);
|
||||
setDesc(`${data.legal_entities_count} организаций`);
|
||||
await dispatch(getConsolidationsListThunk(id));
|
||||
if (versionParam) dispatch(setSelectedVersion(versionParam));
|
||||
await dispatch(getConsolidationsListThunk(id)).unwrap();
|
||||
} catch (e: unknown) {
|
||||
message.error(e instanceof ApiError ? e.message : 'Не удалось загрузить группу компаний');
|
||||
message.error(errorText(e));
|
||||
}
|
||||
}
|
||||
getAllInfo(group_id);
|
||||
}, [dispatch, group_id]);
|
||||
|
||||
// выбор версии следует за ?version= и не перезагружает список
|
||||
useEffect(() => {
|
||||
dispatch(setSelectedVersion(versionParam));
|
||||
}, [dispatch, versionParam]);
|
||||
|
||||
// уход со страницы сбрасывает выбор (при смене группы ?version= пропадает из URL — выбор сбросит эффект выше)
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
dispatch(setSelectedVersion(null));
|
||||
};
|
||||
}, [dispatch, group_id, versionParam]);
|
||||
}, [dispatch]);
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { counterpartyTypeLabel, documentStatusLabel, documentTypeLabel, fileStatusLabel, matchKindLabel, periodLabel } from './labels';
|
||||
import {
|
||||
counterpartyTypeLabel,
|
||||
documentStatusLabel,
|
||||
documentTypeLabel,
|
||||
fileStatusLabel,
|
||||
matchKindLabel,
|
||||
periodLabel,
|
||||
quarterLabel,
|
||||
} from './labels';
|
||||
|
||||
describe('labels', () => {
|
||||
it('тип документа', () => {
|
||||
@ -10,6 +18,10 @@ describe('labels', () => {
|
||||
expect(documentTypeLabel('DISCLOSURE', null)).toBe('Расшифровка');
|
||||
expect(documentTypeLabel('OTHER', null)).toBe('Прочее');
|
||||
});
|
||||
it('квартал', () => {
|
||||
expect(quarterLabel('Q1')).toBe('1 квартал');
|
||||
expect(quarterLabel('Q3')).toBe('3 квартал');
|
||||
});
|
||||
it('период', () => {
|
||||
expect(periodLabel(2024, 'Q1', false)).toBe('2024 К1');
|
||||
expect(periodLabel(2024, 'Q3', true)).toBe('2024 · 9 мес.');
|
||||
|
||||
@ -14,6 +14,8 @@ export function documentTypeLabel(type: DocumentType, account: string | null): s
|
||||
return type === 'OSV' && account ? `ОСВ ${account}` : TYPE_LABELS[type];
|
||||
}
|
||||
|
||||
export const quarterLabel = (q: Quarter): string => `${q.slice(1)} квартал`;
|
||||
|
||||
export function periodLabel(year: number | null, quarter: Quarter | null, isCumulative: boolean | null): string {
|
||||
if (year === null || quarter === null) {
|
||||
return 'период не определён';
|
||||
|
||||
@ -2,11 +2,10 @@ import { ArrowRightOutlined, DownOutlined, MoreOutlined, RightOutlined } from '@
|
||||
import { Button, Dropdown, Space, message } from 'antd';
|
||||
import type { ColumnsType, TableProps } from 'antd/es/table';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { ColumnSettings, DataTable, type TableConfig, isGroup, useFilteredRows, useVisibleColumns } from '../../components/GenericTable';
|
||||
import { useTableFilters } from '../../components/GenericTable/useTableFilters';
|
||||
import { setSelectedVersion } from '../../store/slices/consolidations/consolidationsSlice';
|
||||
import { useAppDispatch, useAppSelector } from '../../store/store';
|
||||
import { useAppSelector } from '../../store/store';
|
||||
import type { ConsRow } from '../../types/consolidations/consolidations';
|
||||
import { consConfigBase } from './config';
|
||||
import { flattenCons } from './flatten';
|
||||
@ -45,7 +44,7 @@ const RowActions = memo(function RowActions({
|
||||
export function ConsoleTableWidget() {
|
||||
const navigate = useNavigate();
|
||||
const { group_id } = useParams();
|
||||
const dispatch = useAppDispatch();
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
const consolidations = useAppSelector((s) => s.consolidations.consolidations);
|
||||
const loading = useAppSelector((s) => s.consolidations.loading);
|
||||
|
||||
@ -124,7 +123,7 @@ export function ConsoleTableWidget() {
|
||||
render: (_: unknown, row: ConsRow) => (
|
||||
<div style={{ display: 'flex' }}>
|
||||
<Button type='default' onClick={() => navigate(`/groups/${group_id}/versions/${row.id}/osv`)}>
|
||||
К отчетности <ArrowRightOutlined />
|
||||
К отчётности <ArrowRightOutlined />
|
||||
</Button>
|
||||
<RowActions row={row} onAction={handleDropdownAction} />
|
||||
</div>
|
||||
@ -135,7 +134,8 @@ export function ConsoleTableWidget() {
|
||||
);
|
||||
|
||||
const onRow: TableProps<ConsRow>['onRow'] = (row) => ({
|
||||
onClick: () => dispatch(setSelectedVersion(row.id)),
|
||||
// выбранная версия живёт в URL (?version=): переживает уход на «К отчётности» и «Назад»
|
||||
onClick: () => setSearchParams({ version: row.id }, { replace: true }),
|
||||
});
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
|
||||
@ -88,7 +88,7 @@ export function OsvDocument({ document, onChanged }: { document: DocumentOut; on
|
||||
showIcon
|
||||
closable
|
||||
onClose={() => setProblem(null)}
|
||||
message={problem.code ? `${problem.message} (${problem.code})` : problem.message}
|
||||
message={<span title={problem.code}>{problem.message}</span>}
|
||||
description={
|
||||
problem.failures.length > 0 ? (
|
||||
<ul style={{ margin: 0, paddingLeft: 18 }}>
|
||||
|
||||
@ -14,6 +14,7 @@ import { type ReactNode, memo, useState } from 'react';
|
||||
import { NavLink, useNavigate } from 'react-router-dom';
|
||||
import { USERS_ADMIN } from '../../auth/permissions';
|
||||
import { createConsolidation } from '../../services/api/consolidations';
|
||||
import { quarterLabel } from '../../utils/labels';
|
||||
import { NewConsolidationModal } from '../NewConsoleModal/NewConsoleModal';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
@ -216,7 +217,7 @@ export function Sidebar() {
|
||||
onOk={async (values) => {
|
||||
const card = await createConsolidation(values);
|
||||
setModalOpen(false);
|
||||
message.success(`Создана консолидация ${values.year} ${values.quarter}`);
|
||||
message.success(`Создана консолидация ${values.year}, ${quarterLabel(values.quarter)}`);
|
||||
navigate(`/groups/${values.company_group_id}?version=${card.id}`);
|
||||
}}
|
||||
/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user