Files
bakery-ia/frontend/src/hooks/business/onboarding/useInventorySetup.ts
2025-09-06 20:01:16 +02:00

338 lines
10 KiB
TypeScript

/**
* Inventory setup hook for creating inventory from suggestions
*/
import { useState, useCallback } from 'react';
import {
useCreateIngredient,
useCreateSalesRecord,
} from '../../../api';
import { useCurrentTenant } from '../../../stores';
import type { ProductSuggestionResponse } from './types';
interface InventorySetupState {
isLoading: boolean;
error: string | null;
createdItems: any[];
inventoryMapping: { [productName: string]: string };
salesImportResult: {
success: boolean;
imported: boolean;
records_created: number;
message: string;
} | null;
isInventoryConfigured: boolean;
}
interface InventorySetupActions {
createInventoryFromSuggestions: (
suggestions: ProductSuggestionResponse[]
) => Promise<{
success: boolean;
createdItems?: any[];
inventoryMapping?: { [productName: string]: string };
}>;
importSalesData: (
salesData: any,
inventoryMapping: { [productName: string]: string }
) => Promise<{
success: boolean;
recordsCreated: number;
message: string;
}>;
clearError: () => void;
reset: () => void;
}
export const useInventorySetup = () => {
const [state, setState] = useState<InventorySetupState>({
isLoading: false,
error: null,
createdItems: [],
inventoryMapping: {},
salesImportResult: null,
isInventoryConfigured: false,
});
const createIngredientMutation = useCreateIngredient();
const createSalesRecordMutation = useCreateSalesRecord();
const currentTenant = useCurrentTenant();
const createInventoryFromSuggestions = useCallback(async (
suggestions: ProductSuggestionResponse[]
): Promise<{
success: boolean;
createdItems?: any[];
inventoryMapping?: { [productName: string]: string };
}> => {
console.log('useInventorySetup - createInventoryFromSuggestions called with:', {
suggestionsCount: suggestions?.length,
suggestions: suggestions?.slice(0, 3), // Log first 3 for debugging
tenantId: currentTenant?.id
});
if (!suggestions || suggestions.length === 0) {
console.error('useInventorySetup - No suggestions provided');
setState(prev => ({
...prev,
error: 'No hay sugerencias para crear el inventario',
}));
return { success: false };
}
if (!currentTenant?.id) {
console.error('useInventorySetup - No tenant ID available');
setState(prev => ({
...prev,
error: 'No se pudo obtener información del tenant',
}));
return { success: false };
}
setState(prev => ({
...prev,
isLoading: true,
error: null
}));
try {
const createdItems = [];
const inventoryMapping: { [key: string]: string } = {};
console.log('useInventorySetup - Creating ingredients from suggestions...');
// Create ingredients from approved suggestions
for (const suggestion of suggestions) {
try {
const ingredientData = {
name: suggestion.suggested_name || suggestion.original_name,
category: suggestion.category || 'Sin categoría',
description: suggestion.notes || '',
unit_of_measure: suggestion.unit_of_measure || 'units',
low_stock_threshold: 10, // Default low stock threshold
reorder_point: 15, // Default reorder point (must be > low_stock_threshold)
reorder_quantity: 50, // Default reorder quantity
shelf_life_days: suggestion.estimated_shelf_life_days || 30,
requires_refrigeration: suggestion.requires_refrigeration || false,
requires_freezing: suggestion.requires_freezing || false,
is_seasonal: suggestion.is_seasonal || false,
cost_per_unit: 0, // Will be set by user later
notes: suggestion.notes || `Producto clasificado automáticamente desde: ${suggestion.original_name}`,
};
console.log('useInventorySetup - Creating ingredient:', {
name: ingredientData.name,
category: ingredientData.category,
original_name: suggestion.original_name,
ingredientData: ingredientData,
tenantId: currentTenant.id,
apiUrl: `/tenants/${currentTenant.id}/ingredients`
});
const createdItem = await createIngredientMutation.mutateAsync({
tenantId: currentTenant.id,
ingredientData,
});
console.log('useInventorySetup - Created ingredient successfully:', {
id: createdItem.id,
name: createdItem.name
});
createdItems.push(createdItem);
// Map both original and suggested names to the same ingredient ID for flexibility
inventoryMapping[suggestion.original_name] = createdItem.id;
if (suggestion.suggested_name && suggestion.suggested_name !== suggestion.original_name) {
inventoryMapping[suggestion.suggested_name] = createdItem.id;
}
} catch (error) {
console.error(`Error creating ingredient ${suggestion.suggested_name || suggestion.original_name}:`, error);
// Continue with other ingredients even if one fails
}
}
if (createdItems.length === 0) {
throw new Error('No se pudo crear ningún elemento del inventario');
}
console.log('useInventorySetup - Successfully created ingredients:', {
createdCount: createdItems.length,
totalSuggestions: suggestions.length,
inventoryMapping
});
setState(prev => ({
...prev,
isLoading: false,
createdItems,
inventoryMapping,
isInventoryConfigured: true,
}));
return {
success: true,
createdItems,
inventoryMapping,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Error creando el inventario';
console.error('useInventorySetup - Error in createInventoryFromSuggestions:', error);
setState(prev => ({
...prev,
isLoading: false,
error: errorMessage
}));
return { success: false };
}
}, [createIngredientMutation, currentTenant]);
const importSalesData = useCallback(async (
salesData: any,
inventoryMapping: { [productName: string]: string }
): Promise<{
success: boolean;
recordsCreated: number;
message: string;
}> => {
if (!currentTenant?.id) {
setState(prev => ({
...prev,
error: 'No se pudo obtener información del tenant',
}));
return {
success: false,
recordsCreated: 0,
message: 'Error: No se pudo obtener información del tenant',
};
}
if (!salesData || !salesData.product_list) {
setState(prev => ({
...prev,
error: 'No hay datos de ventas para importar',
}));
return {
success: false,
recordsCreated: 0,
message: 'Error: No hay datos de ventas para importar',
};
}
setState(prev => ({
...prev,
isLoading: true,
error: null
}));
try {
let recordsCreated = 0;
// Process actual sales data and create sales records
if (salesData.raw_data && Array.isArray(salesData.raw_data)) {
for (const salesRecord of salesData.raw_data) {
try {
// Map product name to inventory product ID
const inventoryProductId = inventoryMapping[salesRecord.product_name];
if (inventoryProductId) {
const salesRecordData = {
date: salesRecord.date,
product_name: salesRecord.product_name,
inventory_product_id: inventoryProductId,
quantity_sold: salesRecord.quantity,
unit_price: salesRecord.unit_price,
total_revenue: salesRecord.total_amount || (salesRecord.quantity * salesRecord.unit_price),
channel: salesRecord.channel || 'tienda',
customer_info: salesRecord.customer_info || {},
notes: salesRecord.notes || '',
};
await createSalesRecordMutation.mutateAsync({
tenantId: currentTenant.id,
salesData: salesRecordData,
});
recordsCreated++;
}
} catch (error) {
console.error('Error creating sales record:', error);
// Continue with next record
}
}
}
const result = {
success: recordsCreated > 0,
imported: recordsCreated > 0,
records_created: recordsCreated,
message: recordsCreated > 0
? `Se importaron ${recordsCreated} registros de ventas exitosamente`
: 'No se pudieron importar registros de ventas',
};
setState(prev => ({
...prev,
isLoading: false,
salesImportResult: result,
}));
return {
success: result.success,
recordsCreated,
message: result.message,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Error importando datos de ventas';
const result = {
success: false,
imported: false,
records_created: 0,
message: errorMessage,
};
setState(prev => ({
...prev,
isLoading: false,
error: errorMessage,
salesImportResult: result,
}));
return {
success: false,
recordsCreated: 0,
message: errorMessage,
};
}
}, [createSalesRecordMutation, currentTenant]);
const clearError = useCallback(() => {
setState(prev => ({ ...prev, error: null }));
}, []);
const reset = useCallback(() => {
setState({
isLoading: false,
error: null,
createdItems: [],
inventoryMapping: {},
salesImportResult: null,
isInventoryConfigured: false,
});
}, []);
return {
// State
isLoading: state.isLoading,
error: state.error,
createdItems: state.createdItems,
inventoryMapping: state.inventoryMapping,
salesImportResult: state.salesImportResult,
isInventoryConfigured: state.isInventoryConfigured,
// Actions
createInventoryFromSuggestions,
importSalesData,
clearError,
reset,
} satisfies InventorySetupState & InventorySetupActions;
};