338 lines
10 KiB
TypeScript
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;
|
|
}; |