feat: mejoras SAT, auth, web y migración

SAT:
- Cron diario 6-10 AM con grupos de tenants (~20% por hora).
- Retries fijos de daily sync a 9 AM y 4 PM CDMX.
- Filtrado de contribuyentes con FIEL vigente en cron.
- Timeout de 5 min en cliente HTTP del SAT.
- Manejo defensivo de 404, EmptyResult (5004) y solicitudes agotadas.
- Fechas formateadas en zona horaria America/Mexico_City.
- Patch a @nodecfdi/sat-ws-descarga-masiva para evitar getResponse crash.
- Sweep de jobs stale con thresholds ajustados.

Auth/Web:
- Primer pago de suscripción define periodo activo en webhook.
- Rate limit de login: 25 intentos / 15 min.
- Recuperación de contraseña: 24h de validez.
- Soporte viewingTenantId en contribuyentes.
- Timeout y estado de carga al crear organización en Facturapi.
- Filtros por cliente y cartera en Mis Asignados.
- Orden alfabético en selector de contribuyente.

DB:
- Migración 057: unique index de declaraciones incluye impuestos.
This commit is contained in:
Horux Dev
2026-08-02 20:06:51 +00:00
parent dfc0183c12
commit 284c7620a9
14 changed files with 409 additions and 45 deletions

View File

@@ -161,6 +161,7 @@ export default function CsdConfigPage() {
const queryClient = useQueryClient();
const [uploading, setUploading] = useState(false);
const [creatingOrg, setCreatingOrg] = useState(false);
const [cerFile, setCerFile] = useState<string>('');
const [keyFile, setKeyFile] = useState<string>('');
const [password, setPassword] = useState('');
@@ -178,16 +179,28 @@ export default function CsdConfigPage() {
};
const handleCreateOrg = async () => {
if (creatingOrg) return;
setCreatingOrg(true);
setMessage(null);
try {
const cfg = { timeout: 60000 };
if (selectedContribuyenteId) {
await apiClient.post(`/contribuyentes/${selectedContribuyenteId}/facturapi/org`);
await apiClient.post(`/contribuyentes/${selectedContribuyenteId}/facturapi/org`, undefined, cfg);
} else {
await apiClient.post('/facturacion/org');
await apiClient.post('/facturacion/org', undefined, cfg);
}
queryClient.invalidateQueries({ queryKey: ['facturapi-org-contrib'] });
setMessage({ type: 'success', text: 'Organización creada en Facturapi' });
} catch (err: any) {
setMessage({ type: 'error', text: err.response?.data?.message || 'Error al crear organización' });
const isTimeout = err?.code === 'ECONNABORTED';
setMessage({
type: 'error',
text: isTimeout
? 'La creación está tardando más de lo esperado. Refresca la página en unos segundos; si no aparece, intenta de nuevo.'
: (err.response?.data?.message || 'Error al crear organización'),
});
} finally {
setCreatingOrg(false);
}
};
@@ -268,7 +281,9 @@ export default function CsdConfigPage() {
<p className="text-sm text-muted-foreground">
No hay organización configurada para este tenant.
</p>
<Button onClick={handleCreateOrg}>Crear Organización</Button>
<Button onClick={handleCreateOrg} disabled={creatingOrg}>
{creatingOrg ? 'Creando… (puede tardar unos segundos)' : 'Crear Organización'}
</Button>
</div>
) : (
<div className="space-y-2 text-sm">