From 1243c2ca6dbe1aaa8ac12e712eb469fb55dfb0ec Mon Sep 17 00:00:00 2001 From: Urtzi Alfaro Date: Thu, 2 Oct 2025 13:20:30 +0200 Subject: [PATCH] Add fixes to procurement logic and fix rel-time connections --- FRONTEND_INTEGRATION_GUIDE.md | 748 +++++++++++ PROCUREMENT_IMPLEMENTATION_SUMMARY.md | 591 ++++++++ SSE_IMPLEMENTATION_COMPLETE.md | 363 +++++ SSE_SECURITY_MITIGATIONS.md | 291 ++++ frontend/src/api/hooks/orders.ts | 186 +++ frontend/src/api/index.ts | 6 + frontend/src/api/services/orders.ts | 81 ++ frontend/src/api/types/orders.ts | 49 + .../dashboard/PurchaseOrdersTracking.tsx | 210 +++ .../src/components/domain/dashboard/index.ts | 1 + frontend/src/contexts/SSEContext.tsx | 120 +- frontend/src/pages/app/DashboardPage.tsx | 8 +- .../analytics/ProcurementAnalyticsPage.tsx | 472 +++++++ .../procurement/ProcurementPage.tsx | 416 +++++- frontend/src/router/AppRouter.tsx | 11 + frontend/src/router/routes.config.ts | 14 +- gateway/app/main.py | 54 +- gateway/app/middleware/auth.py | 21 +- .../kubernetes/base/ingress-https.yaml | 21 +- .../kubernetes/overlays/dev/dev-ingress.yaml | 15 +- services/orders/app/api/procurement.py | 308 ++++- .../repositories/procurement_repository.py | 47 +- .../services/procurement_scheduler_service.py | 114 +- .../app/services/procurement_service.py | 1185 +++++++++++++---- 24 files changed, 4984 insertions(+), 348 deletions(-) create mode 100644 FRONTEND_INTEGRATION_GUIDE.md create mode 100644 PROCUREMENT_IMPLEMENTATION_SUMMARY.md create mode 100644 SSE_IMPLEMENTATION_COMPLETE.md create mode 100644 SSE_SECURITY_MITIGATIONS.md create mode 100644 frontend/src/components/domain/dashboard/PurchaseOrdersTracking.tsx create mode 100644 frontend/src/pages/app/analytics/ProcurementAnalyticsPage.tsx diff --git a/FRONTEND_INTEGRATION_GUIDE.md b/FRONTEND_INTEGRATION_GUIDE.md new file mode 100644 index 00000000..659f1c9a --- /dev/null +++ b/FRONTEND_INTEGRATION_GUIDE.md @@ -0,0 +1,748 @@ +# FRONTEND INTEGRATION GUIDE - Procurement Features + +## ✅ COMPLETED FRONTEND CHANGES + +All TypeScript types, API service methods, and React hooks have been implemented. This guide shows how to use them in your components. + +--- + +## 📦 WHAT'S BEEN ADDED + +### 1. **New Types** (`frontend/src/api/types/orders.ts`) + +```typescript +// Approval workflow tracking +export interface ApprovalWorkflowEntry { + timestamp: string; + from_status: string; + to_status: string; + user_id?: string; + notes?: string; +} + +// Purchase order creation result +export interface CreatePOsResult { + success: boolean; + created_pos: Array<{ + po_id: string; + po_number: string; + supplier_id: string; + items_count: number; + total_amount: number; + }>; + failed_pos: Array<{ + supplier_id: string; + error: string; + }>; + total_created: number; + total_failed: number; +} + +// Request types +export interface LinkRequirementToPORequest { + purchase_order_id: string; + purchase_order_number: string; + ordered_quantity: number; + expected_delivery_date?: string; +} + +export interface UpdateDeliveryStatusRequest { + delivery_status: string; + received_quantity?: number; + actual_delivery_date?: string; + quality_rating?: number; +} + +export interface ApprovalRequest { + approval_notes?: string; +} + +export interface RejectionRequest { + rejection_notes?: string; +} +``` + +**Updated ProcurementPlanResponse:** +- Added `approval_workflow?: ApprovalWorkflowEntry[]` - tracks all approval actions + +--- + +### 2. **New API Methods** (`frontend/src/api/services/orders.ts`) + +```typescript +class OrdersService { + // Recalculate plan with current inventory + static async recalculateProcurementPlan(tenantId: string, planId: string): Promise + + // Approve plan with notes + static async approveProcurementPlan(tenantId: string, planId: string, request?: ApprovalRequest): Promise + + // Reject plan with notes + static async rejectProcurementPlan(tenantId: string, planId: string, request?: RejectionRequest): Promise + + // Auto-create POs from plan + static async createPurchaseOrdersFromPlan(tenantId: string, planId: string, autoApprove?: boolean): Promise + + // Link requirement to PO + static async linkRequirementToPurchaseOrder(tenantId: string, requirementId: string, request: LinkRequirementToPORequest): Promise<{...}> + + // Update delivery status + static async updateRequirementDeliveryStatus(tenantId: string, requirementId: string, request: UpdateDeliveryStatusRequest): Promise<{...}> +} +``` + +--- + +### 3. **New React Hooks** (`frontend/src/api/hooks/orders.ts`) + +```typescript +// Recalculate plan +useRecalculateProcurementPlan(options?) + +// Approve plan +useApproveProcurementPlan(options?) + +// Reject plan +useRejectProcurementPlan(options?) + +// Create POs from plan +useCreatePurchaseOrdersFromPlan(options?) + +// Link requirement to PO +useLinkRequirementToPurchaseOrder(options?) + +// Update delivery status +useUpdateRequirementDeliveryStatus(options?) +``` + +--- + +## 🎨 HOW TO USE IN COMPONENTS + +### Example 1: Recalculate Plan Button + +```typescript +import { useRecalculateProcurementPlan } from '@/api/hooks/orders'; +import { useToast } from '@/hooks/useToast'; + +function ProcurementPlanActions({ plan, tenantId }) { + const { toast } = useToast(); + const recalculateMutation = useRecalculateProcurementPlan({ + onSuccess: (data) => { + if (data.success && data.plan) { + toast({ + title: 'Plan recalculado', + description: `Plan actualizado con ${data.plan.total_requirements} requerimientos`, + variant: 'success', + }); + } + }, + onError: (error) => { + toast({ + title: 'Error al recalcular', + description: error.message, + variant: 'destructive', + }); + }, + }); + + const handleRecalculate = () => { + if (confirm('¿Recalcular el plan con el inventario actual?')) { + recalculateMutation.mutate({ tenantId, planId: plan.id }); + } + }; + + // Show warning if plan is old + const planAgeHours = (new Date().getTime() - new Date(plan.created_at).getTime()) / (1000 * 60 * 60); + const isStale = planAgeHours > 24; + + return ( +
+ {isStale && ( + + + Plan desactualizado + + Este plan tiene más de 24 horas. El inventario puede haber cambiado. + + + )} + + +
+ ); +} +``` + +--- + +### Example 2: Approve/Reject Plan with Notes + +```typescript +import { useApproveProcurementPlan, useRejectProcurementPlan } from '@/api/hooks/orders'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { Textarea } from '@/components/ui/textarea'; + +function ApprovalDialog({ plan, tenantId, open, onClose }) { + const [notes, setNotes] = useState(''); + const [action, setAction] = useState<'approve' | 'reject'>('approve'); + + const approveMutation = useApproveProcurementPlan({ + onSuccess: () => { + toast({ title: 'Plan aprobado', variant: 'success' }); + onClose(); + }, + }); + + const rejectMutation = useRejectProcurementPlan({ + onSuccess: () => { + toast({ title: 'Plan rechazado', variant: 'success' }); + onClose(); + }, + }); + + const handleSubmit = () => { + if (action === 'approve') { + approveMutation.mutate({ + tenantId, + planId: plan.id, + approval_notes: notes || undefined, + }); + } else { + rejectMutation.mutate({ + tenantId, + planId: plan.id, + rejection_notes: notes || undefined, + }); + } + }; + + return ( + + + + + {action === 'approve' ? 'Aprobar' : 'Rechazar'} Plan de Compras + + + +
+
+ +