feat: SAT sync improvements, XML export, and operational fixes
SAT sync enhancements: - Filter active (vigente) CFDIs only via DocumentStatus to avoid SAT rejecting recibidos with "No se permite descarga de XML cancelados" - Reclassify CFDIs at save time: tipo='ingreso' received by tenant becomes 'egreso' based on RFC (emisor vs receptor) - Fix pool cleanup bug during long syncs: refresh getPool() on each saveCfdis call instead of holding stale reference for 45+ minutes - Add X-View-Tenant support to SAT controller via viewingTenantId - Add tenantMiddleware to SAT routes for global admin impersonation Cron jobs: - Add separate every-6-hours schedule for specific RFCs - ROEM691011EZ4 configured for frequent sync (00, 06, 12, 18 MX time) XML filesystem export: - Write .xml files to /var/horux/xml/<RFC>/YYYY/MM/UUID.xml - Activated per-RFC via XML_EXPORT_RFCS allowlist - Organized by year/month for browsability Auth improvements: - Send welcome + admin-notification emails on /auth/register (previously only /tenants createTenant flow sent emails) - Set role='contador' for self-registered users (not admin) to prevent new tenants from accessing cross-tenant data Infrastructure: - Set express trust proxy=1 to accept X-Forwarded-For from Nginx (fixes ERR_ERL_UNEXPECTED_X_FORWARDED_FOR from rate limiter) Operational scripts: - setup-horux360-tenant.ts: Provision Horux 360 tenant manually - send-welcome-aaron.ts: Resend welcome email for Aaron (registered before welcome-on-register was added) - export-xmls-roem.ts: Backfill filesystem XMLs from DB for ROEM Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ import { isGlobalAdmin } from '../utils/global-admin.js';
|
||||
*/
|
||||
export async function start(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const tenantId = req.user!.tenantId;
|
||||
const tenantId = req.viewingTenantId || req.user!.tenantId;
|
||||
const { type, dateFrom, dateTo } = req.body as StartSyncRequest;
|
||||
|
||||
const jobId = await startSync(
|
||||
@@ -45,7 +45,7 @@ export async function start(req: Request, res: Response): Promise<void> {
|
||||
*/
|
||||
export async function status(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const tenantId = req.user!.tenantId;
|
||||
const tenantId = req.viewingTenantId || req.user!.tenantId;
|
||||
const syncStatus = await getSyncStatus(tenantId);
|
||||
res.json(syncStatus);
|
||||
} catch (error: any) {
|
||||
@@ -59,7 +59,7 @@ export async function status(req: Request, res: Response): Promise<void> {
|
||||
*/
|
||||
export async function history(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const tenantId = req.user!.tenantId;
|
||||
const tenantId = req.viewingTenantId || req.user!.tenantId;
|
||||
const page = parseInt(req.query.page as string) || 1;
|
||||
const limit = parseInt(req.query.limit as string) || 10;
|
||||
|
||||
@@ -80,7 +80,7 @@ export async function history(req: Request, res: Response): Promise<void> {
|
||||
*/
|
||||
export async function jobDetail(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const tenantId = req.user!.tenantId;
|
||||
const tenantId = req.viewingTenantId || req.user!.tenantId;
|
||||
const { id } = req.params;
|
||||
const { jobs } = await getSyncHistory(tenantId, 1, 100);
|
||||
const job = jobs.find(j => j.id === id);
|
||||
|
||||
Reference in New Issue
Block a user