- Updated the `iva_factor` field in the `InvoiceFinancials` model to use a numeric type with precision and scale. - Adjusted related frontend components and API interfaces to reflect the new numeric type for `iva_factor`. - Enhanced Alembic migration scripts to accommodate the schema changes, ensuring proper index management and data type conversions. These changes improve data integrity and consistency for financial calculations in invoices.
545 lines
16 KiB
TypeScript
545 lines
16 KiB
TypeScript
/**
|
|
* API Client para Facturas (Invoices)
|
|
* Gestiona las operaciones CRUD para facturas y sus relaciones
|
|
*/
|
|
import { api } from '$lib/api';
|
|
|
|
export type OperationType = 'imp' | 'exp';
|
|
export type TransportType = 'none' | 'transport' | 'box' | 'licence plates' | 'truck' | 'vessel' | 'rail barge' | 'container' | 'airplane' | 'gondola' | 'flatbed';
|
|
|
|
// --- Interfaces ---
|
|
|
|
export interface InvoiceComplianceMx {
|
|
invoice_id?: number;
|
|
pedimento_id?: number | null;
|
|
pedimento_r1?: number | null;
|
|
pedimento_k1?: number | null;
|
|
remesa?: number | null;
|
|
// Pedimento relationship data (when joined)
|
|
pedimento?: {
|
|
pedimento_number?: string;
|
|
pedimento_code?: string;
|
|
customs_office?: string;
|
|
license?: string;
|
|
year?: string;
|
|
} | null;
|
|
aduana?: string | null;
|
|
port_of_entry?: string | null;
|
|
destination?: string | null;
|
|
manifest_number?: string | null;
|
|
provider_header?: string | null;
|
|
provider_id?: number | null;
|
|
sold_to_header?: string | null;
|
|
sold_to_id?: number | null;
|
|
shipped_to_header?: string | null;
|
|
shipped_to_id?: number | null;
|
|
shipped_by_header?: string | null;
|
|
shipped_by_id?: number | null;
|
|
customs_broker_id?: number | null;
|
|
customs_broker_us_id?: number | null;
|
|
broker_invoice_num?: string | null;
|
|
broker_invoice_date?: string | null;
|
|
is_mixed?: boolean | null;
|
|
waste_type?: string | null;
|
|
scrap_type?: string | null;
|
|
appendix_17?: number | null;
|
|
is_regime_change?: boolean | null;
|
|
which_exchange_rate?: string | null;
|
|
value_method?: string | null;
|
|
act_value?: string | null;
|
|
is_pedimento_pending?: boolean | null;
|
|
is_owner_of_goods?: string | null;
|
|
generate_balances?: string | null;
|
|
was_reviewed_by_company?: boolean | null;
|
|
edocument?: string | null;
|
|
code_signature?: string | null;
|
|
electronic_signature?: string | null;
|
|
certificate_number?: string | null;
|
|
niu_number?: string | null;
|
|
bill_of_lading_count?: string | null;
|
|
addendum_vu?: string | null;
|
|
origin_destination_cove?: string | null;
|
|
vucem_operation_num?: string | null;
|
|
customs_person_line?: number | null;
|
|
contingency_mode?: boolean | null;
|
|
enclosure?: string | null;
|
|
guide_type_to_identify?: string | null;
|
|
location?: string | null;
|
|
dot_code?: string | null;
|
|
subdivision?: string | null;
|
|
acts_as?: string | null;
|
|
movement_type?: string | null;
|
|
office_document?: string | null;
|
|
reason_export?: string | null;
|
|
signature_key?: string | null;
|
|
sem_id?: number | null;
|
|
rule_3121_parties_ii?: boolean | null;
|
|
}
|
|
|
|
export interface InvoiceFinancials {
|
|
id?: number;
|
|
invoice_id?: number;
|
|
currency?: string | null;
|
|
currency_type?: string | null;
|
|
exchange_rate?: number | null;
|
|
exchange_rate_mm?: number | null;
|
|
value_mn?: number | null;
|
|
value_me?: number | null;
|
|
value_mc?: number | null;
|
|
customs_value_mn?: number | null;
|
|
customs_value_me?: number | null;
|
|
raw_material_value_mn?: number | null;
|
|
raw_material_value_me?: number | null;
|
|
aggregate_value_mn?: number | null;
|
|
aggregate_value_me?: number | null;
|
|
aggregate_value_mc?: number | null;
|
|
mexican_value_mn?: number | null;
|
|
mexican_value_me?: number | null;
|
|
mexican_value_mc?: number | null;
|
|
national_packaging_mn?: number | null;
|
|
national_packaging_me?: number | null;
|
|
national_packaging_mc?: number | null;
|
|
freight?: number | null;
|
|
insurance?: number | null;
|
|
insurance_value?: number | null;
|
|
packaging?: number | null;
|
|
other_increments?: number | null;
|
|
other_deductibles?: number | null;
|
|
total_increments_mn?: number | null;
|
|
total_increments_me?: number | null;
|
|
iva_mn?: number | null;
|
|
iva_me?: number | null;
|
|
iva_mc?: number | null;
|
|
iva_factor?: number | null;
|
|
tax_value_me?: number | null;
|
|
seal_value_2500?: boolean | null;
|
|
total_quantity?: number | null;
|
|
gross_weight?: number | null;
|
|
net_weight?: number | null;
|
|
bundle_count?: number | null;
|
|
weight_factor?: number | null;
|
|
}
|
|
|
|
export interface InvoiceLogistics {
|
|
id?: number;
|
|
invoice_id?: number;
|
|
carrier_id?: string | null;
|
|
transport_id?: string | null;
|
|
transport_us_id?: string | null;
|
|
transport_type?: TransportType | null;
|
|
transport_num?: string | null;
|
|
transport_mode?: string | null;
|
|
driver_name?: string | null;
|
|
is_rail?: string | null;
|
|
rail_id?: string | null;
|
|
vehicle_num?: string | null;
|
|
license_plate?: string | null;
|
|
license_plate_complete?: string | null;
|
|
trailer_num?: string | null;
|
|
seal_number?: string | null;
|
|
guide_number?: string | null;
|
|
bill_number?: string | null;
|
|
reference_number?: string | null;
|
|
shipment_number?: string | null;
|
|
incoterm?: string | null;
|
|
identifier_1?: string | null;
|
|
complement_1?: string | null;
|
|
identifier_2?: string | null;
|
|
complement_2?: string | null;
|
|
weight_type?: string | null;
|
|
container_types?: string | null;
|
|
vehicle_data?: string | null;
|
|
origin_location?: string | null;
|
|
destination_location?: string | null;
|
|
transport_itinerary?: string | null;
|
|
destination_goods?: string | null;
|
|
entry_exit_date?: string | null;
|
|
delivery_date?: string | null;
|
|
delivered_status?: string | null;
|
|
received_by?: string | null;
|
|
payment_date?: string | null;
|
|
payment_receipt_num?: string | null;
|
|
is_ctm_process?: string | null;
|
|
is_subdivision?: boolean | null;
|
|
acts_as_cd?: boolean | null;
|
|
equipment_reviewed?: boolean | null;
|
|
pedimento_arrived?: boolean | null;
|
|
green_light_mx?: boolean | null;
|
|
green_light_us?: boolean | null;
|
|
red_light_mx?: boolean | null;
|
|
red_light_us?: boolean | null;
|
|
}
|
|
|
|
export interface InvoiceSalesDetails {
|
|
id?: number;
|
|
invoice_id?: number;
|
|
line_number: number;
|
|
sales_order?: string | null;
|
|
colors_description?: string | null;
|
|
square_color_code?: string | null;
|
|
line_bundles?: number | null;
|
|
}
|
|
|
|
export interface InvoiceCollections {
|
|
id?: number;
|
|
invoice_id?: number;
|
|
concept?: string | null;
|
|
is_collected?: number | null;
|
|
collection_date?: string | null;
|
|
amount?: number | null;
|
|
collector_user?: string | null;
|
|
}
|
|
|
|
export interface Invoice {
|
|
id: number;
|
|
tenant_id: number;
|
|
company_id: number;
|
|
system?: string | null;
|
|
operation_type?: OperationType | null;
|
|
invoice_type?: string | null;
|
|
document_type?: string | null;
|
|
invoice_number?: string | null;
|
|
project_number?: string | null;
|
|
purchase_order?: string | null;
|
|
related_doc_id?: number | null;
|
|
alternate_invoice?: string | null;
|
|
invoice_ref?: string | null;
|
|
proforma_number?: string | null;
|
|
invoice_date?: string | null;
|
|
capture_date: string;
|
|
emission_date?: string | null;
|
|
status?: "pending" | "processed" | "reversed" | boolean | null;
|
|
processed_date?: string | null;
|
|
who_processed?: string | null;
|
|
capture_user?: string | null;
|
|
traffic_light_status?: string | null;
|
|
process_log?: string | null;
|
|
status_rec?: number | null;
|
|
status_rep?: string | null;
|
|
observation_es?: string | null;
|
|
observation_en?: string | null;
|
|
comments_status?: string | null;
|
|
vu_observations?: string | null;
|
|
cfdi_uuid?: string | null;
|
|
path_pdf?: string | null;
|
|
path_xml?: string | null;
|
|
subcompany?: string | null;
|
|
party_count?: number | null;
|
|
generate_id?: boolean | null;
|
|
generate_desc_parties?: string | null;
|
|
apply_manual_discount?: boolean | null;
|
|
is_bulk?: boolean | null;
|
|
download_substance?: boolean | null;
|
|
download_class?: boolean | null;
|
|
download_def?: boolean | null;
|
|
total_items?: number | null;
|
|
payment_terms?: string | null;
|
|
handling_fees?: number | null;
|
|
option_iv18?: string | null;
|
|
enajenation_goods?: boolean | null;
|
|
compliance_mx?: InvoiceComplianceMx | null;
|
|
financials?: InvoiceFinancials | null;
|
|
logistics?: InvoiceLogistics | null;
|
|
details?: InvoiceSalesDetails[];
|
|
collections?: InvoiceCollections[];
|
|
// Client-side only properties
|
|
is_selected?: boolean;
|
|
}
|
|
|
|
export interface InvoiceListResponse {
|
|
items: Invoice[];
|
|
total: number;
|
|
page: number;
|
|
page_size: number;
|
|
}
|
|
|
|
export interface CreateInvoiceData {
|
|
system: string;
|
|
operation_type: OperationType;
|
|
invoice_type: string;
|
|
document_type: string;
|
|
invoice_number: string;
|
|
project_number?: string | null;
|
|
purchase_order?: string | null;
|
|
related_doc_id?: number | null;
|
|
alternate_invoice?: string | null;
|
|
invoice_ref?: string | null;
|
|
proforma_number?: string | null;
|
|
invoice_date?: string | null;
|
|
emission_date?: string | null;
|
|
status?: "pending" | "processed" | "reversed" | boolean | null;
|
|
processed_date?: string | null;
|
|
who_processed?: string | null;
|
|
capture_user?: string | null;
|
|
traffic_light_status?: string | null;
|
|
process_log?: string | null;
|
|
status_rec?: number | null;
|
|
status_rep?: string | null;
|
|
observation_es?: string | null;
|
|
observation_en?: string | null;
|
|
comments_status?: string | null;
|
|
vu_observations?: string | null;
|
|
cfdi_uuid?: string | null;
|
|
path_pdf?: string | null;
|
|
path_xml?: string | null;
|
|
subcompany?: string | null;
|
|
party_count?: number | null;
|
|
generate_id?: boolean | null;
|
|
generate_desc_parties?: string | null;
|
|
apply_manual_discount?: boolean | null;
|
|
is_bulk?: boolean | null;
|
|
download_substance?: boolean | null;
|
|
download_class?: boolean | null;
|
|
download_def?: boolean | null;
|
|
payment_terms?: string | null;
|
|
handling_fees?: number | null;
|
|
option_iv18?: string | null;
|
|
enajenation_goods?: boolean | null;
|
|
compliance_mx?: Omit<InvoiceComplianceMx, 'invoice_id'> | null;
|
|
financials?: Omit<InvoiceFinancials, 'id' | 'invoice_id'> | null;
|
|
logistics?: Omit<InvoiceLogistics, 'id' | 'invoice_id'> | null;
|
|
details?: Omit<InvoiceSalesDetails, 'id' | 'invoice_id'>[] | null;
|
|
collections?: Omit<InvoiceCollections, 'id' | 'invoice_id'>[] | null;
|
|
}
|
|
|
|
export interface UpdateInvoiceData {
|
|
id: number; // Required by backend
|
|
operation_type?: OperationType | null;
|
|
invoice_type?: string | null;
|
|
document_type?: string | null;
|
|
invoice_number?: string | null;
|
|
project_number?: string | null;
|
|
purchase_order?: string | null;
|
|
related_doc_id?: number | null;
|
|
invoice_date?: string | null;
|
|
traffic_light_status?: string | null;
|
|
process_log?: string | null;
|
|
observation_es?: string | null;
|
|
observation_en?: string | null;
|
|
comments_status?: string | null;
|
|
cfdi_uuid?: string | null;
|
|
path_pdf?: string | null;
|
|
path_xml?: string | null;
|
|
status?: "pending" | "processed" | "reversed" | boolean | null;
|
|
compliance_mx?: Partial<InvoiceComplianceMx> | null;
|
|
financials?: Partial<InvoiceFinancials> | null;
|
|
logistics?: Partial<InvoiceLogistics>[] | null;
|
|
details?: Partial<InvoiceSalesDetails>[] | null;
|
|
collections?: Partial<InvoiceCollections>[] | null;
|
|
}
|
|
|
|
/**
|
|
* API para Facturas
|
|
*/
|
|
export const invoicesApi = {
|
|
/**
|
|
* Lista todas las facturas con paginación
|
|
*/
|
|
list: (companyId: number, page = 1, pageSize = 50, filters?: Record<string, any>) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString(),
|
|
page: page.toString(),
|
|
page_size: pageSize.toString()
|
|
});
|
|
|
|
// Agregar filtros si existen
|
|
if (filters) {
|
|
Object.entries(filters).forEach(([key, value]) => {
|
|
if (value !== null && value !== undefined && value !== '') {
|
|
params.append(key, String(value));
|
|
}
|
|
});
|
|
}
|
|
|
|
return api.get<InvoiceListResponse>(`/v1/a76/invoices/?${params.toString()}`);
|
|
},
|
|
|
|
/**
|
|
* Obtiene una factura por ID
|
|
*/
|
|
get: (invoiceId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.get<Invoice>(`/v1/a76/invoices/${invoiceId}?${params.toString()}`);
|
|
},
|
|
|
|
/**
|
|
* Crea una nueva factura
|
|
*/
|
|
create: (companyId: number, data: CreateInvoiceData) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.post<Invoice>(`/v1/a76/invoices/?${params.toString()}`, data);
|
|
},
|
|
|
|
/**
|
|
* Actualiza una factura existente
|
|
*/
|
|
update: (invoiceId: number, companyId: number, data: UpdateInvoiceData) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.put<Invoice>(`/v1/a76/invoices/${invoiceId}/?${params.toString()}`, data);
|
|
},
|
|
|
|
/**
|
|
* Elimina una factura
|
|
*/
|
|
delete: (invoiceId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.delete(`/v1/a76/invoices/${invoiceId}?${params.toString()}`);
|
|
},
|
|
|
|
/**
|
|
* Obtiene datos para la creación de una factura (catálogos consolidados)
|
|
*/
|
|
getCreationData: (companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.get<any>(`/v1/a76/invoices/creation-data?${params.toString()}`);
|
|
},
|
|
|
|
/**
|
|
* Obtiene datos para la edición de una factura (catálogos consolidados + factura)
|
|
*/
|
|
getEditionData: (invoiceId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.get<any>(`/v1/a76/invoices/${invoiceId}/edition-data?${params.toString()}`);
|
|
},
|
|
|
|
// --- Nested Resources ---
|
|
|
|
/**
|
|
* Logística de factura
|
|
*/
|
|
logistics: {
|
|
list: (invoiceId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.get<InvoiceLogistics[]>(`/v1/a76/invoices/${invoiceId}/logistics/?${params.toString()}`);
|
|
},
|
|
|
|
create: (invoiceId: number, companyId: number, data: Omit<InvoiceLogistics, 'id' | 'invoice_id'>) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.post<InvoiceLogistics>(`/v1/a76/invoices/${invoiceId}/logistics/?${params.toString()}`, data);
|
|
},
|
|
|
|
delete: (invoiceId: number, logisticsId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.delete(`/v1/a76/invoices/${invoiceId}/logistics/${logisticsId}/?${params.toString()}`);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Detalles de venta de factura
|
|
*/
|
|
details: {
|
|
list: (invoiceId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.get<InvoiceSalesDetails[]>(`/v1/a76/invoices/${invoiceId}/details/?${params.toString()}`);
|
|
},
|
|
|
|
create: (invoiceId: number, companyId: number, data: Omit<InvoiceSalesDetails, 'id' | 'invoice_id'>) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.post<InvoiceSalesDetails>(`/v1/a76/invoices/${invoiceId}/details/?${params.toString()}`, data);
|
|
},
|
|
|
|
delete: (invoiceId: number, detailId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.delete(`/v1/a76/invoices/${invoiceId}/details/${detailId}/?${params.toString()}`);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Cobranzas de factura
|
|
*/
|
|
collections: {
|
|
list: (invoiceId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.get<InvoiceCollections[]>(`/v1/a76/invoices/${invoiceId}/collections/?${params.toString()}`);
|
|
},
|
|
|
|
create: (invoiceId: number, companyId: number, data: Omit<InvoiceCollections, 'id' | 'invoice_id'>) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.post<InvoiceCollections>(`/v1/a76/invoices/${invoiceId}/collections/?${params.toString()}`, data);
|
|
},
|
|
|
|
delete: (invoiceId: number, collectionId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.delete(`/v1/a76/invoices/${invoiceId}/collections/${collectionId}/?${params.toString()}`);
|
|
}
|
|
},
|
|
|
|
processInvoice: (invoiceId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.post<{ task_id: string }>(
|
|
`/v1/a76/invoices/${invoiceId}/process?${params.toString()}`,
|
|
{}
|
|
);
|
|
},
|
|
|
|
getProcessStatus: (taskId: string) => {
|
|
return api.get<{
|
|
state: 'PROCESSING' | 'SUCCESS' | 'FAILURE';
|
|
info?: { current: number; status: string };
|
|
result?: {
|
|
status: 'success' | 'validation_error' | 'error';
|
|
invoice_id?: number;
|
|
message?: string;
|
|
errors?: Array<{ field: string; message: string; code?: string; solution?: string[] }>;
|
|
sql_errors?: Array<{ consecutive: number; error: string }>;
|
|
};
|
|
}>(`/v1/a76/invoices/process/${taskId}/status`);
|
|
},
|
|
|
|
revertInvoice: (invoiceId: number, companyId: number) => {
|
|
const params = new URLSearchParams({
|
|
company_id: companyId.toString()
|
|
});
|
|
return api.post<{ task_id: string }>(
|
|
`/v1/a76/invoices/${invoiceId}/revert?${params.toString()}`,
|
|
{}
|
|
);
|
|
},
|
|
|
|
getRevertStatus: (taskId: string) => {
|
|
return api.get<{
|
|
state: 'PROCESSING' | 'SUCCESS' | 'FAILURE';
|
|
info?: { current: number; status: string };
|
|
result?: {
|
|
status: 'success' | 'validation_error' | 'error';
|
|
invoice_id?: number;
|
|
message?: string;
|
|
errors?: Array<{ field: string; message: string; code?: string; solution?: string[] }>;
|
|
sql_errors?: Array<{ consecutive: number; error: string }>;
|
|
};
|
|
}>(`/v1/a76/invoices/revert/${taskId}/status`);
|
|
}
|
|
};
|