Fix new services implementation 3

This commit is contained in:
Urtzi Alfaro
2025-08-14 16:47:34 +02:00
parent 0951547e92
commit 03737430ee
51 changed files with 657 additions and 982 deletions

View File

@@ -163,7 +163,8 @@ export const useForecast = () => {
tenantId: string,
format: 'csv' | 'excel' | 'json',
params?: {
product_name?: string;
inventory_product_id?: string; // Primary way to filter by product
product_name?: string; // For backward compatibility
start_date?: string;
end_date?: string;
}

View File

@@ -91,7 +91,8 @@ export class ForecastingService {
async getForecasts(
tenantId: string,
params?: BaseQueryParams & {
product_name?: string;
inventory_product_id?: string; // Primary way to filter by product
product_name?: string; // For backward compatibility - will need inventory service lookup
start_date?: string;
end_date?: string;
model_id?: string;
@@ -158,7 +159,8 @@ export class ForecastingService {
}
return forecastsArray.map((forecast: any) => ({
product_name: forecast.product_name,
inventory_product_id: forecast.inventory_product_id,
product_name: forecast.product_name, // Optional - for display
next_day_prediction: forecast.predicted_demand || 0,
next_week_avg: forecast.predicted_demand || 0,
trend_direction: 'stable' as const,
@@ -168,9 +170,10 @@ export class ForecastingService {
} catch (error) {
console.error('QuickForecasts API call failed, using fallback data:', error);
// Return mock data for common bakery products
// Return mock data for common bakery products (using mock inventory_product_ids)
return [
{
inventory_product_id: 'mock-pan-de-molde-001',
product_name: 'Pan de Molde',
next_day_prediction: 25,
next_week_avg: 175,
@@ -179,6 +182,7 @@ export class ForecastingService {
last_updated: new Date().toISOString()
},
{
inventory_product_id: 'mock-baguettes-002',
product_name: 'Baguettes',
next_day_prediction: 20,
next_week_avg: 140,
@@ -187,6 +191,7 @@ export class ForecastingService {
last_updated: new Date().toISOString()
},
{
inventory_product_id: 'mock-croissants-003',
product_name: 'Croissants',
next_day_prediction: 15,
next_week_avg: 105,
@@ -195,6 +200,7 @@ export class ForecastingService {
last_updated: new Date().toISOString()
},
{
inventory_product_id: 'mock-magdalenas-004',
product_name: 'Magdalenas',
next_day_prediction: 12,
next_week_avg: 84,
@@ -244,7 +250,8 @@ export class ForecastingService {
tenantId: string,
format: 'csv' | 'excel' | 'json',
params?: {
product_name?: string;
inventory_product_id?: string; // Primary way to filter by product
product_name?: string; // For backward compatibility
start_date?: string;
end_date?: string;
}
@@ -272,7 +279,8 @@ export class ForecastingService {
async getForecastAccuracy(
tenantId: string,
params?: {
product_name?: string;
inventory_product_id?: string; // Primary way to filter by product
product_name?: string; // For backward compatibility
model_id?: string;
start_date?: string;
end_date?: string;
@@ -280,7 +288,8 @@ export class ForecastingService {
): Promise<{
overall_accuracy: number;
product_accuracy: Array<{
product_name: string;
inventory_product_id: string;
product_name?: string; // Optional - for display
accuracy: number;
sample_size: number;
}>;

View File

@@ -139,7 +139,8 @@ export class SalesService {
params?: {
start_date?: string;
end_date?: string;
product_names?: string[];
inventory_product_ids?: string[]; // Primary way to filter by products
product_names?: string[]; // For backward compatibility - will need inventory service lookup
metrics?: string[];
}
): Promise<any> {

View File

@@ -176,7 +176,8 @@ export interface PurchaseOrderItem {
price_list_item_id?: string;
ingredient_id: string;
product_code?: string;
product_name: string;
inventory_product_id: string; // Reference to inventory service product
product_name?: string; // Optional - for display, populated by frontend from inventory service
ordered_quantity: number;
unit_of_measure: string;
unit_price: number;
@@ -207,7 +208,8 @@ export interface CreatePurchaseOrderRequest {
items: {
ingredient_id: string;
product_code?: string;
product_name: string;
inventory_product_id: string; // Reference to inventory service product
product_name?: string; // Optional - for backward compatibility
ordered_quantity: number;
unit_of_measure: string;
unit_price: number;
@@ -268,7 +270,8 @@ export interface DeliveryItem {
delivery_id: string;
purchase_order_item_id: string;
ingredient_id: string;
product_name: string;
inventory_product_id: string; // Reference to inventory service product
product_name?: string; // Optional - for display, populated by frontend from inventory service
ordered_quantity: number;
delivered_quantity: number;
accepted_quantity: number;

View File

@@ -101,7 +101,8 @@ export class TrainingService {
async getModels(
tenantId: string,
params?: BaseQueryParams & {
product_name?: string;
inventory_product_id?: string; // Primary way to filter by product
product_name?: string; // For backward compatibility - will need inventory service lookup
is_active?: boolean;
}
): Promise<PaginatedResponse<ModelInfo>> {

View File

@@ -9,8 +9,10 @@ export interface SalesData {
id: string;
tenant_id: string;
date: string;
product_name: string;
category?: string;
inventory_product_id: string; // Reference to inventory service product
// Note: product_name now needs to be fetched from inventory service using inventory_product_id
product_name?: string; // Optional - for backward compatibility, populated by frontend logic
category?: string; // Optional - fetched from inventory service
quantity: number;
unit_price: number;
total_revenue: number;
@@ -55,7 +57,9 @@ export interface SalesDataQuery extends BaseQueryParams {
tenant_id: string;
start_date?: string;
end_date?: string;
product_names?: string[];
// Note: product_names filtering now requires inventory service integration or use inventory_product_ids
product_names?: string[]; // For backward compatibility - will need inventory service lookup
inventory_product_ids?: string[]; // Primary way to filter by products
location_ids?: string[];
sources?: string[];
min_quantity?: number;
@@ -64,7 +68,7 @@ export interface SalesDataQuery extends BaseQueryParams {
max_revenue?: number;
search_term?: string;
sales_channel?: string;
inventory_product_id?: string;
inventory_product_id?: string; // Single product filter
is_validated?: boolean;
}
@@ -115,7 +119,8 @@ export interface DashboardStats {
}
export interface ProductStats {
product_name: string;
inventory_product_id: string; // Reference to inventory service product
product_name?: string; // Optional - for display, populated by frontend from inventory service
total_quantity: number;
total_revenue: number;
avg_price: number;

View File

@@ -6,7 +6,7 @@
import { ExternalFactors } from './data';
export interface SingleForecastRequest {
product_name: string;
inventory_product_id: string;
forecast_date: string;
forecast_days: number;
location: string;
@@ -16,7 +16,8 @@ export interface SingleForecastRequest {
}
export interface BatchForecastRequest {
product_names?: string[];
inventory_product_ids?: string[]; // Primary way to specify products
product_names?: string[]; // For backward compatibility - will need inventory service lookup
forecast_date: string;
forecast_days: number;
location: string;
@@ -28,7 +29,8 @@ export interface BatchForecastRequest {
export interface ForecastResponse {
id: string;
tenant_id: string;
product_name: string;
inventory_product_id: string;
product_name?: string; // Optional - for display, populated by frontend from inventory service
forecast_date: string;
predicted_demand: number;
confidence_lower?: number;
@@ -77,7 +79,8 @@ export interface ForecastAlert {
}
export interface QuickForecast {
product_name: string;
inventory_product_id: string;
product_name?: string; // Optional - for display, populated by frontend from inventory service
next_day_prediction: number;
next_week_avg: number;
trend_direction: 'up' | 'down' | 'stable';

View File

@@ -14,7 +14,7 @@ export interface TrainingJobRequest {
}
export interface SingleProductTrainingRequest {
product_name: string;
inventory_product_id: string;
config?: TrainingJobConfig;
priority?: number;
}
@@ -81,11 +81,12 @@ export interface TrainingJobResults {
total_training_time_seconds: number;
average_model_accuracy?: number;
trained_models: TrainedModelInfo[];
failed_products?: string[];
failed_products?: string[]; // inventory_product_ids of failed products
}
export interface TrainedModelInfo {
product_name: string;
inventory_product_id: string;
product_name?: string; // Optional - for display, populated by frontend from inventory service
model_id: string;
model_type: string;
accuracy_metrics: TrainingMetrics;
@@ -107,7 +108,8 @@ export interface TrainingMetrics {
export interface ModelInfo {
model_id: string;
tenant_id: string;
product_name: string;
inventory_product_id: string;
product_name?: string; // Optional - for display, populated by frontend from inventory service
model_type: string;
model_path: string;
version: number;

View File

@@ -33,7 +33,8 @@ interface PurchaseOrderFormProps {
interface OrderItem {
ingredient_id: string;
product_code: string;
product_name: string;
inventory_product_id: string; // Reference to inventory service product
product_name: string; // For backward compatibility and display
ordered_quantity: number;
unit_of_measure: string;
unit_price: number;
@@ -80,6 +81,7 @@ const initialFormData: FormData = {
const initialOrderItem: OrderItem = {
ingredient_id: '',
product_code: '',
inventory_product_id: '',
product_name: '',
ordered_quantity: 0,
unit_of_measure: '',
@@ -123,7 +125,8 @@ const PurchaseOrderForm: React.FC<PurchaseOrderFormProps> = ({
items: order.items?.map(item => ({
ingredient_id: item.ingredient_id,
product_code: item.product_code || '',
product_name: item.product_name,
inventory_product_id: item.inventory_product_id,
product_name: item.product_name || '',
ordered_quantity: item.ordered_quantity,
unit_of_measure: item.unit_of_measure,
unit_price: item.unit_price,
@@ -193,6 +196,7 @@ const PurchaseOrderForm: React.FC<PurchaseOrderFormProps> = ({
const ingredient = ingredients.find(ing => ing.id === ingredientId);
if (ingredient) {
handleItemChange(index, 'ingredient_id', ingredientId);
handleItemChange(index, 'inventory_product_id', ingredient.id);
handleItemChange(index, 'product_name', ingredient.name);
handleItemChange(index, 'unit_of_measure', ingredient.unit_of_measure);
handleItemChange(index, 'product_code', ingredient.sku || '');
@@ -279,6 +283,7 @@ const PurchaseOrderForm: React.FC<PurchaseOrderFormProps> = ({
items: formData.items.map(item => ({
ingredient_id: item.ingredient_id,
product_code: item.product_code || undefined,
inventory_product_id: item.inventory_product_id,
product_name: item.product_name,
ordered_quantity: item.ordered_quantity,
unit_of_measure: item.unit_of_measure,

View File

@@ -115,7 +115,8 @@ export const useDashboard = () => {
const forecastPromises = products.map(async (product) => {
try {
const forecastRequest = {
product_name: product,
inventory_product_id: product, // Use product as inventory_product_id
product_name: product, // Keep for backward compatibility
forecast_date: new Date().toISOString().split('T')[0], // Today's date as YYYY-MM-DD
forecast_days: 1,
location: 'madrid_centro', // Default location for Madrid bakery

View File

@@ -101,7 +101,9 @@ export const useOrderSuggestions = () => {
for (const product of dailyProducts) {
// Find forecast for this product
const forecast = quickForecasts.find(f => f.product_name === product);
const forecast = quickForecasts.find(f =>
f.product_name === product || f.inventory_product_id === product
);
if (forecast) {
// Calculate suggested quantity based on prediction

View File

@@ -4,7 +4,8 @@ import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
export interface Forecast {
id: string;
tenant_id: string;
product_name: string;
inventory_product_id: string; // Reference to inventory service product
product_name?: string; // Optional - for display, populated by frontend from inventory service
location: string;
forecast_date: string;
created_at: string;
@@ -41,7 +42,8 @@ export interface ForecastAlert {
id: string;
tenant_id: string;
type: 'high_demand' | 'low_demand' | 'stockout_risk' | 'overproduction';
product_name: string;
inventory_product_id: string; // Reference to inventory service product
product_name?: string; // Optional - for display, populated by frontend from inventory service
message: string;
severity: 'low' | 'medium' | 'high';
created_at: string;
@@ -109,13 +111,15 @@ export const generateForecast = createAsyncThunk(
'forecast/generate',
async ({
tenantId,
productName,
inventoryProductId,
productName, // For backward compatibility
forecastDate,
forecastDays = 1,
location
}: {
tenantId: string;
productName: string;
inventoryProductId?: string;
productName?: string; // For backward compatibility
forecastDate: string;
forecastDays?: number;
location: string;
@@ -127,7 +131,7 @@ export const generateForecast = createAsyncThunk(
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
},
body: JSON.stringify({
product_name: productName,
inventory_product_id: inventoryProductId || productName, // Use inventoryProductId or fallback to productName for backward compatibility
forecast_date: forecastDate,
forecast_days: forecastDays,
location,
@@ -146,11 +150,13 @@ export const generateBatchForecast = createAsyncThunk(
'forecast/generateBatch',
async ({
tenantId,
products,
inventoryProductIds,
products, // For backward compatibility
forecastDays = 7
}: {
tenantId: string;
products: string[];
inventoryProductIds?: string[];
products?: string[]; // For backward compatibility
forecastDays?: number;
}) => {
const response = await fetch(`/api/v1/tenants/${tenantId}/forecasts/batch`, {
@@ -160,7 +166,7 @@ export const generateBatchForecast = createAsyncThunk(
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
},
body: JSON.stringify({
products,
inventory_product_ids: inventoryProductIds || products, // Use inventoryProductIds or fallback to products for backward compatibility
forecast_days: forecastDays,
batch_name: `Batch_${new Date().toISOString()}`,
}),
@@ -358,7 +364,7 @@ const forecastSlice = createSlice({
state.isLoading = false;
// Convert API forecasts to QuickForecast format
state.todayForecasts = (action.payload.forecasts || []).map((forecast: Forecast) => ({
product: forecast.product_name,
product: forecast.product_name || forecast.inventory_product_id, // Use product_name if available, otherwise use ID
predicted: Math.round(forecast.predicted_demand),
confidence: forecast.confidence_level > 0.8 ? 'high' :
forecast.confidence_level > 0.6 ? 'medium' : 'low',