Agregar El Salvador y actualizar dist validado

This commit is contained in:
2026-09-07 15:14:41 -04:00
parent 043a442639
commit 2495256856
22 changed files with 3387 additions and 301 deletions
+3 -2
View File
@@ -65,8 +65,9 @@ export default function BambooCorrectionModal({
const recipientLabel = useMemo(() => {
if (country.code === 'TT') return 'Laurele Nicome-Jack';
if (country.code === 'PA') return 'Delarys Peñalba y Ariadne Cortéz';
return 'Pablo Gamboa Cú, Sara Meyer Lima y María Morales Adler';
if (country.code === 'PA') return 'Delarys Peñalba';
if (country.code === 'SV') return 'Carlos Antonio Malta Méndez';
return 'haguirre@gomezleemarketing.com';
}, [country.code]);
const sourceNames = useMemo(
+1 -1
View File
@@ -36,7 +36,7 @@ export default function CountrySelector({ onSelect }: CountrySelectorProps) {
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{COUNTRIES.map((country) => (
<button
key={country.code}
+30 -19
View File
@@ -84,6 +84,13 @@ function getReconciliationUrl(countryCode: string) {
);
}
if (countryCode === 'SV') {
return (
(import.meta.env.VITE_N8N_EL_SALVADOR_WEBHOOK_URL as string | undefined) ||
'https://agenteit.digitalcompass.agency/webhook/nominasv-bamboo-test'
);
}
return (
(import.meta.env.VITE_N8N_GUATEMALA_WEBHOOK_URL as string | undefined) ||
(import.meta.env.VITE_N8N_WEBHOOK_URL as string | undefined) ||
@@ -787,6 +794,8 @@ export default function Dashboard({
}: DashboardProps) {
const defaultPeriod = useMemo(() => readStoredPeriod(country.code), [country.code]);
const isPanama = country.code === 'PA';
const isElSalvador = country.code === 'SV';
const isMultiPayrollCountry = isPanama || isElSalvador;
const [isProcessing, setIsProcessing] = useState(false);
const [processingStep, setProcessingStep] = useState('');
const [filterMode, setFilterMode] = useState<
@@ -1000,11 +1009,11 @@ export default function Dashboard({
}
};
const addPanamaPayrollFiles = async (files: File[]) => {
const addMultiPayrollFiles = async (files: File[]) => {
const excelFiles = files.filter(isExcelFile);
if (excelFiles.length === 0) {
onShowToast('Formato inválido. Panamá requiere uno o varios archivos Excel (.xlsx, .xls).', 'error');
onShowToast(`Formato inválido. ${country.name} requiere uno o varios archivos Excel (.xlsx, .xls).`, 'error');
return;
}
@@ -1027,7 +1036,7 @@ export default function Dashboard({
const detected = await applyDetectedPayrollPeriod(detectionFile, combined.length);
if (!detected) {
onShowToast(`${newFiles.length} archivo(s) de nómina de Panamá cargado(s) con éxito.`, 'success');
onShowToast(`${newFiles.length} archivo(s) de nómina de ${country.name} cargado(s) con éxito.`, 'success');
}
};
@@ -1040,8 +1049,8 @@ export default function Dashboard({
const files = Array.from(e.dataTransfer.files) as File[];
if (files.length === 0) return;
if (isPanama) {
await addPanamaPayrollFiles(files);
if (isMultiPayrollCountry) {
await addMultiPayrollFiles(files);
return;
}
@@ -1072,8 +1081,8 @@ export default function Dashboard({
const files = Array.from(e.target.files || []) as File[];
if (files.length === 0) return;
if (isPanama) {
await addPanamaPayrollFiles(files);
if (isMultiPayrollCountry) {
await addMultiPayrollFiles(files);
return;
}
@@ -1085,7 +1094,7 @@ export default function Dashboard({
}
};
const removePanamaPayrollFile = (indexToRemove: number) => {
const removeMultiPayrollFile = (indexToRemove: number) => {
setUploadedPayrollFiles(
uploadedPayrollFiles.filter((_, index) => index !== indexToRemove)
);
@@ -1117,7 +1126,7 @@ export default function Dashboard({
onShowToast('Planilla de banco removida.', 'info');
};
const clearPanamaUploadedFiles = () => {
const clearUploadedFiles = () => {
setUploadedPayroll(null);
setUploadedPayrollFiles([]);
setUploadedBankFiles([]);
@@ -1208,7 +1217,7 @@ export default function Dashboard({
return;
}
const payrollFilesForRun = isPanama
const payrollFilesForRun = isMultiPayrollCountry
? uploadedPayrollFiles
: (uploadedPayroll ? [uploadedPayroll] : []);
@@ -1217,8 +1226,8 @@ export default function Dashboard({
payrollFilesForRun.some((file) => !file.rawFile)
) {
onShowToast(
isPanama
? 'Debes cargar la nómina principal y los suplementos de Panamá antes de ejecutar el cruce.'
isMultiPayrollCountry
? `Debes cargar todos los archivos de nómina de ${country.name} correspondientes al período antes de ejecutar el cruce.`
: 'Debes cargar la nómina antes de ejecutar el cruce.',
'error'
);
@@ -1297,7 +1306,7 @@ export default function Dashboard({
Object.entries(metadata).forEach(([key, value]) => {
formData.append(key, Array.isArray(value) ? JSON.stringify(value) : String(value));
});
if (isPanama) {
if (isMultiPayrollCountry) {
payrollFilesForRun.forEach((file, index) => {
if (!file.rawFile) return;
formData.append('payroll_files', file.rawFile, file.name);
@@ -1639,11 +1648,11 @@ export default function Dashboard({
</div>
</section>
{isPanama && (uploadedPayrollFiles.length > 0 || uploadedBankFiles.length > 0) && (
{isMultiPayrollCountry && (uploadedPayrollFiles.length > 0 || uploadedBankFiles.length > 0) && (
<div className="flex justify-end" style={{ marginBottom: '-20px' }}>
<button
type="button"
onClick={clearPanamaUploadedFiles}
onClick={clearUploadedFiles}
className="inline-flex items-center gap-2 border border-red-100 bg-white px-3 py-2 text-[10px] font-black text-red-600 hover:bg-red-50 transition-colors uppercase tracking-wide shadow-sm"
aria-label="Borrar todos los archivos cargados"
title="Eliminar de una vez todas las nóminas y planillas del banco cargadas"
@@ -1668,11 +1677,11 @@ export default function Dashboard({
ref={payrollInputRef}
onChange={handlePayrollSelect}
accept=".xlsx,.xls"
multiple={isPanama}
multiple={isMultiPayrollCountry}
className="hidden"
/>
{isPanama ? (
{isMultiPayrollCountry ? (
uploadedPayrollFiles.length > 0 ? (
<div className="space-y-3">
<div className="border border-[#4F758B]/20 bg-[#4F758B]/5 px-4 py-3">
@@ -1680,7 +1689,9 @@ export default function Dashboard({
{uploadedPayrollFiles.length} archivo(s) de nómina cargado(s)
</p>
<p className="text-[10px] text-neutral-500 mt-1">
Incluye la quincenal y todas las planillas extraordinarias, vacaciones o XIII Mes que correspondan al período.
{isPanama
? 'Incluye la quincenal y todas las planillas extraordinarias, vacaciones o XIII Mes que correspondan al período.'
: 'Incluye todos los archivos de nómina de El Salvador que correspondan a la quincena seleccionada (General, Karla Elias, Nestlé, Motorola u otros del mismo formato).'}
</p>
</div>
@@ -1697,7 +1708,7 @@ export default function Dashboard({
</div>
</div>
<button
onClick={() => removePanamaPayrollFile(index)}
onClick={() => removeMultiPayrollFile(index)}
className="p-1.5 text-neutral-400 hover:text-red-500 hover:bg-neutral-100 transition-all rounded"
aria-label={`Remover ${file.name}`}
>
+16 -2
View File
@@ -40,6 +40,13 @@ function getHistoryUrl(countryCode: string) {
);
}
if (countryCode === 'SV') {
return (
(import.meta.env.VITE_HISTORICOS_SV_URL as string | undefined) ||
'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-sv-historicos'
);
}
return (
(import.meta.env.VITE_HISTORICOS_GT_URL as string | undefined) ||
'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-historicos'
@@ -58,6 +65,13 @@ function getMarkResolvedUrl(countryCode: string) {
);
}
if (countryCode === 'SV') {
return (
(import.meta.env.VITE_MARCAR_RESUELTO_SV_URL as string | undefined) ||
'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-sv-marcar-resuelto'
);
}
return (
(import.meta.env.VITE_MARCAR_RESUELTO_GT_URL as string | undefined) ||
'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-marcar-resuelto'
@@ -143,7 +157,7 @@ function normalizeReports(input: unknown, country: CountryConfig): HistoricalRep
return {
id: String(report.id ?? `reporte-${index + 1}`),
country: (rawCountry === 'TT' ? 'TT' : rawCountry === 'PA' ? 'PA' : 'GT') as HistoricalReport['country'],
country: (rawCountry === 'TT' ? 'TT' : rawCountry === 'PA' ? 'PA' : rawCountry === 'SV' ? 'SV' : 'GT') as HistoricalReport['country'],
date: formatDate(report.fecha ?? report.period_end ?? rawExecutionDate, country.locale),
executionDate: formatDateTime(rawExecutionDate, country.locale),
period: String(report.periodo ?? report.period_label ?? report.period ?? 'Período no especificado'),
@@ -457,7 +471,7 @@ export default function History({
if (!isCurrentRequest()) return;
// Respaldo temporal: webhook histórico existente. También se envía el país
// para impedir que se mezclen reportes de Guatemala, Trinidad y Tobago y Panamá.
// para impedir que se mezclen reportes de Guatemala, Trinidad y Tobago, Panamá y El Salvador.
const historyUrl = getHistoryUrl(country.code);
if (!historyUrl) {
throw new Error(`No está configurado el origen histórico de ${country.name}.`);
+8
View File
@@ -25,6 +25,14 @@ export const COUNTRIES: CountryConfig[] = [
locale: 'es-PA',
enabled: true,
},
{
code: 'SV',
name: 'El Salvador',
flag: 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_El_Salvador.svg.webp',
currency: 'USD',
locale: 'es-SV',
enabled: true,
},
];
export const DEFAULT_COUNTRY_CODE: CountryCode = 'GT';
+1 -1
View File
@@ -4,7 +4,7 @@ export type ViewType = 'cruce' | 'historial';
export type ToastType = 'success' | 'error' | 'info';
export type CountryCode = 'GT' | 'TT' | 'PA';
export type CountryCode = 'GT' | 'TT' | 'PA' | 'SV';
export interface CountryConfig {
code: CountryCode;