// ================================================================ // frontend/src/api/services/sales.ts // ================================================================ /** * Sales Service - Complete backend alignment * * Backend API structure (3-tier architecture): * - ATOMIC: sales_records.py * - OPERATIONS: sales_operations.py (validation, import, aggregation) * - ANALYTICS: analytics.py * * Last Updated: 2025-10-05 * Status: ✅ Complete - Zero drift with backend */ import { apiClient } from '../client'; import { // Sales Data SalesDataCreate, SalesDataUpdate, SalesDataResponse, SalesDataQuery, // Import ImportValidationResult, BulkImportResponse, ImportSummary, // Analytics SalesAnalytics, ProductSalesAnalytics, CategorySalesAnalytics, ChannelPerformance, } from '../types/sales'; export class SalesService { private readonly baseUrl = '/tenants'; // =================================================================== // ATOMIC: Sales Records CRUD // Backend: services/sales/app/api/sales_records.py // =================================================================== async createSalesRecord( tenantId: string, salesData: SalesDataCreate ): Promise { return apiClient.post( `${this.baseUrl}/${tenantId}/sales/sales`, salesData ); } async getSalesRecords( tenantId: string, query?: SalesDataQuery ): Promise { const queryParams = new URLSearchParams(); if (query?.start_date) queryParams.append('start_date', query.start_date); if (query?.end_date) queryParams.append('end_date', query.end_date); if (query?.product_name) queryParams.append('product_name', query.product_name); if (query?.product_category) queryParams.append('product_category', query.product_category); if (query?.location_id) queryParams.append('location_id', query.location_id); if (query?.sales_channel) queryParams.append('sales_channel', query.sales_channel); if (query?.source) queryParams.append('source', query.source); if (query?.is_validated !== undefined) queryParams.append('is_validated', query.is_validated.toString()); if (query?.limit !== undefined) queryParams.append('limit', query.limit.toString()); if (query?.offset !== undefined) queryParams.append('offset', query.offset.toString()); if (query?.order_by) queryParams.append('order_by', query.order_by); if (query?.order_direction) queryParams.append('order_direction', query.order_direction); const url = queryParams.toString() ? `${this.baseUrl}/${tenantId}/sales/sales?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/sales/sales`; return apiClient.get(url); } async getSalesRecord(tenantId: string, recordId: string): Promise { return apiClient.get( `${this.baseUrl}/${tenantId}/sales/sales/${recordId}` ); } async updateSalesRecord( tenantId: string, recordId: string, updateData: SalesDataUpdate ): Promise { return apiClient.put( `${this.baseUrl}/${tenantId}/sales/sales/${recordId}`, updateData ); } async deleteSalesRecord(tenantId: string, recordId: string): Promise<{ message: string }> { return apiClient.delete<{ message: string }>( `${this.baseUrl}/${tenantId}/sales/sales/${recordId}` ); } async getProductCategories(tenantId: string): Promise { return apiClient.get(`${this.baseUrl}/${tenantId}/sales/sales/categories`); } // =================================================================== // OPERATIONS: Validation // Backend: services/sales/app/api/sales_operations.py // =================================================================== async validateSalesRecord( tenantId: string, recordId: string, validationNotes?: string ): Promise { const queryParams = new URLSearchParams(); if (validationNotes) queryParams.append('validation_notes', validationNotes); const url = queryParams.toString() ? `${this.baseUrl}/${tenantId}/sales/operations/validate-record/${recordId}?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/sales/operations/validate-record/${recordId}`; return apiClient.post(url); } // =================================================================== // OPERATIONS: Cross-Service Queries // Backend: services/sales/app/api/sales_operations.py // =================================================================== async getProductSales( tenantId: string, inventoryProductId: string, startDate?: string, endDate?: string ): Promise { const queryParams = new URLSearchParams(); if (startDate) queryParams.append('start_date', startDate); if (endDate) queryParams.append('end_date', endDate); const url = queryParams.toString() ? `${this.baseUrl}/${tenantId}/inventory-products/${inventoryProductId}/sales?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/inventory-products/${inventoryProductId}/sales`; return apiClient.get(url); } // =================================================================== // OPERATIONS: Data Import // Backend: services/sales/app/api/sales_operations.py // =================================================================== async validateImportFile(tenantId: string, file: File): Promise { const formData = new FormData(); formData.append('file', file); return apiClient.uploadFile( `${this.baseUrl}/${tenantId}/sales/operations/import/validate`, formData ); } async importSalesData( tenantId: string, file: File, skipValidation: boolean = false ): Promise { const formData = new FormData(); formData.append('file', file); formData.append('skip_validation', skipValidation.toString()); return apiClient.uploadFile( `${this.baseUrl}/${tenantId}/sales/operations/import`, formData ); } async getImportHistory( tenantId: string, limit: number = 50, offset: number = 0 ): Promise { const queryParams = new URLSearchParams(); queryParams.append('limit', limit.toString()); queryParams.append('offset', offset.toString()); return apiClient.get( `${this.baseUrl}/${tenantId}/sales/operations/import/history?${queryParams.toString()}` ); } async downloadImportTemplate(tenantId: string): Promise { return apiClient.get( `${this.baseUrl}/${tenantId}/sales/operations/import/template`, { responseType: 'blob' } ); } // =================================================================== // OPERATIONS: Aggregation // Backend: services/sales/app/api/sales_operations.py // =================================================================== async aggregateSalesByProduct( tenantId: string, startDate?: string, endDate?: string ): Promise { const queryParams = new URLSearchParams(); if (startDate) queryParams.append('start_date', startDate); if (endDate) queryParams.append('end_date', endDate); const url = queryParams.toString() ? `${this.baseUrl}/${tenantId}/sales/operations/aggregate/by-product?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/sales/operations/aggregate/by-product`; return apiClient.get(url); } async aggregateSalesByCategory( tenantId: string, startDate?: string, endDate?: string ): Promise { const queryParams = new URLSearchParams(); if (startDate) queryParams.append('start_date', startDate); if (endDate) queryParams.append('end_date', endDate); const url = queryParams.toString() ? `${this.baseUrl}/${tenantId}/sales/operations/aggregate/by-category?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/sales/operations/aggregate/by-category`; return apiClient.get(url); } async aggregateSalesByChannel( tenantId: string, startDate?: string, endDate?: string ): Promise { const queryParams = new URLSearchParams(); if (startDate) queryParams.append('start_date', startDate); if (endDate) queryParams.append('end_date', endDate); const url = queryParams.toString() ? `${this.baseUrl}/${tenantId}/sales/operations/aggregate/by-channel?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/sales/operations/aggregate/by-channel`; return apiClient.get(url); } // =================================================================== // ANALYTICS: Sales Summary // Backend: services/sales/app/api/analytics.py // =================================================================== async getSalesAnalytics( tenantId: string, startDate?: string, endDate?: string ): Promise { const queryParams = new URLSearchParams(); if (startDate) queryParams.append('start_date', startDate); if (endDate) queryParams.append('end_date', endDate); const url = queryParams.toString() ? `${this.baseUrl}/${tenantId}/sales/analytics/summary?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/sales/analytics/summary`; return apiClient.get(url); } } export const salesService = new SalesService();