#!/usr/bin/env python3 """Process pending CFDI stamping queues for all tenants (Facturapi). Called by systemd timer every 2 minutes so pending invoices stamp without anyone pressing a button. Each round first enqueues the missing payment complements (REP) of PPD invoices and the credit notes of returns. """ import os import sys sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'pos')) import psycopg2 # noqa: E402 MASTER_DB = os.environ.get('MASTER_DB_NAME', 'nexus_autoparts') def main(): from services.cfdi_queue import enqueue_pending_complements, process_queue master = psycopg2.connect(dbname=MASTER_DB) mcur = master.cursor() mcur.execute("SELECT id, db_name FROM tenants WHERE is_active = true ORDER BY id") tenants = mcur.fetchall() mcur.close() master.close() for tenant_id, db_name in tenants: try: conn = psycopg2.connect(dbname=db_name) cur = conn.cursor() cur.execute(""" SELECT key, value FROM tenant_config WHERE key IN ('cfdi_facturapi_key', 'cfdi_facturapi_org_id', 'facturapi_secret_key', 'facturapi_org_id') """) tcfg = {k: v for k, v in cur.fetchall()} cur.close() if not tcfg.get('cfdi_facturapi_key') and not tcfg.get('facturapi_secret_key'): conn.close() continue # tenant has no CFDI configured # Enqueue missing payment complements (REP) and credit notes first; # they only need the tenant DB, not the Facturapi API. try: enqueue_pending_complements(conn) except Exception as e: conn.rollback() print(f'[{db_name}] complement sync ERROR: {str(e)[:200]}', flush=True) # Quick check: are there pending items? cur = conn.cursor() cur.execute("SELECT count(*) FROM cfdi_queue WHERE status IN ('pending','failed')") pending = cur.fetchone()[0] cur.close() if not pending: conn.close() continue result = process_queue(conn, tcfg) conn.close() print(f'[{db_name}] processed={result.get("processed")} ' f'stamped={result.get("stamped")} failed={result.get("failed")}', flush=True) except Exception as e: print(f'[{db_name}] ERROR: {str(e)[:200]}', flush=True) print('CFDI queue round complete.', flush=True) if __name__ == '__main__': main()