300 lines
8.3 KiB
TypeScript
300 lines
8.3 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 };
|
||
|
|
}> => {
|
||
|
|
if (!suggestions || suggestions.length === 0) {
|
||
|
|
setState(prev => ({
|
||
|
|
...prev,
|
||
|
|
error: 'No hay sugerencias para crear el inventario',
|
||
|
|
}));
|
||
|
|
return { success: false };
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!currentTenant?.id) {
|
||
|
|
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 } = {};
|
||
|
|
|
||
|
|
// Create ingredients from approved suggestions
|
||
|
|
for (const suggestion of suggestions) {
|
||
|
|
try {
|
||
|
|
const ingredientData = {
|
||
|
|
name: suggestion.name,
|
||
|
|
category: suggestion.category || 'Sin categoría',
|
||
|
|
description: suggestion.description || '',
|
||
|
|
unit_of_measure: suggestion.unit_of_measure || 'unidad',
|
||
|
|
cost_per_unit: suggestion.cost_per_unit || 0,
|
||
|
|
supplier_info: suggestion.supplier_info || {},
|
||
|
|
nutritional_info: suggestion.nutritional_info || {},
|
||
|
|
storage_requirements: suggestion.storage_requirements || {},
|
||
|
|
allergen_info: suggestion.allergen_info || {},
|
||
|
|
is_active: true,
|
||
|
|
};
|
||
|
|
|
||
|
|
const createdItem = await createIngredientMutation.mutateAsync({
|
||
|
|
tenantId: currentTenant.id,
|
||
|
|
ingredientData,
|
||
|
|
});
|
||
|
|
|
||
|
|
createdItems.push(createdItem);
|
||
|
|
inventoryMapping[suggestion.name] = createdItem.id;
|
||
|
|
} catch (error) {
|
||
|
|
console.error(`Error creating ingredient ${suggestion.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');
|
||
|
|
}
|
||
|
|
|
||
|
|
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';
|
||
|
|
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;
|
||
|
|
};
|