Files
bakery-ia/frontend/src/api/services/sales.ts

275 lines
9.5 KiB
TypeScript
Raw Normal View History

2025-10-06 15:27:01 +02:00
// ================================================================
// frontend/src/api/services/sales.ts
// ================================================================
/**
2025-10-06 15:27:01 +02:00
* 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
*/
2025-10-06 15:27:01 +02:00
import { apiClient } from '../client';
import {
2025-10-06 15:27:01 +02:00
// Sales Data
SalesDataCreate,
SalesDataUpdate,
SalesDataResponse,
SalesDataQuery,
2025-10-06 15:27:01 +02:00
// Import
ImportValidationResult,
BulkImportResponse,
ImportSummary,
// Analytics
SalesAnalytics,
2025-10-06 15:27:01 +02:00
ProductSalesAnalytics,
CategorySalesAnalytics,
ChannelPerformance,
} from '../types/sales';
export class SalesService {
private readonly baseUrl = '/tenants';
2025-10-06 15:27:01 +02:00
// ===================================================================
// ATOMIC: Sales Records CRUD
// Backend: services/sales/app/api/sales_records.py
// ===================================================================
async createSalesRecord(
2025-10-06 15:27:01 +02:00
tenantId: string,
salesData: SalesDataCreate
): Promise<SalesDataResponse> {
2025-10-06 15:27:01 +02:00
return apiClient.post<SalesDataResponse>(
`${this.baseUrl}/${tenantId}/sales/sales`,
salesData
);
}
async getSalesRecords(
2025-10-06 15:27:01 +02:00
tenantId: string,
query?: SalesDataQuery
): Promise<SalesDataResponse[]> {
const queryParams = new URLSearchParams();
2025-10-06 15:27:01 +02:00
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);
2025-10-06 15:27:01 +02:00
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);
2025-10-06 15:27:01 +02:00
const url = queryParams.toString()
? `${this.baseUrl}/${tenantId}/sales/sales?${queryParams.toString()}`
: `${this.baseUrl}/${tenantId}/sales/sales`;
return apiClient.get<SalesDataResponse[]>(url);
}
2025-10-06 15:27:01 +02:00
async getSalesRecord(tenantId: string, recordId: string): Promise<SalesDataResponse> {
return apiClient.get<SalesDataResponse>(
`${this.baseUrl}/${tenantId}/sales/sales/${recordId}`
);
}
async updateSalesRecord(
2025-10-06 15:27:01 +02:00
tenantId: string,
recordId: string,
updateData: SalesDataUpdate
): Promise<SalesDataResponse> {
2025-10-06 15:27:01 +02:00
return apiClient.put<SalesDataResponse>(
`${this.baseUrl}/${tenantId}/sales/sales/${recordId}`,
updateData
);
}
2025-10-06 15:27:01 +02:00
async deleteSalesRecord(tenantId: string, recordId: string): Promise<{ message: string }> {
return apiClient.delete<{ message: string }>(
`${this.baseUrl}/${tenantId}/sales/sales/${recordId}`
);
}
2025-10-06 15:27:01 +02:00
async getProductCategories(tenantId: string): Promise<string[]> {
2025-10-07 07:15:07 +02:00
return apiClient.get<string[]>(`${this.baseUrl}/${tenantId}/sales/categories`);
2025-10-06 15:27:01 +02:00
}
// ===================================================================
// OPERATIONS: Validation
// Backend: services/sales/app/api/sales_operations.py
// ===================================================================
async validateSalesRecord(
2025-10-06 15:27:01 +02:00
tenantId: string,
recordId: string,
validationNotes?: string
): Promise<SalesDataResponse> {
const queryParams = new URLSearchParams();
if (validationNotes) queryParams.append('validation_notes', validationNotes);
2025-10-06 15:27:01 +02:00
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<SalesDataResponse>(url);
}
2025-10-06 15:27:01 +02:00
// ===================================================================
// OPERATIONS: Cross-Service Queries
// Backend: services/sales/app/api/sales_operations.py
// ===================================================================
async getProductSales(
tenantId: string,
2025-10-06 15:27:01 +02:00
inventoryProductId: string,
startDate?: string,
endDate?: string
2025-10-06 15:27:01 +02:00
): Promise<SalesDataResponse[]> {
const queryParams = new URLSearchParams();
if (startDate) queryParams.append('start_date', startDate);
if (endDate) queryParams.append('end_date', endDate);
2025-10-06 15:27:01 +02:00
const url = queryParams.toString()
? `${this.baseUrl}/${tenantId}/inventory-products/${inventoryProductId}/sales?${queryParams.toString()}`
: `${this.baseUrl}/${tenantId}/inventory-products/${inventoryProductId}/sales`;
2025-10-06 15:27:01 +02:00
return apiClient.get<SalesDataResponse[]>(url);
}
2025-10-06 15:27:01 +02:00
// ===================================================================
// OPERATIONS: Data Import
// Backend: services/sales/app/api/sales_operations.py
// ===================================================================
async validateImportFile(tenantId: string, file: File): Promise<ImportValidationResult> {
const formData = new FormData();
formData.append('file', file);
return apiClient.uploadFile<ImportValidationResult>(
`${this.baseUrl}/${tenantId}/sales/operations/import/validate`,
formData
);
}
async importSalesData(
tenantId: string,
file: File,
skipValidation: boolean = false
): Promise<BulkImportResponse> {
const formData = new FormData();
formData.append('file', file);
formData.append('skip_validation', skipValidation.toString());
return apiClient.uploadFile<BulkImportResponse>(
`${this.baseUrl}/${tenantId}/sales/operations/import`,
formData
);
}
async getImportHistory(
tenantId: string,
limit: number = 50,
offset: number = 0
): Promise<ImportSummary[]> {
const queryParams = new URLSearchParams();
queryParams.append('limit', limit.toString());
queryParams.append('offset', offset.toString());
return apiClient.get<ImportSummary[]>(
`${this.baseUrl}/${tenantId}/sales/operations/import/history?${queryParams.toString()}`
);
}
async downloadImportTemplate(tenantId: string): Promise<Blob> {
return apiClient.get<Blob>(
`${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
2025-10-06 15:27:01 +02:00
): Promise<ProductSalesAnalytics[]> {
const queryParams = new URLSearchParams();
if (startDate) queryParams.append('start_date', startDate);
if (endDate) queryParams.append('end_date', endDate);
2025-10-06 15:27:01 +02:00
const url = queryParams.toString()
? `${this.baseUrl}/${tenantId}/sales/operations/aggregate/by-product?${queryParams.toString()}`
: `${this.baseUrl}/${tenantId}/sales/operations/aggregate/by-product`;
2025-10-06 15:27:01 +02:00
return apiClient.get<ProductSalesAnalytics[]>(url);
}
2025-10-06 15:27:01 +02:00
async aggregateSalesByCategory(
tenantId: string,
startDate?: string,
endDate?: string
): Promise<CategorySalesAnalytics[]> {
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<CategorySalesAnalytics[]>(url);
}
async aggregateSalesByChannel(
tenantId: string,
startDate?: string,
endDate?: string
): Promise<ChannelPerformance[]> {
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<ChannelPerformance[]>(url);
}
// ===================================================================
// ANALYTICS: Sales Summary
// Backend: services/sales/app/api/analytics.py
// ===================================================================
async getSalesAnalytics(
tenantId: string,
startDate?: string,
endDate?: string
): Promise<SalesAnalytics> {
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<SalesAnalytics>(url);
}
}
2025-10-06 15:27:01 +02:00
export const salesService = new SalesService();