REFACTOR data service
This commit is contained in:
297
frontend/src/api/services/sales.service.ts
Normal file
297
frontend/src/api/services/sales.service.ts
Normal file
@@ -0,0 +1,297 @@
|
||||
// frontend/src/api/services/sales.service.ts
|
||||
/**
|
||||
* Sales Data Service
|
||||
* Handles sales data operations for the sales microservice
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client';
|
||||
import { RequestTimeouts } from '../client/config';
|
||||
import type {
|
||||
SalesData,
|
||||
SalesValidationResult,
|
||||
SalesDataQuery,
|
||||
SalesDataImport,
|
||||
SalesImportResult,
|
||||
DashboardStats,
|
||||
PaginatedResponse,
|
||||
ActivityItem,
|
||||
} from '../types';
|
||||
|
||||
export class SalesService {
|
||||
/**
|
||||
* Upload Sales History File
|
||||
*/
|
||||
async uploadSalesHistory(
|
||||
tenantId: string,
|
||||
file: File,
|
||||
additionalData?: Record<string, any>
|
||||
): Promise<SalesImportResult> {
|
||||
// Determine file format
|
||||
const fileName = file.name.toLowerCase();
|
||||
let fileFormat: string;
|
||||
|
||||
if (fileName.endsWith('.csv')) {
|
||||
fileFormat = 'csv';
|
||||
} else if (fileName.endsWith('.json')) {
|
||||
fileFormat = 'json';
|
||||
} else if (fileName.endsWith('.xlsx') || fileName.endsWith('.xls')) {
|
||||
fileFormat = 'excel';
|
||||
} else {
|
||||
fileFormat = 'csv'; // Default fallback
|
||||
}
|
||||
|
||||
const uploadData = {
|
||||
file_format: fileFormat,
|
||||
...additionalData,
|
||||
};
|
||||
|
||||
return apiClient.upload(
|
||||
`/tenants/${tenantId}/sales/import`,
|
||||
file,
|
||||
uploadData,
|
||||
{
|
||||
timeout: RequestTimeouts.LONG,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Sales Data
|
||||
*/
|
||||
async validateSalesData(
|
||||
tenantId: string,
|
||||
file: File
|
||||
): Promise<SalesValidationResult> {
|
||||
const fileName = file.name.toLowerCase();
|
||||
let fileFormat: string;
|
||||
|
||||
if (fileName.endsWith('.csv')) {
|
||||
fileFormat = 'csv';
|
||||
} else if (fileName.endsWith('.json')) {
|
||||
fileFormat = 'json';
|
||||
} else if (fileName.endsWith('.xlsx') || fileName.endsWith('.xls')) {
|
||||
fileFormat = 'excel';
|
||||
} else {
|
||||
fileFormat = 'csv';
|
||||
}
|
||||
|
||||
return apiClient.upload(
|
||||
`/tenants/${tenantId}/sales/import/validate`,
|
||||
file,
|
||||
{
|
||||
file_format: fileFormat,
|
||||
validate_only: true,
|
||||
source: 'onboarding_upload',
|
||||
},
|
||||
{
|
||||
timeout: RequestTimeouts.MEDIUM,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Sales Data
|
||||
*/
|
||||
async getSalesData(
|
||||
tenantId: string,
|
||||
query?: SalesDataQuery
|
||||
): Promise<PaginatedResponse<SalesData>> {
|
||||
return apiClient.get(`/tenants/${tenantId}/sales`, { params: query });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Single Sales Record
|
||||
*/
|
||||
async getSalesRecord(tenantId: string, recordId: string): Promise<SalesData> {
|
||||
return apiClient.get(`/tenants/${tenantId}/sales/${recordId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Sales Record
|
||||
*/
|
||||
async updateSalesRecord(
|
||||
tenantId: string,
|
||||
recordId: string,
|
||||
data: Partial<SalesData>
|
||||
): Promise<SalesData> {
|
||||
return apiClient.put(`/tenants/${tenantId}/sales/${recordId}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Sales Record
|
||||
*/
|
||||
async deleteSalesRecord(tenantId: string, recordId: string): Promise<{ message: string }> {
|
||||
return apiClient.delete(`/tenants/${tenantId}/sales/${recordId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Dashboard Statistics
|
||||
*/
|
||||
async getDashboardStats(tenantId: string): Promise<DashboardStats> {
|
||||
return apiClient.get(`/tenants/${tenantId}/sales/stats`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Analytics Data
|
||||
*/
|
||||
async getAnalytics(
|
||||
tenantId: string,
|
||||
params?: {
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
product_names?: string[];
|
||||
metrics?: string[];
|
||||
}
|
||||
): Promise<any> {
|
||||
return apiClient.get(`/tenants/${tenantId}/sales/analytics`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* Export Sales Data
|
||||
*/
|
||||
async exportSalesData(
|
||||
tenantId: string,
|
||||
format: 'csv' | 'excel' | 'json',
|
||||
query?: SalesDataQuery
|
||||
): Promise<Blob> {
|
||||
const response = await apiClient.request(`/tenants/${tenantId}/sales/export`, {
|
||||
method: 'GET',
|
||||
params: { ...query, format },
|
||||
headers: {
|
||||
'Accept': format === 'csv' ? 'text/csv' :
|
||||
format === 'excel' ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' :
|
||||
'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
return new Blob([response], {
|
||||
type: format === 'csv' ? 'text/csv' :
|
||||
format === 'excel' ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' :
|
||||
'application/json',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Recent Activity
|
||||
*/
|
||||
async getRecentActivity(tenantId: string, limit?: number): Promise<ActivityItem[]> {
|
||||
return apiClient.get(`/tenants/${tenantId}/sales/activity`, {
|
||||
params: { limit },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Products List from Sales Data
|
||||
*/
|
||||
async getProductsList(tenantId: string): Promise<string[]> {
|
||||
try {
|
||||
const response = await apiClient.get(`/tenants/${tenantId}/sales/products`);
|
||||
|
||||
console.log('🔍 Products API Response Analysis:');
|
||||
console.log('- Type:', typeof response);
|
||||
console.log('- Is Array:', Array.isArray(response));
|
||||
console.log('- Keys:', Object.keys(response || {}));
|
||||
console.log('- Response:', response);
|
||||
|
||||
let productsArray: any[] = [];
|
||||
|
||||
// ✅ FIX: Handle different response formats
|
||||
if (Array.isArray(response)) {
|
||||
// Standard array response
|
||||
productsArray = response;
|
||||
console.log('✅ Response is already an array');
|
||||
} else if (response && typeof response === 'object') {
|
||||
// Object with numeric keys - convert to array
|
||||
const keys = Object.keys(response);
|
||||
if (keys.length > 0 && keys.every(key => !isNaN(Number(key)))) {
|
||||
// Object has numeric keys like {0: {...}, 1: {...}}
|
||||
productsArray = Object.values(response);
|
||||
console.log('✅ Converted object with numeric keys to array');
|
||||
} else {
|
||||
console.warn('⚠️ Response is object but not with numeric keys:', response);
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ Response is not array or object:', response);
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log('📦 Products array:', productsArray);
|
||||
|
||||
// Extract product names from the array
|
||||
const productNames = productsArray
|
||||
.map((product: any) => {
|
||||
if (typeof product === 'string') {
|
||||
return product;
|
||||
}
|
||||
if (product && typeof product === 'object') {
|
||||
return product.product_name ||
|
||||
product.name ||
|
||||
product.productName ||
|
||||
null;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean) // Remove null/undefined values
|
||||
.filter((name: string) => name.trim().length > 0); // Remove empty strings
|
||||
|
||||
console.log('📋 Extracted product names:', productNames);
|
||||
|
||||
if (productNames.length === 0) {
|
||||
console.warn('⚠️ No valid product names extracted from response');
|
||||
}
|
||||
|
||||
return productNames;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to fetch products list:', error);
|
||||
|
||||
// Return fallback products for Madrid bakery
|
||||
return [
|
||||
'Croissants',
|
||||
'Pan de molde',
|
||||
'Baguettes',
|
||||
'Café',
|
||||
'Napolitanas',
|
||||
'Pan integral',
|
||||
'Magdalenas',
|
||||
'Churros'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Sales Summary by Period
|
||||
*/
|
||||
async getSalesSummary(
|
||||
tenantId: string,
|
||||
period: 'daily' | 'weekly' | 'monthly' = 'daily'
|
||||
): Promise<any> {
|
||||
return apiClient.get(`/tenants/${tenantId}/sales/summary`, {
|
||||
params: { period }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Sales Analytics
|
||||
*/
|
||||
async getSalesAnalytics(
|
||||
tenantId: string,
|
||||
startDate?: string,
|
||||
endDate?: string
|
||||
): Promise<{
|
||||
total_revenue: number;
|
||||
waste_reduction_percentage?: number;
|
||||
forecast_accuracy?: number;
|
||||
stockout_events?: number;
|
||||
}> {
|
||||
return apiClient.get(`/tenants/${tenantId}/sales/analytics/summary`, {
|
||||
params: {
|
||||
start_date: startDate,
|
||||
end_date: endDate
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const salesService = new SalesService();
|
||||
Reference in New Issue
Block a user