Шахматка: при выборе одной стороны Дт/Кт не шлём запрос (ревью #207)
- режим запроса вынесен в utils/chessMatrixMode (all / manual / half) с тестами; при «одна сторона» запрос матрицы не уходит, индикатор загрузки снимается; - подсказка по спеке intercompany-matrix-ui: один текст в таблице и у фильтров, у фильтров — только в состоянии «одна сторона»; - высота шахматки: min-height только для состояния подсказки, обёртка снова auto (в обычном режиме «Итого» сразу под строками); - откат pairs?.length в shouldWarnCounterPairs (маскировал старый API); - вернули комментарий про сумму, лишний columns || [] и дубль CSS убраны; опечатка CataloguGroupTabsI. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
65c44978bd
commit
d3c4d35dca
17
web/src/utils/chessMatrixMode.test.ts
Normal file
17
web/src/utils/chessMatrixMode.test.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { chessMatrixMode } from './chessMatrixMode';
|
||||
|
||||
describe('chessMatrixMode', () => {
|
||||
it('фильтры пусты — полная матрица', () => {
|
||||
expect(chessMatrixMode([], [])).toBe('all');
|
||||
});
|
||||
it('выбраны Дт и Кт — ручной расчёт', () => {
|
||||
expect(chessMatrixMode(['62'], ['60'])).toBe('manual');
|
||||
});
|
||||
it('только Дт — одна сторона', () => {
|
||||
expect(chessMatrixMode(['62'], [])).toBe('half');
|
||||
});
|
||||
it('только Кт — одна сторона', () => {
|
||||
expect(chessMatrixMode([], ['60'])).toBe('half');
|
||||
});
|
||||
});
|
||||
16
web/src/utils/chessMatrixMode.ts
Normal file
16
web/src/utils/chessMatrixMode.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Режим запроса шахматки по фильтрам счетов:
|
||||
* - `all` — фильтры пусты, берём полную матрицу;
|
||||
* - `manual` — выбраны и Дт, и Кт, считаем по выбранным счетам;
|
||||
* - `half` — выбрана только одна сторона: запрос не шлём, показываем подсказку.
|
||||
*/
|
||||
export type ChessMatrixMode = 'all' | 'manual' | 'half';
|
||||
|
||||
/** Текст для состояния «выбрана одна сторона» — в таблице и у фильтров один и тот же (openspec intercompany-matrix-ui). */
|
||||
export const CHESS_HALF_FILTER_HINT = 'Для расчёта по счетам выберите счета и в Дт, и в Кт';
|
||||
|
||||
export const chessMatrixMode = (debit: readonly string[], credit: readonly string[]): ChessMatrixMode => {
|
||||
if (debit.length > 0 && credit.length > 0) return 'manual';
|
||||
if (debit.length === 0 && credit.length === 0) return 'all';
|
||||
return 'half';
|
||||
};
|
||||
@ -140,7 +140,7 @@ export const shouldWarnCounterPairs = (
|
||||
pairs: readonly CounterPairOut[],
|
||||
storage: Pick<Storage, 'getItem'> | null,
|
||||
): boolean => {
|
||||
if (pairs?.length === 0) return false;
|
||||
if (pairs.length === 0) return false;
|
||||
if (status && FINISHED_STATUSES.includes(status)) return false;
|
||||
try {
|
||||
return storage?.getItem(`${COUNTER_PAIRS_STORAGE_PREFIX}${versionId}`) !== counterPairsKey(pairs);
|
||||
|
||||
@ -2,7 +2,7 @@ import { Tabs } from 'antd';
|
||||
import { useAppSelector } from '../../../store/store';
|
||||
import type { RelatedGroupOut } from '../../../types/api';
|
||||
import './CatalogueCompanyGroupTabs.css';
|
||||
interface CataloguGroupTabsI {
|
||||
interface CatalogueCompanyGroupTabsI {
|
||||
activeTab: CatalogueCompanyGroupTabKey;
|
||||
setActiveTab: (val: CatalogueCompanyGroupTabKey) => void;
|
||||
gskData: RelatedGroupOut[];
|
||||
@ -11,7 +11,7 @@ interface CataloguGroupTabsI {
|
||||
|
||||
export type CatalogueCompanyGroupTabKey = 'legal_entities' | 'gsk' | 'gsz';
|
||||
|
||||
export const CatalogueCompanyGroupTabs = ({ activeTab, setActiveTab, gskData, gszData }: CataloguGroupTabsI) => {
|
||||
export const CatalogueCompanyGroupTabs = ({ activeTab, setActiveTab, gskData, gszData }: CatalogueCompanyGroupTabsI) => {
|
||||
const activeCompanyGroup = useAppSelector((state) => state.catalogue_jur.activeCompanyGroup);
|
||||
|
||||
return (
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
} from '../../../services/api/intercompanyMatrix';
|
||||
import type { IntercompanyMatrixResponse, IntercompanyMatrixResultResponse } from '../../../types/api';
|
||||
import type { VersionCard } from '../../../types/consolidations/version_members';
|
||||
import { chessMatrixMode } from '../../../utils/chessMatrixMode';
|
||||
import { saveBlob, xlsxFilename } from '../../../utils/download';
|
||||
import { useNotificationApi } from '../../../utils/useNotification';
|
||||
import { ConsolidationChessFormatTableFilter } from '../ConsolidationChessFormatTableFilter/ConsolidationChessFormatTableFilter';
|
||||
@ -72,14 +73,15 @@ export const ChessContent = ({ card }: { card: VersionCard | null }) => {
|
||||
return () => document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
||||
}, []);
|
||||
|
||||
const isHalfFiltered = (debit.length > 0 && credit.length === 0) || (debit.length === 0 && credit.length > 0);
|
||||
const matrixMode = chessMatrixMode(debit, credit);
|
||||
const isHalfFiltered = matrixMode === 'half';
|
||||
|
||||
const loadData = useCallback(
|
||||
async (vId: string, dIds: string[], cIds: string[], isActual: () => boolean) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
let res: IntercompanyMatrixResponse;
|
||||
if (dIds?.length && cIds?.length) {
|
||||
if (chessMatrixMode(dIds, cIds) === 'manual') {
|
||||
res = await fetchChessConsolidationsManual(vId, { debit_account_ids: dIds, credit_account_ids: cIds });
|
||||
} else {
|
||||
res = await fetchChessConsolidations(vId);
|
||||
@ -97,7 +99,7 @@ export const ChessContent = ({ card }: { card: VersionCard | null }) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
[notification, isHalfFiltered],
|
||||
[notification],
|
||||
);
|
||||
const loadResData = useCallback(
|
||||
async (vid: string) => {
|
||||
@ -124,7 +126,7 @@ export const ChessContent = ({ card }: { card: VersionCard | null }) => {
|
||||
}
|
||||
setMatrixExportLoading(true);
|
||||
try {
|
||||
if (debit?.length && credit?.length) {
|
||||
if (matrixMode === 'manual') {
|
||||
const { blob, filename } = await exportChessTableManual(version_id, { debit_account_ids: debit, credit_account_ids: credit });
|
||||
saveBlob(blob, filename ?? xlsxFilename('Шахматка', versionInfo));
|
||||
} else {
|
||||
@ -167,7 +169,8 @@ export const ChessContent = ({ card }: { card: VersionCard | null }) => {
|
||||
}, [data, companies]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!version_id) return;
|
||||
// выбрана только одна сторона Дт/Кт — запрос не шлём, таблица показывает подсказку
|
||||
if (!version_id || isHalfFiltered) return;
|
||||
|
||||
let isMounted = true;
|
||||
const isActual = () => isMounted;
|
||||
@ -178,7 +181,7 @@ export const ChessContent = ({ card }: { card: VersionCard | null }) => {
|
||||
isMounted = false;
|
||||
setLoading(false);
|
||||
};
|
||||
}, [version_id, debit, credit, loadData]);
|
||||
}, [version_id, debit, credit, isHalfFiltered, loadData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!version_id) return;
|
||||
@ -202,7 +205,7 @@ export const ChessContent = ({ card }: { card: VersionCard | null }) => {
|
||||
style={{
|
||||
backgroundColor: isChessFullscreen ? '#fff' : 'transparent',
|
||||
padding: isChessFullscreen ? 6 : 0,
|
||||
height: isChessFullscreen ? '100vh' : '100%',
|
||||
height: isChessFullscreen ? '100vh' : 'auto',
|
||||
overflow: isChessFullscreen ? 'hidden' : 'visible',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
|
||||
@ -17,15 +17,6 @@
|
||||
background-color: #e6f4ea !important ;
|
||||
}
|
||||
|
||||
.chessTable .ant-table-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chessTable.customHeight .ant-table-container {
|
||||
height: 550px !important;
|
||||
}
|
||||
|
||||
.chessTable .ant-table-summary {
|
||||
margin-top: auto;
|
||||
min-height: 550px;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import type React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import type { IntercompanyMatrixResponse, MatrixCell, MatrixCompany } from '../../../types/api';
|
||||
import './ConsolidatedChessTable.css';
|
||||
import { CHESS_HALF_FILTER_HINT } from '../../../utils/chessMatrixMode';
|
||||
import { chessTableCellFormatValue } from '../../../utils/chessTableCellFormatValue';
|
||||
import { sumAmounts } from '../../../utils/money';
|
||||
|
||||
@ -111,6 +112,7 @@ export const ConsolidationChessTable: React.FC<ConsolidationChessTableI> = ({
|
||||
}
|
||||
});
|
||||
|
||||
// сумму, которую не удалось сложить, не выдумываем нулём — ячейка покажет «—»
|
||||
totalRow[colCompany.id] = sumAmounts(amountsToSum) ?? '';
|
||||
});
|
||||
|
||||
@ -121,9 +123,9 @@ export const ConsolidationChessTable: React.FC<ConsolidationChessTableI> = ({
|
||||
|
||||
return (
|
||||
<Table<MatrixCompany>
|
||||
className={!isFullscreen ? 'chessTable customHeight' : 'chessTable'}
|
||||
className={isHalfFiltered && !isFullscreen ? 'chessTable customHeight' : 'chessTable'}
|
||||
dataSource={isHalfFiltered ? [] : data?.companies || []}
|
||||
columns={isHalfFiltered ? [] : columns || []}
|
||||
columns={isHalfFiltered ? [] : columns}
|
||||
rowKey='id'
|
||||
pagination={false}
|
||||
loading={loading}
|
||||
@ -133,7 +135,7 @@ export const ConsolidationChessTable: React.FC<ConsolidationChessTableI> = ({
|
||||
title={() => toolbar}
|
||||
locale={{
|
||||
emptyText: isHalfFiltered ? (
|
||||
<div style={{ padding: '40px 0', color: '#8c8c8c', fontSize: 16 }}>Для получения данных выберите счета по Дт и Кт</div>
|
||||
<div style={{ padding: '40px 0', color: '#8c8c8c', fontSize: 16 }}>{CHESS_HALF_FILTER_HINT}</div>
|
||||
) : (
|
||||
'Нет данных'
|
||||
),
|
||||
|
||||
@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
|
||||
import { ApiError, api } from '../../../api/client';
|
||||
import { type OptionType, TreeFilterDropdown } from '../../../components/TreeFilterDropdown/TreeFilterDropdown';
|
||||
import type { Account, MatrixCompany, Quarter } from '../../../types/api';
|
||||
import { CHESS_HALF_FILTER_HINT, chessMatrixMode } from '../../../utils/chessMatrixMode';
|
||||
import { quarterShortLabel } from '../../../utils/labels';
|
||||
import { useNotificationApi } from '../../../utils/useNotification';
|
||||
|
||||
@ -109,7 +110,8 @@ export const ConsolidationChessTableFilter: React.FC<ConsolidationChessTableFilt
|
||||
loadAccounts();
|
||||
}, [notification]);
|
||||
|
||||
const warningBlock = <span style={{ color: '#d46b08', fontSize: 13 }}>Выберите счета по Дт и Кт для фильтрации</span>;
|
||||
const warningBlock =
|
||||
chessMatrixMode(debit, credit) === 'half' ? <span style={{ color: '#d46b08', fontSize: 13 }}>{CHESS_HALF_FILTER_HINT}</span> : null;
|
||||
|
||||
return (
|
||||
<Space size='middle' wrap>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user