feat(crm,ops): frontend de Solicitudes, Cotizaciones y Embarques (páginas con pestañas)
- Solicitudes (RFQ): alta + detalle (Requerimientos / Tarifas) - Cotizaciones: detalle con conceptos (costo/venta/margen), totales, acciones Enviar/Aceptar/Rechazar y "Liberar a Operaciones" - Embarques: alta + detalle (Datos / Documentos Master-House) - clientes API comercial + ops; sidebar CRM + grupo Operaciones - svelte-check: 0 errores de tipo en archivos del CRM/OPS Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
90
frontend/src/lib/api/ops/index.ts
Normal file
90
frontend/src/lib/api/ops/index.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Cliente API — Operaciones (Embarques y documentos de transporte).
|
||||
*/
|
||||
import { api } from '$lib/api';
|
||||
|
||||
export type ShipmentStatus =
|
||||
| 'abierta' | 'booking' | 'en_transito' | 'arribado' | 'entregada' | 'cerrada' | 'cancelada';
|
||||
|
||||
export interface Shipment {
|
||||
id: number;
|
||||
reference: string | null;
|
||||
quote_id: number | null;
|
||||
service_request_id: number | null;
|
||||
account_id: number | null;
|
||||
operation_type: string | null;
|
||||
transport_mode: string | null;
|
||||
service_type: string | null;
|
||||
incoterm: string | null;
|
||||
origin: string | null;
|
||||
destination: string | null;
|
||||
status: ShipmentStatus;
|
||||
booking_number: string | null;
|
||||
carrier_supplier_id: number | null;
|
||||
customs_agent_id: number | null;
|
||||
destination_agent_id: number | null;
|
||||
cutoff_date: string | null;
|
||||
etd: string | null;
|
||||
eta: string | null;
|
||||
vessel_flight: string | null;
|
||||
container_number: string | null;
|
||||
notes: string | null;
|
||||
owner_user_id: string | null;
|
||||
created_by: string | null;
|
||||
updated_by: string | null;
|
||||
tenant_id: number;
|
||||
company_id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
export type ShipmentInput = Partial<Omit<Shipment, 'id' | 'tenant_id' | 'company_id' | 'created_at' | 'updated_at' | 'created_by' | 'updated_by'>>;
|
||||
|
||||
export interface ShipmentDocument {
|
||||
id: number;
|
||||
shipment_id: number;
|
||||
doc_kind: string;
|
||||
doc_type: string;
|
||||
number: string | null;
|
||||
issue_date: string | null;
|
||||
file_url: string | null;
|
||||
file_key: string | null;
|
||||
notes: string | null;
|
||||
tenant_id: number;
|
||||
company_id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
export type ShipmentDocumentInput = Partial<Omit<ShipmentDocument, 'id' | 'tenant_id' | 'company_id' | 'created_at' | 'updated_at'>> & {
|
||||
shipment_id: number;
|
||||
doc_type: string;
|
||||
};
|
||||
|
||||
function qp(companyId: number, extra?: Record<string, string | number | undefined>) {
|
||||
const qs = new URLSearchParams({ company_id: String(companyId) });
|
||||
for (const [k, v] of Object.entries(extra ?? {})) if (v !== undefined && v !== '') qs.set(k, String(v));
|
||||
return qs.toString();
|
||||
}
|
||||
async function unwrap<T>(p: Promise<{ data?: T; error?: string }>): Promise<T> {
|
||||
const res = await p;
|
||||
if (res.error) throw new Error(res.error);
|
||||
return res.data as T;
|
||||
}
|
||||
|
||||
export const shipmentsAPI = {
|
||||
list: (companyId: number, params?: { search?: string; status?: string; account_id?: number }) =>
|
||||
unwrap<Shipment[]>(api.get(`/v1/ops/shipments?${qp(companyId, params)}`)),
|
||||
get: (id: number, companyId: number) => unwrap<Shipment>(api.get(`/v1/ops/shipments/${id}?${qp(companyId)}`)),
|
||||
create: (data: ShipmentInput, companyId: number) => unwrap<Shipment>(api.post(`/v1/ops/shipments?${qp(companyId)}`, data)),
|
||||
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)),
|
||||
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)}`))
|
||||
};
|
||||
|
||||
export const shipmentDocumentsAPI = {
|
||||
create: (data: ShipmentDocumentInput, companyId: number) => unwrap<ShipmentDocument>(api.post(`/v1/ops/shipment-documents?${qp(companyId)}`, data)),
|
||||
update: (id: number, data: Partial<ShipmentDocumentInput>, companyId: number) => unwrap<ShipmentDocument>(api.patch(`/v1/ops/shipment-documents/${id}?${qp(companyId)}`, data)),
|
||||
remove: (id: number, companyId: number) => unwrap(api.delete(`/v1/ops/shipment-documents/${id}?${qp(companyId)}`))
|
||||
};
|
||||
Reference in New Issue
Block a user