# 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