Add POI feature and imporve the overall backend implementation

This commit is contained in:
Urtzi Alfaro
2025-11-12 15:34:10 +01:00
parent e8096cd979
commit 5783c7ed05
173 changed files with 16862 additions and 9078 deletions

View File

@@ -17,9 +17,9 @@ import type { ItemType } from '../../../../components/domain/unified-wizard';
// Import AddStockModal separately since we need it for adding batches
import AddStockModal from '../../../../components/domain/inventory/AddStockModal';
import { useIngredients, useStockAnalytics, useStockMovements, useStockByIngredient, useCreateIngredient, useSoftDeleteIngredient, useHardDeleteIngredient, useAddStock, useConsumeStock, useUpdateIngredient, useUpdateStock, useTransformationsByIngredient } from '../../../../api/hooks/inventory';
import { useIngredients, useStockAnalytics, useStockMovements, useStockByIngredient, useCreateIngredient, useSoftDeleteIngredient, useHardDeleteIngredient, useAddStock, useConsumeStock, useUpdateIngredient, useUpdateStock, useCreateStockMovement, useTransformationsByIngredient } from '../../../../api/hooks/inventory';
import { useTenantId } from '../../../../hooks/useTenantId';
import { IngredientResponse, StockCreate, StockMovementCreate, IngredientCreate } from '../../../../api/types/inventory';
import { IngredientResponse, StockCreate, StockMovementCreate, StockMovementType, IngredientCreate } from '../../../../api/types/inventory';
import { subscriptionService } from '../../../../api/services/subscription';
import { useQueryClient } from '@tanstack/react-query';
@@ -55,6 +55,7 @@ const InventoryPage: React.FC = () => {
const consumeStockMutation = useConsumeStock();
const updateIngredientMutation = useUpdateIngredient();
const updateStockMutation = useUpdateStock();
const createStockMovementMutation = useCreateStockMovement();
// API Data
const {
@@ -795,8 +796,36 @@ const InventoryPage: React.FC = () => {
});
}}
onMarkAsWaste={async (batchId) => {
// TODO: Implement mark as waste functionality
console.log('Mark as waste:', batchId);
if (!tenantId || !batches) return;
try {
// Find the batch to get its details
const batch = batches.find(b => b.id === batchId);
if (!batch) {
console.error('Batch not found:', batchId);
return;
}
// Create a waste movement for the entire current quantity
const movementData: StockMovementCreate = {
ingredient_id: selectedItem!.id,
stock_id: batchId,
movement_type: StockMovementType.WASTE,
quantity: batch.current_quantity,
unit_cost: batch.unit_cost || undefined,
notes: `Batch marked as waste. Reason: ${batch.is_expired ? 'Expired' : 'Damaged/Spoiled'}`
};
await createStockMovementMutation.mutateAsync({
tenantId,
movementData
});
// Refresh the batches list
queryClient.invalidateQueries({ queryKey: ['stock', 'byIngredient', tenantId, selectedItem!.id] });
} catch (error) {
console.error('Failed to mark batch as waste:', error);
}
}}
waitForRefetch={true}
isRefetching={isRefetchingBatches}

View File

@@ -21,7 +21,7 @@ import {
CustomerType,
CustomerSegment
} from '../../../../api/types/orders';
import { useOrders, useCustomers, useOrdersDashboard, useCreateOrder, useCreateCustomer, useUpdateCustomer } from '../../../../api/hooks/orders';
import { useOrders, useCustomers, useOrdersDashboard, useCreateOrder, useUpdateOrder, useCreateCustomer, useUpdateCustomer } from '../../../../api/hooks/orders';
import { useCurrentTenant } from '../../../../stores/tenant.store';
import { useAuthUser } from '../../../../stores/auth.store';
import { OrderFormModal } from '../../../../components/domain/orders';
@@ -76,6 +76,7 @@ const OrdersPage: React.FC = () => {
// Mutations
const createOrderMutation = useCreateOrder();
const updateOrderMutation = useUpdateOrder();
const createCustomerMutation = useCreateCustomer();
const updateCustomerMutation = useUpdateCustomer();
@@ -634,11 +635,36 @@ const OrdersPage: React.FC = () => {
sections={sections}
showDefaultActions={true}
onSave={async () => {
// TODO: Implement order update functionality
// Note: The backend only has updateOrderStatus, not a general update endpoint
// For now, orders can be updated via status changes using useUpdateOrderStatus
console.log('Saving order:', selectedOrder);
console.warn('Order update not yet implemented - only status updates are supported via useUpdateOrderStatus');
if (!selectedOrder || !tenantId) return;
try {
// Build the update payload from the selectedOrder state
const updateData = {
status: selectedOrder.status,
priority: selectedOrder.priority,
requested_delivery_date: selectedOrder.requested_delivery_date,
delivery_method: selectedOrder.delivery_method,
delivery_address: selectedOrder.delivery_address,
delivery_instructions: selectedOrder.delivery_instructions,
delivery_window_start: selectedOrder.delivery_window_start,
delivery_window_end: selectedOrder.delivery_window_end,
payment_method: selectedOrder.payment_method,
payment_status: selectedOrder.payment_status,
special_instructions: selectedOrder.special_instructions,
custom_requirements: selectedOrder.custom_requirements,
allergen_warnings: selectedOrder.allergen_warnings,
};
await updateOrderMutation.mutateAsync({
tenantId: tenantId,
orderId: selectedOrder.id,
data: updateData
});
setShowForm(false);
} catch (error) {
console.error('Failed to update order:', error);
}
}}
onFieldChange={(sectionIndex, fieldIndex, value) => {
const newOrder = { ...selectedOrder };

View File

@@ -11,6 +11,7 @@ import { showToast } from '../../../../utils/toast';
import { usePOSConfigurationData, usePOSConfigurationManager, usePOSTransactions, usePOSTransactionsDashboard, usePOSTransaction } from '../../../../api/hooks/pos';
import { POSConfiguration } from '../../../../api/types/pos';
import { posService } from '../../../../api/services/pos';
import { salesService } from '../../../../api/services/sales';
import { bakeryColors } from '../../../../styles/colors';
// Import new POS components
@@ -18,6 +19,7 @@ import { POSProductCard } from '../../../../components/domain/pos/POSProductCard
import { POSCart } from '../../../../components/domain/pos/POSCart';
import { POSPayment } from '../../../../components/domain/pos/POSPayment';
import { CreatePOSConfigModal } from '../../../../components/domain/pos/CreatePOSConfigModal';
import { POSSyncStatus } from '../../../../components/domain/pos/POSSyncStatus';
interface CartItem {
id: string;
@@ -752,17 +754,37 @@ const POSPage: React.FC = () => {
const tax = subtotal * taxRate;
const total = subtotal + tax;
const processPayment = (paymentData: any) => {
const processPayment = async (paymentData: any) => {
if (cart.length === 0) return;
console.log('Processing payment:', {
cart,
...paymentData,
total,
});
try {
// Create sales records for each item in the cart
const saleDate = new Date().toISOString().split('T')[0];
setCart([]);
showToast.success('Venta procesada exitosamente');
for (const item of cart) {
const salesData = {
inventory_product_id: item.id, // Product ID for inventory tracking
product_name: item.name,
product_category: 'finished_product',
quantity_sold: item.quantity,
unit_price: item.price,
total_amount: item.price * item.quantity,
sale_date: saleDate,
sales_channel: 'pos',
source: 'manual_pos',
payment_method: paymentData.method || 'cash',
notes: paymentData.notes || 'Venta desde POS manual',
};
await salesService.createSalesRecord(tenantId, salesData);
}
setCart([]);
showToast.success(`Venta procesada exitosamente: €${total.toFixed(2)}`);
} catch (error: any) {
console.error('Error processing payment:', error);
showToast.error(error.response?.data?.detail || 'Error al procesar la venta');
}
};
// Loading and error states
@@ -1030,6 +1052,11 @@ const POSPage: React.FC = () => {
{posData.configurations.length > 0 && (
<TransactionsSection tenantId={tenantId} />
)}
{/* POS to Sales Sync Status - Only show if there are configurations */}
{posData.configurations.length > 0 && (
<POSSyncStatus tenantId={tenantId} />
)}
</div>
)}

View File

@@ -135,78 +135,19 @@ const ProductionPage: React.FC = () => {
// The QualityCheckModal should be enhanced to handle stage-specific checks
};
// Helper function to generate mock process stage data for the selected batch
const generateMockProcessStageData = (batch: ProductionBatchResponse) => {
// Mock data based on batch status - this would come from the API in real implementation
const mockProcessStage = {
current: batch.status === ProductionStatusEnum.PENDING ? 'mixing' as const :
batch.status === ProductionStatusEnum.IN_PROGRESS ? 'baking' as const :
batch.status === ProductionStatusEnum.QUALITY_CHECK ? 'cooling' as const :
'finishing' as const,
history: batch.status !== ProductionStatusEnum.PENDING ? [
{ stage: 'mixing' as const, timestamp: batch.actual_start_time || batch.planned_start_time, duration: 30 },
...(batch.status === ProductionStatusEnum.IN_PROGRESS || batch.status === ProductionStatusEnum.QUALITY_CHECK || batch.status === ProductionStatusEnum.COMPLETED ? [
{ stage: 'proofing' as const, timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(), duration: 90 },
{ stage: 'shaping' as const, timestamp: new Date(Date.now() - 1 * 60 * 60 * 1000).toISOString(), duration: 15 }
] : []),
...(batch.status === ProductionStatusEnum.QUALITY_CHECK || batch.status === ProductionStatusEnum.COMPLETED ? [
{ stage: 'baking' as const, timestamp: new Date(Date.now() - 30 * 60 * 1000).toISOString(), duration: 45 }
] : [])
] : [],
pendingQualityChecks: batch.status === ProductionStatusEnum.IN_PROGRESS ? [
{
id: 'qc1',
name: 'Control de temperatura interna',
stage: 'baking' as const,
isRequired: true,
isCritical: true,
status: 'pending' as const,
checkType: 'temperature' as const
}
] : batch.status === ProductionStatusEnum.QUALITY_CHECK ? [
{
id: 'qc2',
name: 'Inspección visual final',
stage: 'cooling' as const,
isRequired: true,
isCritical: false,
status: 'pending' as const,
checkType: 'visual' as const
}
] : [],
completedQualityChecks: batch.status === ProductionStatusEnum.COMPLETED ? [
{
id: 'qc1',
name: 'Control de temperatura interna',
stage: 'baking' as const,
isRequired: true,
isCritical: true,
status: 'completed' as const,
checkType: 'temperature' as const
},
{
id: 'qc2',
name: 'Inspección visual final',
stage: 'cooling' as const,
isRequired: true,
isCritical: false,
status: 'completed' as const,
checkType: 'visual' as const
}
] : batch.status === ProductionStatusEnum.IN_PROGRESS ? [
{
id: 'qc3',
name: 'Verificación de masa',
stage: 'mixing' as const,
isRequired: true,
isCritical: false,
status: 'completed' as const,
checkType: 'visual' as const
}
] : []
// Helper function to get process stage data from the batch (now from real backend data)
const getProcessStageData = (batch: ProductionBatchResponse) => {
// Backend now provides these fields in the API response:
// - current_process_stage
// - process_stage_history
// - pending_quality_checks
// - completed_quality_checks
return {
current: batch.current_process_stage || 'mixing',
history: batch.process_stage_history || [],
pendingQualityChecks: batch.pending_quality_checks || [],
completedQualityChecks: batch.completed_quality_checks || []
};
return mockProcessStage;
};
@@ -576,7 +517,7 @@ const ProductionPage: React.FC = () => {
label: '',
value: (
<CompactProcessStageTracker
processStage={generateMockProcessStageData(selectedBatch)}
processStage={getProcessStageData(selectedBatch)}
onAdvanceStage={(currentStage) => handleStageAdvance(selectedBatch.id, currentStage)}
onQualityCheck={(checkId) => {
setShowQualityModal(true);