Добавил черновики накладных и OCR через Яндекс. LLM для расшифровки универсальный

This commit is contained in:
2025-12-17 03:38:24 +03:00
parent fda30276a5
commit e2df2350f7
32 changed files with 1785 additions and 214 deletions

View File

@@ -7,7 +7,12 @@ import type {
InvoiceResponse,
ProductMatch,
Recommendation,
UnmatchedItem
UnmatchedItem,
Store,
Supplier,
DraftInvoice,
UpdateDraftItemRequest,
CommitDraftRequest
} from './types';
// Базовый URL
@@ -28,6 +33,14 @@ apiClient.interceptors.response.use(
}
);
// Мок поставщиков (так как эндпоинта пока нет)
const MOCK_SUPPLIERS: Supplier[] = [
{ id: '00000000-0000-0000-0000-000000000001', name: 'ООО "Рога и Копыта"' },
{ id: '00000000-0000-0000-0000-000000000002', name: 'ИП Иванов (Овощи)' },
{ id: '00000000-0000-0000-0000-000000000003', name: 'Metro Cash&Carry' },
{ id: '00000000-0000-0000-0000-000000000004', name: 'Simple Wine' },
];
export const api = {
checkHealth: async (): Promise<HealthResponse> => {
const { data } = await apiClient.get<HealthResponse>('/health');
@@ -64,4 +77,39 @@ export const api = {
const { data } = await apiClient.post<InvoiceResponse>('/invoices/send', payload);
return data;
},
// Получить список складов
getStores: async (): Promise<Store[]> => {
const { data } = await apiClient.get<Store[]>('/dictionaries/stores');
return data;
},
// Получить список поставщиков (Mock)
getSuppliers: async (): Promise<Supplier[]> => {
// Имитация асинхронности
return new Promise((resolve) => {
setTimeout(() => resolve(MOCK_SUPPLIERS), 300);
});
},
// Получить черновик
getDraft: async (id: string): Promise<DraftInvoice> => {
const { data } = await apiClient.get<DraftInvoice>(`/drafts/${id}`);
return data;
},
// Обновить строку черновика (и обучить модель)
updateDraftItem: async (draftId: string, itemId: string, payload: UpdateDraftItemRequest): Promise<DraftInvoice> => {
// Бэкенд возвращает обновленный черновик целиком (обычно) или обновленный item.
// Предположим, что возвращается обновленный Item или просто 200 OK.
// Но для React Query удобно возвращать данные.
// Если бэк возвращает только item, типизацию нужно уточнить. Пока ждем DraftInvoice или any.
const { data } = await apiClient.patch<DraftInvoice>(`/drafts/${draftId}/items/${itemId}`, payload);
return data;
},
// Зафиксировать черновик
commitDraft: async (draftId: string, payload: CommitDraftRequest): Promise<{ document_number: string }> => {
const { data } = await apiClient.post<{ document_number: string }>(`/drafts/${draftId}/commit`, payload);
return data;
},
};

View File

@@ -95,4 +95,69 @@ export interface InvoiceResponse {
export interface HealthResponse {
status: string;
time: string;
}
// --- Справочники ---
export interface Store {
id: UUID;
name: string;
}
export interface Supplier {
id: UUID;
name: string;
}
// --- Черновик Накладной (Draft) ---
export type DraftStatus = 'PROCESSING' | 'READY_TO_VERIFY' | 'COMPLETED' | 'ERROR';
export interface DraftItem {
id: UUID;
// Данные из OCR (Read-only)
raw_name: string;
raw_amount: number;
raw_price: number;
// Редактируемые данные
product_id: UUID | null;
container_id: UUID | null; // Фасовка
quantity: number;
price: number;
sum: number;
// Мета-данные
is_matched: boolean;
product?: CatalogItem; // Развернутый объект для UI
container?: ProductContainer; // Развернутый объект для UI
}
export interface DraftInvoice {
id: UUID;
status: DraftStatus;
document_number: string;
date_incoming: string | null; // YYYY-MM-DD
store_id: UUID | null;
supplier_id: UUID | null;
comment: string;
items: DraftItem[];
created_at?: string;
}
// DTO для обновления строки
export interface UpdateDraftItemRequest {
product_id?: UUID;
container_id?: UUID | null; // null если сбросили фасовку
quantity?: number;
price?: number;
}
// DTO для коммита
export interface CommitDraftRequest {
date_incoming: string;
store_id: UUID;
supplier_id: UUID;
comment: string;
}