Merge branch 'feature/api-sitar-frac-ame' into feature/company-id-lost

This commit is contained in:
2026-04-24 10:29:14 -06:00
56 changed files with 688 additions and 684 deletions

View File

@@ -805,24 +805,6 @@ export const api = {
fetchBlob(`/v1/a76/exchange-rate/imports/${jobId}/errors/scan-csv`)
},
// CSV import for Fracción Americana (us_tariff_fractions/imports)
americanFractionImports: {
upload: (file: File, companyId: number, uploadOptions?: CsvFormDataUploadOptions) => {
const formData = new FormData();
formData.append('file', file);
return fetchApiFormDataPost(
`/v1/a76/us-tariff-fractions/imports/upload?company_id=${companyId}`,
formData,
uploadOptions
);
},
status: (jobId: string) => api.get(`/v1/a76/us-tariff-fractions/imports/${jobId}/status`),
commit: (jobId: string) =>
api.post(`/v1/a76/us-tariff-fractions/imports/${jobId}/commit`, {}),
downloadScanErrorsCsv: (jobId: string) =>
fetchBlob(`/v1/a76/us-tariff-fractions/imports/${jobId}/errors/scan-csv`)
},
// CSV import for Pedimentos (pedimentos/imports)
pedimentosImports: {
upload: (

View File

@@ -107,7 +107,6 @@ export interface Company {
responsible_mother_last_name: string | null;
responsible_rfc?: string | null;
position?: string | null;
has_express_line?: boolean;
fiscal_deposit?: boolean;
generate_barcodes_with_fiel?: boolean;
is_service_company?: boolean;
@@ -273,7 +272,6 @@ export interface CompanyCreate {
responsible_mother_last_name?: string | null;
responsible_rfc?: string | null;
position?: string | null;
has_express_line?: boolean;
fiscal_deposit?: boolean;
generate_barcodes_with_fiel?: boolean;
is_service_company?: boolean;

View File

@@ -1,37 +1,19 @@
import { api } from '$lib/api';
import type { ApiResponse } from '$lib/api';
// Interfaces
/** List item shape returned by GET /v1/a76/us-tariff-fractions/ (SITAR-backed, read-only). */
export interface USTariffFraction {
id: number;
code: string;
prefix: string | null;
type_code: string | null;
ad_valorem: number | null;
fixed_cost: number | null;
unit_of_measure: string | null;
description: string | null;
created_at: string | null;
updated_at: string | null;
}
export interface USTariffFractionCreate {
code: string;
prefix?: string | null;
type_code?: string | null;
ad_valorem?: number | null;
fixed_cost?: number | null;
unit_of_measure?: string | null;
description?: string | null;
}
export interface USTariffFractionUpdate {
fraction?: string | null;
prefix?: string | null;
type_code?: string | null;
ad_valorem?: number | null;
fixed_cost?: number | null;
unit_of_measure?: string | null;
description?: string | null;
created_at?: string;
updated_at?: string;
}
export interface USTariffFractionListResponse {
@@ -42,47 +24,20 @@ export interface USTariffFractionListResponse {
pages: number;
}
// API Functions
/** Paginated list from SITAR (same data as tariff-fractions?catalog=american). */
export async function getUSTariffFractions(
page = 1,
pageSize = 50,
companyId: number,
filters: Record<string, any> = {}
filters: Record<string, string | number | boolean> = {}
): Promise<ApiResponse<USTariffFractionListResponse>> {
const queryParams = new URLSearchParams({
page: page.toString(),
page_size: pageSize.toString(),
company_id: companyId.toString(),
...filters
...Object.fromEntries(
Object.entries(filters).map(([k, v]) => [k, String(v)])
)
});
return await api.get(`/v1/a76/us-tariff-fractions/?${queryParams.toString()}`);
}
export async function getUSTariffFractionById(
id: number,
companyId: number
): Promise<ApiResponse<USTariffFraction>> {
return await api.get(`/v1/a76/us-tariff-fractions/${id}/?company_id=${companyId}`);
}
export async function createUSTariffFraction(
data: USTariffFractionCreate,
companyId: number
): Promise<ApiResponse<USTariffFraction>> {
return await api.post(`/v1/a76/us-tariff-fractions/?company_id=${companyId}`, data);
}
export async function updateUSTariffFraction(
id: number,
data: USTariffFractionUpdate,
companyId: number
): Promise<ApiResponse<USTariffFraction>> {
return await api.put(`/v1/a76/us-tariff-fractions/${id}/?company_id=${companyId}`, data);
}
export async function deleteUSTariffFraction(
id: number,
companyId: number
): Promise<ApiResponse<void>> {
return await api.delete(`/v1/a76/us-tariff-fractions/${id}/?company_id=${companyId}`);
}

View File

@@ -21,6 +21,7 @@ export interface Transporter {
ftp_password?: string;
ftp_directory?: string;
filler_code?: string;
has_express_line?: boolean;
company_id?: number | string;
tenant_id?: number | string;
}

View File

@@ -40,7 +40,6 @@
customs_brokers: 'Agentes aduanales',
clients_providers: 'Clientes / proveedores',
exchange_rates: 'Tipos de cambio',
american_fractions: 'Fracciones arancelarias US',
pedimentos: 'Pedimentos',
material_classes: 'Clases de material',
vehicles: 'Vehículos',

View File

@@ -37,7 +37,6 @@
| 'customs_brokers'
| 'clients_providers'
| 'exchange_rate'
| 'us_tariff_fractions'
| 'pedimentos'
| 'classes'
| 'vehicles'
@@ -139,9 +138,6 @@
case 'exchange_rate':
blob = await api.exchangeRateImports.downloadScanErrorsCsv(jobId);
break;
case 'us_tariff_fractions':
blob = await api.americanFractionImports.downloadScanErrorsCsv(jobId);
break;
case 'pedimentos':
blob = await api.pedimentosImports.downloadScanErrorsCsv(jobId);
break;

View File

@@ -6,6 +6,7 @@
import * as Tabs from "$lib/components/ui/tabs";
import { Switch } from "$lib/components/ui/switch";
import { createCompany, updateCompany, type Company } from "$lib/api/dashboard/a76/general_catalogs/company";
import { companyStore } from "$lib/stores/company.svelte";
let {
open = $bindable(false),
@@ -43,7 +44,6 @@
// Configuración / Operativo
manufacturer_id: '',
has_express_line: false,
is_service_company: false,
order_format_type: '',
@@ -76,7 +76,6 @@
position: item.position || '',
manufacturer_id: item.manufacturer_id || '',
has_express_line: item.has_express_line || false,
is_service_company: item.is_service_company || false,
order_format_type: item.order_format_type || '',
@@ -89,7 +88,7 @@
name: '', rfc: '', curp: '', main_activity: '',
program: '', program_number: '', prosec: 0, prosec_authorization: '',
responsible_name: '', responsible_last_name: '', responsible_mother_last_name: '', responsible_rfc: '', position: '',
manufacturer_id: '', has_express_line: false, is_service_company: false, order_format_type: '',
manufacturer_id: '', is_service_company: false, order_format_type: '',
ctpat_svi: '', trusted_exporter_number: ''
};
}
@@ -124,7 +123,6 @@
position: formData.position.trim() || null,
manufacturer_id: formData.manufacturer_id.trim() || null,
has_express_line: formData.has_express_line,
is_service_company: formData.is_service_company,
order_format_type: formData.order_format_type.trim() || null,
@@ -143,6 +141,7 @@
throw new Error(response.error);
}
await companyStore.loadCompanies();
open = false;
if (onSuccess) onSuccess();
} catch (e) {
@@ -249,10 +248,6 @@
<Tabs.Content value="config" class="space-y-4 py-4">
<div class="grid grid-cols-2 gap-6">
<div class="flex items-center space-x-2">
<Switch id="express" bind:checked={formData.has_express_line} />
<Label for="express">Carril Exprés (OEA/NEEC)</Label>
</div>
<div class="flex items-center space-x-2">
<Switch id="service" bind:checked={formData.is_service_company} />
<Label for="service">Es Empresa de Servicios</Label>

View File

@@ -14,10 +14,6 @@
getTariffFractions,
type TariffFraction
} from '$lib/api/dashboard/a76/general_catalogs/tariff-fractions';
import {
getUSTariffFractions,
type USTariffFraction
} from '$lib/api/dashboard/a76/general_catalogs/us-tariff-fractions';
import {
getDepreciationCatalog,
type DepreciationCatalog
@@ -203,7 +199,7 @@
formData.import_tariff_type = (fraction.umt ?? '') as string;
showFractionDialog = false;
}
let usTariffFractions = $state<USTariffFraction[]>([]);
let usTariffFractions = $state<TariffFraction[]>([]);
let searchUSFraction = $state('');
let currentUSPage = $state(1);
let totalUSFractions = $state(0);
@@ -392,10 +388,11 @@
isLoadingUSFractions = true;
try {
const filters = search ? { search } : {};
const filters: Record<string, string> = { catalog: 'american' };
if (search?.trim()) filters.search = search.trim();
const pageSize = 100;
const response = await getUSTariffFractions(page, pageSize, companyId, filters);
const response = await getTariffFractions(page, pageSize, companyId, filters);
if (response.data) {
if (page === 1) {
@@ -415,10 +412,16 @@
}
}
function selectUSFraction(fraction: USTariffFraction) {
formData.us_fraction = fraction.code;
formData.us_fraction_ad_valorem = fraction.ad_valorem?.toString() || '0.00';
formData.us_fraction_fixed_rate = fraction.fixed_cost?.toString() || '0.00000000';
function selectUSFraction(fraction: TariffFraction) {
formData.us_fraction = fraction.code || '';
const advRaw = fraction.adv_impo;
let advNum = 0;
if (advRaw != null && String(advRaw).trim() !== '') {
const n = parseFloat(String(advRaw).replace('%', '').trim());
if (!Number.isNaN(n)) advNum = n;
}
formData.us_fraction_ad_valorem = advNum ? String(advNum) : '0.00';
formData.us_fraction_fixed_rate = '0.00000000';
// Actualizar tarifa de exportación
formData.export_tariff_code = fraction.code || '';
formData.export_tariff_type = 'HTS'; // Harmonized Tariff Schedule
@@ -1041,10 +1044,10 @@
class="cursor-pointer border-b transition-colors hover:bg-gray-100 dark:hover:bg-gray-700"
onclick={() => selectUSFraction(fraction)}
>
<td class="px-4 py-2 whitespace-nowrap">{fraction.code}</td>
<td class="px-4 py-2 whitespace-nowrap">{fraction.prefix || ''}</td>
<td class="px-4 py-2 whitespace-nowrap">{fraction.ad_valorem || '0.00'}</td>
<td class="px-4 py-2 whitespace-nowrap">{fraction.fixed_cost || '0.00'}</td>
<td class="px-4 py-2 whitespace-nowrap">{fraction.fraction || fraction.code}</td>
<td class="px-4 py-2 whitespace-nowrap">{fraction.code || ''}</td>
<td class="px-4 py-2 whitespace-nowrap">{fraction.adv_impo ?? ''}</td>
<td class="px-4 py-2 whitespace-nowrap">{fraction.adv_expo ?? ''}</td>
<td class="px-4 py-2">{fraction.description || ''}</td>
</tr>
{:else}

View File

@@ -5,7 +5,10 @@
import * as Table from "$lib/components/ui/table";
import { toast } from "svelte-sonner";
import { Search, Loader2, Hash } from "lucide-svelte";
import { getUSTariffFractions, type USTariffFraction } from "$lib/api/dashboard/a76/general_catalogs/us-tariff-fractions";
import {
getTariffFractions,
type TariffFraction
} from "$lib/api/dashboard/a76/general_catalogs/tariff-fractions";
import { companyStore } from "$lib/stores/company.svelte";
import { m } from '$lib/i18n/messages';
@@ -15,11 +18,11 @@
onSelect
}: {
open: boolean,
onSelect: (item: USTariffFraction) => void
onSelect: (item: TariffFraction) => void
} = $props();
// --- ESTADO ---
let items = $state<USTariffFraction[]>([]);
let items = $state<TariffFraction[]>([]);
let loading = $state(false);
let searchTerm = $state("");
let loadedForCompanyId = $state<number | null>(null);
@@ -30,8 +33,8 @@
return (code || '').replace(/[.\s-]/g, '');
}
function isEligibleAmericanFraction(item: USTariffFraction) {
const normalizedCode = normalizeAmericanFractionCode(item.code || '');
function isEligibleAmericanFraction(item: TariffFraction) {
const normalizedCode = normalizeAmericanFractionCode(item.code || item.fraction || '');
return /^\d{8}$/.test(normalizedCode) || /^\d{10}$/.test(normalizedCode);
}
@@ -70,7 +73,9 @@
loading = true;
try {
const response = await getUSTariffFractions(1, 1000, companyId);
const response = await getTariffFractions(1, 1000, companyId, {
catalog: 'american'
});
if (response.error) {
console.error("Error al cargar fracciones americanas:", response.error);
@@ -97,7 +102,7 @@
}
}
function handleSelect(item: USTariffFraction) {
function handleSelect(item: TariffFraction) {
if (onSelect) onSelect(item);
open = false;
}
@@ -150,7 +155,7 @@
<div class="flex items-center gap-2">
<Hash class="h-3 w-3 text-blue-500" />
<span class="font-mono font-bold text-blue-600 dark:text-blue-400">
{item.code}
{item.fraction || item.code}
</span>
</div>
</Table.Cell>

View File

@@ -8,6 +8,7 @@
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
import PackageDialog from './package-dialog.svelte';
import USFractionSelectorDialog from '$lib/components/dashboard/goods/modales/us-fraction-selector-dialog.svelte';
import type { TariffFraction } from '$lib/api/dashboard/a76/general_catalogs/tariff-fractions';
import { m } from '$lib/i18n/messages';
let {
@@ -118,11 +119,12 @@
quantities.package_description = pkg.description_es || pkg.description_en || pkg.key;
}
function handleAmericanFractionSelect(fraction: any) {
function handleAmericanFractionSelect(fraction: TariffFraction) {
customs.american_fraction = fraction.code || '';
(customs as any).american_fraction_description = fraction.description || '';
if (fraction.ad_valorem !== null && fraction.ad_valorem !== undefined) {
customs.advalorem_american = fraction.ad_valorem;
if (fraction.adv_impo != null && String(fraction.adv_impo).trim() !== '') {
const n = parseFloat(String(fraction.adv_impo).replace('%', '').trim());
if (!Number.isNaN(n)) customs.advalorem_american = n;
}
}

View File

@@ -3,6 +3,7 @@
import * as Dialog from '$lib/components/ui/dialog';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Switch } from '$lib/components/ui/switch';
import * as Select from '$lib/components/ui/select';
import { transportersApi, type Transporter } from '$lib/api/dashboard/a76/transporters';
import { countriesApi, type Country } from '$lib/api/dashboard/reference_data/countries';
@@ -45,7 +46,8 @@
ftp_user: '',
ftp_password: '',
ftp_directory: '',
filler_code: ''
filler_code: '',
has_express_line: false
});
let loading = $state(false);
@@ -99,7 +101,8 @@
ftp_user: '',
ftp_password: '',
ftp_directory: '',
filler_code: ''
filler_code: '',
has_express_line: false
};
}
@@ -110,7 +113,7 @@
return;
}
if (item) {
formData = { ...item };
formData = { ...item, has_express_line: item.has_express_line ?? false };
} else {
formData = emptyTransporterForm();
}
@@ -284,6 +287,11 @@
<Label for="filler_code">Código Relleno</Label>
<Input id="filler_code" bind:value={formData.filler_code} maxlength={20} />
</div>
<div class="flex items-center justify-between gap-3 rounded-lg border p-4">
<Label for="has_express_line" class="text-sm font-medium">Carril express</Label>
<Switch id="has_express_line" bind:checked={formData.has_express_line} />
</div>
</section>
<!-- Dirección -->

View File

@@ -318,10 +318,6 @@ export function getSidebarData(): SidebarData {
title: m["sidebar.fractions.sitar_us"](),
url: "/dashboard/general_catalogs/tariff-fractions/us",
},
{
title: m["sidebar.fractions.american"](),
url: "/dashboard/general_catalogs/tariff-fractions/american",
},
{
title: m["sidebar.fractions.canadian"](),
url: "/dashboard/general_catalogs/tariff-fractions/canadian",

View File

@@ -8,7 +8,6 @@ import {
Plane,
Package,
Briefcase,
Globe,
CreditCard,
DollarSign,
Calendar,
@@ -227,14 +226,6 @@ export const catalogosConfig: CsvUploadItem[] = [
templateId: 'exchange_rates',
layoutModule: 'layouts_csv/exchange_rate'
},
{
id: 'american_fractions',
title: 'Fracc. Ame.',
icon: Globe,
modelTarget: 'AmericanFraction',
templateId: 'american_fractions',
layoutModule: 'layouts_csv/us_tariff_fractions'
},
{
id: 'material_classes',
title: 'Clases de Materiales',

View File

@@ -5,7 +5,6 @@ const VALID_PROFILES: CsvImportProfile[] = [
'customs_brokers',
'clients_providers',
'exchange_rates',
'american_fractions',
'pedimentos',
'material_classes',
'vehicles',

View File

@@ -9,7 +9,6 @@ export type CsvImportProfile =
| 'customs_brokers'
| 'clients_providers'
| 'exchange_rates'
| 'american_fractions'
| 'pedimentos'
| 'material_classes'
| 'vehicles'
@@ -88,7 +87,6 @@ function isCsvImportProfile(p: unknown): p is CsvImportProfile {
'customs_brokers',
'clients_providers',
'exchange_rates',
'american_fractions',
'pedimentos',
'material_classes',
'vehicles',

View File

@@ -15,8 +15,6 @@ export function fetchCsvImportStatus(
return api.clientProviderImports.status(jobId);
case 'exchange_rates':
return api.exchangeRateImports.status(jobId);
case 'american_fractions':
return api.americanFractionImports.status(jobId);
case 'pedimentos':
return api.pedimentosImports.status(jobId);
case 'material_classes':