feat(ops,fin,crm): frontend de las reglas del PDF (decisiones, cierre, PDF factura, continuidad)

- Embarque: "Cerrar operación" (costos finales, habilita facturar), "Reprogramar
  salida" (Cut Off), y bitácora con puntos de decisión (autorizar/rechazar → hito
  de corrección) + transporte terrestre/recolección en Datos.
- Factura: "Enviar (PDF)" que genera y guarda el PDF, "Ver PDF", y revisión del
  cliente (en revisión / aprueba / con observaciones); estado en_revision_cliente.
- Solicitud: "Registrar contacto" y "Re-cotizar"; Cotización: "Re-cotizar (clonar)";
  Oportunidad: "Convertir a solicitud" (enlaza embudo→RFQ).
- Clientes API ampliados (ops/fin/crm) + catálogos (incoterms/participantes) +
  etiquetas de estado. svelte-check: 0 errores de tipo en archivos nuevos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aduanasoft
2026-07-15 09:08:07 -06:00
parent 3ea8d5f3ef
commit e806512a89
9 changed files with 317 additions and 20 deletions

View File

@@ -21,14 +21,21 @@ export interface Shipment {
status: ShipmentStatus;
booking_number: string | null;
carrier_supplier_id: number | null;
ground_carrier_supplier_id: number | null;
customs_agent_id: number | null;
destination_agent_id: number | null;
cutoff_date: string | null;
pickup_at: string | null;
etd: string | null;
previous_etd: string | null;
eta: string | null;
vessel_flight: string | null;
container_number: string | null;
notes: string | null;
actual_cost_total: number | null;
cost_currency: string | null;
closed_at: string | null;
closed_by: string | null;
owner_user_id: string | null;
created_by: string | null;
updated_by: string | null;
@@ -44,7 +51,11 @@ export interface ShipmentEvent {
shipment_id: number;
event_type: string | null;
title: string;
status: string;
kind: string; // hito | decision
status: string; // pendiente | completado | omitido | rechazado | en_correccion
outcome: string | null; // autorizado | rechazado
parent_event_id: number | null;
attempt: number;
position: number;
planned_date: string | null;
actual_date: string | null;
@@ -98,6 +109,10 @@ export const shipmentsAPI = {
createFromQuote: (quoteId: number, companyId: number) =>
unwrap<Shipment>(api.post(`/v1/ops/shipments/from-quote?${qp(companyId, { quote_id: quoteId })}`, {})),
update: (id: number, data: Partial<ShipmentInput>, companyId: number) => unwrap<Shipment>(api.patch(`/v1/ops/shipments/${id}?${qp(companyId)}`, data)),
reschedule: (id: number, data: { etd?: string | null; cutoff_date?: string | null; reason?: string | null }, companyId: number) =>
unwrap<Shipment>(api.post(`/v1/ops/shipments/${id}/reschedule?${qp(companyId)}`, data)),
close: (id: number, data: { actual_cost_total: number; cost_currency?: string; notes?: string | null }, companyId: number) =>
unwrap<Shipment>(api.post(`/v1/ops/shipments/${id}/close?${qp(companyId)}`, data)),
remove: (id: number, companyId: number) => unwrap(api.delete(`/v1/ops/shipments/${id}?${qp(companyId)}`)),
documents: (shipmentId: number, companyId: number) =>
unwrap<ShipmentDocument[]>(api.get(`/v1/ops/shipments/${shipmentId}/documents?${qp(companyId)}`)),
@@ -111,6 +126,8 @@ export const shipmentEventsAPI = {
create: (data: ShipmentEventInput, companyId: number) => unwrap<ShipmentEvent>(api.post(`/v1/ops/shipment-events?${qp(companyId)}`, data)),
update: (id: number, data: Partial<ShipmentEventInput>, companyId: number) => unwrap<ShipmentEvent>(api.patch(`/v1/ops/shipment-events/${id}?${qp(companyId)}`, data)),
complete: (id: number, companyId: number) => unwrap<ShipmentEvent>(api.patch(`/v1/ops/shipment-events/${id}/complete?${qp(companyId)}`, {})),
decide: (id: number, outcome: 'autorizado' | 'rechazado', companyId: number, notes?: string | null) =>
unwrap<ShipmentEvent>(api.patch(`/v1/ops/shipment-events/${id}/decision?${qp(companyId)}`, { outcome, notes })),
remove: (id: number, companyId: number) => unwrap(api.delete(`/v1/ops/shipment-events/${id}?${qp(companyId)}`))
};