Andrey Epifancev 1249e2ff3b feat(osv): сопоставление контрагента в сводной ОСВ — «Указать» → «Связать» по макету [32] (#14)
- ИНН-ячейка строк без сопоставления подсвечена, ссылка «Указать» рядом с ИНН из файла [32-osv-no-inn].
- Модалка «Выбор организации для связи с …» [32-osv-map-counterparty]: поиск с паузой 300 мс,
  scope group / весь справочник (от 3 символов), блок «Рекомендуемые», «Связать».
- POST …/osv/entries/{последняя entry_id}/counterparty; 409 ALIAS_TAKEN → перенос алиаса (move_alias),
  LOCK_REQUIRED → «Взять версию в работу?» → POST …/lock → повтор, LOCKED_BY_OTHER → имя держателя.
- После успеха перечитываются строки и сводка («Осталось N»).
- Чистые функции counterpartyMapping.ts + тесты; спека consolidated-osv-ui.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-08 17:39:23 +04:00

40 lines
1.8 KiB
TypeScript

import { api, apiFetchBlob } from '../../api/client';
import type {
CandidateOut,
ConsolidatedRowOut,
MapCounterpartyIn,
MapCounterpartyOut,
OsvFilters,
OsvSummaryOut,
Page,
} from '../../types/api';
export function buildOsvQuery(filters: OsvFilters, page?: { limit: number; offset: number }): string {
const params = new URLSearchParams();
if (filters.legal_entity_id) params.set('legal_entity_id', filters.legal_entity_id);
if (filters.account_root) params.set('account_root', filters.account_root);
if (filters.counterparty_type) params.set('counterparty_type', filters.counterparty_type);
if (filters.unmapped) params.set('unmapped', 'true');
const q = filters.q?.trim();
if (q) params.set('q', q);
if (page) {
params.set('limit', String(page.limit));
params.set('offset', String(page.offset));
}
const s = params.toString();
return s ? `?${s}` : '';
}
const base = (versionId: string) => `/consolidations/versions/${versionId}/osv`;
export const getConsolidatedOsv = (versionId: string, filters: OsvFilters, limit: number, offset: number) =>
api.get<Page<ConsolidatedRowOut>>(`${base(versionId)}${buildOsvQuery(filters, { limit, offset })}`);
export const getOsvSummary = (versionId: string) => api.get<OsvSummaryOut>(`${base(versionId)}/summary`);
export const exportOsv = (versionId: string, filters: OsvFilters) => apiFetchBlob(`${base(versionId)}/export${buildOsvQuery(filters)}`);
/** `query` — из `buildCandidatesQuery` (виджет ConsolidatedOsv). */
export const getCounterpartyCandidates = (versionId: string, query: string) =>
api.get<CandidateOut[]>(`/consolidations/versions/${versionId}/counterparty-candidates${query}`);
export const mapCounterparty = (versionId: string, entryId: string, body: MapCounterpartyIn) =>
api.post<MapCounterpartyOut>(`${base(versionId)}/entries/${entryId}/counterparty`, body);