Итого:
diff --git a/rmser-view/src/services/api.ts b/rmser-view/src/services/api.ts
index 071ded3..54f380d 100644
--- a/rmser-view/src/services/api.ts
+++ b/rmser-view/src/services/api.ts
@@ -13,15 +13,25 @@ import type {
DraftInvoice,
UpdateDraftItemRequest,
CommitDraftRequest,
- // Новые типы
ProductSearchResult,
AddContainerRequest,
- AddContainerResponse
+ AddContainerResponse,
+ DictionariesResponse,
+ DraftSummary
} from './types';
// Базовый URL
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080/api';
+// Телеграм объект
+const tg = window.Telegram?.WebApp;
+
+// ID для локальной разработки (Fallback)
+const DEBUG_USER_ID = 665599275;
+
+// Событие для глобальной обработки 401
+export const UNAUTHORIZED_EVENT = 'rms_unauthorized';
+
const apiClient = axios.create({
baseURL: API_URL,
headers: {
@@ -29,33 +39,36 @@ const apiClient = axios.create({
},
});
+// --- Request Interceptor (Авторизация) ---
+apiClient.interceptors.request.use((config) => {
+ // 1. Пробуем взять ID из Telegram WebApp
+ // 2. Ищем в URL параметрах (удобно для тестов в браузере: ?_tg_id=123)
+ // 3. Используем хардкод для локальной разработки
+ const urlParams = new URLSearchParams(window.location.search);
+ const paramId = urlParams.get('_tg_id');
+
+ const userId = tg?.initDataUnsafe?.user?.id || paramId || DEBUG_USER_ID;
+
+ if (userId) {
+ config.headers['X-Telegram-User-ID'] = userId.toString();
+ }
+
+ return config;
+});
+
+// --- Response Interceptor (Обработка ошибок) ---
apiClient.interceptors.response.use(
(response) => response,
(error) => {
+ if (error.response && error.response.status === 401) {
+ // Генерируем кастомное событие, которое поймает App.tsx
+ window.dispatchEvent(new Event(UNAUTHORIZED_EVENT));
+ }
console.error('API Error:', error);
return Promise.reject(error);
}
);
-// Мок поставщиков
-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 interface DraftSummary {
- id: string;
- document_number: string;
- date_incoming: string;
- status: string;
- items_count: number;
- total_sum: number;
- store_name?: string;
-}
-
export const api = {
checkHealth: async (): Promise
=> {
const { data } = await apiClient.get('/health');
@@ -67,14 +80,11 @@ export const api = {
return data;
},
- // Оставляем для совместимости со старыми компонентами (если используются),
- // но в Draft Flow будем использовать поиск.
getCatalogItems: async (): Promise => {
const { data } = await apiClient.get('/ocr/catalog');
return data;
},
- // Поиск товаров ---
searchProducts: async (query: string): Promise => {
const { data } = await apiClient.get('/ocr/search', {
params: { q: query }
@@ -82,9 +92,7 @@ export const api = {
return data;
},
- // Создание фасовки ---
createContainer: async (payload: AddContainerRequest): Promise => {
- // Внимание: URL эндпоинта взят из вашего ТЗ (/drafts/container)
const { data } = await apiClient.post('/drafts/container', payload);
return data;
},
@@ -109,15 +117,28 @@ export const api = {
return data;
},
- getStores: async (): Promise => {
- const { data } = await apiClient.get('/dictionaries/stores');
+ // --- НОВЫЙ МЕТОД: Получение всех справочников ---
+ getDictionaries: async (): Promise => {
+ const { data } = await apiClient.get('/dictionaries');
return data;
},
+ // Старые методы оставляем для совместимости, но они могут вызывать getDictionaries внутри или deprecated endpoint
+ getStores: async (): Promise => {
+ // Можно использовать новый эндпоинт и возвращать часть данных
+ const { data } = await apiClient.get('/dictionaries');
+ return data.stores;
+ },
+
getSuppliers: async (): Promise => {
- return new Promise((resolve) => {
- setTimeout(() => resolve(MOCK_SUPPLIERS), 300);
- });
+ // Реальный запрос вместо мока
+ const { data } = await apiClient.get('/dictionaries');
+ return data.suppliers;
+ },
+
+ getDrafts: async (): Promise => {
+ const { data } = await apiClient.get('/drafts');
+ return data;
},
getDraft: async (id: string): Promise => {
@@ -125,12 +146,6 @@ export const api = {
return data;
},
- // Получить список черновиков
- getDrafts: async (): Promise => {
- const { data } = await apiClient.get('/drafts');
- return data;
- },
-
updateDraftItem: async (draftId: string, itemId: string, payload: UpdateDraftItemRequest): Promise => {
const { data } = await apiClient.patch(`/drafts/${draftId}/items/${itemId}`, payload);
return data;
@@ -140,8 +155,7 @@ export const api = {
const { data } = await apiClient.post<{ document_number: string }>(`/drafts/${draftId}/commit`, payload);
return data;
},
-
- // Отменить/Удалить черновик
+
deleteDraft: async (id: string): Promise => {
await apiClient.delete(`/drafts/${id}`);
},
diff --git a/rmser-view/src/services/types.ts b/rmser-view/src/services/types.ts
index 9adc4c4..5f791ac 100644
--- a/rmser-view/src/services/types.ts
+++ b/rmser-view/src/services/types.ts
@@ -121,6 +121,11 @@ export interface Supplier {
name: string;
}
+export interface DictionariesResponse {
+ stores: Store[];
+ suppliers: Supplier[];
+}
+
// --- Черновик Накладной (Draft) ---
export type DraftStatus = 'PROCESSING' | 'READY_TO_VERIFY' | 'COMPLETED' | 'ERROR' | 'CANCELED';
@@ -146,6 +151,18 @@ export interface DraftItem {
container?: ProductContainer; // Развернутый объект для UI
}
+// --- Список Черновиков (Summary) ---
+export interface DraftSummary {
+ id: UUID;
+ document_number: string;
+ date_incoming: string;
+ status: DraftStatus; // Используем существующий тип статуса
+ items_count: number;
+ total_sum: number;
+ store_name?: string;
+ created_at: string;
+}
+
export interface DraftInvoice {
id: UUID;
status: DraftStatus;
diff --git a/rmser-view/src/vite-env.d.ts b/rmser-view/src/vite-env.d.ts
new file mode 100644
index 0000000..45d0dd5
--- /dev/null
+++ b/rmser-view/src/vite-env.d.ts
@@ -0,0 +1,24 @@
+///
+
+interface TelegramWebApp {
+ initData: string;
+ initDataUnsafe: {
+ user?: {
+ id: number;
+ first_name: string;
+ last_name?: string;
+ username?: string;
+ language_code?: string;
+ };
+ // ... другие поля по необходимости
+ };
+ close: () => void;
+ expand: () => void;
+ // ... другие методы
+}
+
+interface Window {
+ Telegram?: {
+ WebApp: TelegramWebApp;
+ };
+}
\ No newline at end of file