Improve backend
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { Edit, Package, Calendar, Building2 } from 'lucide-react';
|
||||
import { AddModal } from '../../ui/AddModal/AddModal';
|
||||
import { useUpdatePurchaseOrder, usePurchaseOrder } from '../../../api/hooks/purchase-orders';
|
||||
import { useTenantStore } from '../../../stores/tenant.store';
|
||||
import type { PurchaseOrderItem } from '../../../api/types/orders';
|
||||
import { statusColors } from '../../../styles/colors';
|
||||
|
||||
interface ModifyPurchaseOrderModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
poId: string;
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* ModifyPurchaseOrderModal - Modal for modifying existing purchase orders
|
||||
* Allows editing of items, delivery dates, and notes for pending approval POs
|
||||
*/
|
||||
export const ModifyPurchaseOrderModal: React.FC<ModifyPurchaseOrderModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
poId,
|
||||
onSuccess
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [formData, setFormData] = useState<Record<string, any>>({});
|
||||
|
||||
// Get current tenant
|
||||
const { currentTenant } = useTenantStore();
|
||||
const tenantId = currentTenant?.id || '';
|
||||
|
||||
// Fetch the purchase order details
|
||||
const { data: purchaseOrder, isLoading: isLoadingPO } = usePurchaseOrder(
|
||||
tenantId,
|
||||
poId,
|
||||
{ enabled: !!tenantId && !!poId && isOpen }
|
||||
);
|
||||
|
||||
// Update purchase order mutation
|
||||
const updatePurchaseOrderMutation = useUpdatePurchaseOrder();
|
||||
|
||||
// Unit options for select field
|
||||
const unitOptions = [
|
||||
{ value: 'kg', label: 'Kilogramos' },
|
||||
{ value: 'g', label: 'Gramos' },
|
||||
{ value: 'l', label: 'Litros' },
|
||||
{ value: 'ml', label: 'Mililitros' },
|
||||
{ value: 'units', label: 'Unidades' },
|
||||
{ value: 'boxes', label: 'Cajas' },
|
||||
{ value: 'bags', label: 'Bolsas' }
|
||||
];
|
||||
|
||||
// Priority options
|
||||
const priorityOptions = [
|
||||
{ value: 'urgent', label: 'Urgente' },
|
||||
{ value: 'high', label: 'Alta' },
|
||||
{ value: 'normal', label: 'Normal' },
|
||||
{ value: 'low', label: 'Baja' }
|
||||
];
|
||||
|
||||
// Reset form when modal closes
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
setFormData({});
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleSave = async (formData: Record<string, any>) => {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const items = formData.items || [];
|
||||
|
||||
if (items.length === 0) {
|
||||
throw new Error('Por favor, agrega al menos un producto');
|
||||
}
|
||||
|
||||
// Validate quantities
|
||||
const invalidQuantities = items.some((item: any) => item.ordered_quantity <= 0);
|
||||
if (invalidQuantities) {
|
||||
throw new Error('Todas las cantidades deben ser mayores a 0');
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
const invalidProducts = items.some((item: any) => !item.product_name);
|
||||
if (invalidProducts) {
|
||||
throw new Error('Todos los productos deben tener un nombre');
|
||||
}
|
||||
|
||||
// Prepare the update data
|
||||
const updateData: any = {
|
||||
notes: formData.notes || undefined,
|
||||
priority: formData.priority || undefined,
|
||||
};
|
||||
|
||||
// Add delivery date if changed
|
||||
if (formData.required_delivery_date) {
|
||||
updateData.required_delivery_date = formData.required_delivery_date;
|
||||
}
|
||||
|
||||
// Update purchase order
|
||||
await updatePurchaseOrderMutation.mutateAsync({
|
||||
tenantId,
|
||||
poId,
|
||||
data: updateData
|
||||
});
|
||||
|
||||
// Trigger success callback
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error modifying purchase order:', error);
|
||||
throw error; // Let AddModal handle error display
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const statusConfig = {
|
||||
color: statusColors.pending.primary,
|
||||
text: 'Modificando Orden',
|
||||
icon: Edit,
|
||||
isCritical: false,
|
||||
isHighlight: true
|
||||
};
|
||||
|
||||
// Build sections dynamically based on purchase order data
|
||||
const sections = useMemo(() => {
|
||||
if (!purchaseOrder) return [];
|
||||
|
||||
const supplierSection = {
|
||||
title: 'Información del Proveedor',
|
||||
icon: Building2,
|
||||
fields: [
|
||||
{
|
||||
label: 'Proveedor',
|
||||
name: 'supplier_name',
|
||||
type: 'text' as const,
|
||||
required: false,
|
||||
defaultValue: purchaseOrder.supplier_name || '',
|
||||
span: 2,
|
||||
disabled: true,
|
||||
helpText: 'El proveedor no puede ser modificado'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const orderDetailsSection = {
|
||||
title: 'Detalles de la Orden',
|
||||
icon: Calendar,
|
||||
fields: [
|
||||
{
|
||||
label: 'Prioridad',
|
||||
name: 'priority',
|
||||
type: 'select' as const,
|
||||
options: priorityOptions,
|
||||
defaultValue: purchaseOrder.priority || 'normal',
|
||||
helpText: 'Ajusta la prioridad de esta orden'
|
||||
},
|
||||
{
|
||||
label: 'Fecha de Entrega Requerida',
|
||||
name: 'required_delivery_date',
|
||||
type: 'date' as const,
|
||||
defaultValue: purchaseOrder.required_delivery_date || '',
|
||||
helpText: 'Fecha límite para la entrega'
|
||||
},
|
||||
{
|
||||
label: 'Notas',
|
||||
name: 'notes',
|
||||
type: 'textarea' as const,
|
||||
placeholder: 'Instrucciones especiales para el proveedor...',
|
||||
span: 2,
|
||||
defaultValue: purchaseOrder.notes || '',
|
||||
helpText: 'Información adicional o instrucciones especiales'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const itemsSection = {
|
||||
title: 'Productos de la Orden',
|
||||
icon: Package,
|
||||
fields: [
|
||||
{
|
||||
label: 'Productos',
|
||||
name: 'items',
|
||||
type: 'list' as const,
|
||||
span: 2,
|
||||
defaultValue: (purchaseOrder.items || []).map((item: PurchaseOrderItem) => ({
|
||||
id: item.id,
|
||||
inventory_product_id: item.inventory_product_id,
|
||||
product_code: item.product_code || '',
|
||||
product_name: item.product_name || '',
|
||||
ordered_quantity: item.ordered_quantity,
|
||||
unit_of_measure: item.unit_of_measure,
|
||||
unit_price: parseFloat(item.unit_price),
|
||||
})),
|
||||
listConfig: {
|
||||
itemFields: [
|
||||
{
|
||||
name: 'product_name',
|
||||
label: 'Producto',
|
||||
type: 'text',
|
||||
required: true,
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
name: 'product_code',
|
||||
label: 'SKU',
|
||||
type: 'text',
|
||||
required: false,
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
name: 'ordered_quantity',
|
||||
label: 'Cantidad',
|
||||
type: 'number',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'unit_of_measure',
|
||||
label: 'Unidad',
|
||||
type: 'select',
|
||||
required: true,
|
||||
options: unitOptions
|
||||
},
|
||||
{
|
||||
name: 'unit_price',
|
||||
label: 'Precio Unitario (€)',
|
||||
type: 'currency',
|
||||
required: true,
|
||||
placeholder: '0.00'
|
||||
}
|
||||
],
|
||||
addButtonLabel: 'Agregar Producto',
|
||||
emptyStateText: 'No hay productos en esta orden',
|
||||
showSubtotals: true,
|
||||
subtotalFields: { quantity: 'ordered_quantity', price: 'unit_price' },
|
||||
disabled: false
|
||||
},
|
||||
helpText: 'Modifica las cantidades, unidades y precios según sea necesario'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
return [supplierSection, orderDetailsSection, itemsSection];
|
||||
}, [purchaseOrder, priorityOptions, unitOptions]);
|
||||
|
||||
return (
|
||||
<AddModal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
title="Modificar Orden de Compra"
|
||||
subtitle={`Orden ${purchaseOrder?.po_number || ''}`}
|
||||
statusIndicator={statusConfig}
|
||||
sections={sections}
|
||||
size="xl"
|
||||
loading={loading || isLoadingPO}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModifyPurchaseOrderModal;
|
||||
Reference in New Issue
Block a user