From d9d03c6c27d85d4546c8ddefd15214e2cb569234 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 4 Aug 2025 10:06:04 -0600 Subject: [PATCH 1/5] Update __init__.py Fix: evitar uso recursivo de cursores en SQLite usando cursores locales --- satcfdi/catalogs/__init__.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/satcfdi/catalogs/__init__.py b/satcfdi/catalogs/__init__.py index 0009863..0c37863 100644 --- a/satcfdi/catalogs/__init__.py +++ b/satcfdi/catalogs/__init__.py @@ -8,18 +8,21 @@ db_file = os.path.join(current_dir, "catalogs.db") conn = sqlite3.connect(db_file, check_same_thread=False) -c = conn.cursor() def select(catalog_name, key): - c.execute(f"SELECT value FROM {catalog_name} WHERE key = ?", (pickle.dumps(key),)) - if ds := c.fetchone(): - return pickle.loads(ds[0]) + with conn: + c = conn.cursor() + c.execute(f"SELECT value FROM {catalog_name} WHERE key = ?", (pickle.dumps(key),)) + if ds := c.fetchone(): + return pickle.loads(ds[0]) def select_all(catalog_name): - c.execute(f"SELECT key, value FROM {catalog_name}") - return {pickle.loads(k): pickle.loads(v) for k, v in c.fetchall()} + with conn: + c = conn.cursor() + c.execute(f"SELECT key, value FROM {catalog_name}") + return {pickle.loads(k): pickle.loads(v) for k, v in c.fetchall()} def catalog_code(catalog_name, key, index=None): @@ -60,8 +63,10 @@ def split_at_upper_itr(word: str): def trans(k): - c.execute(f"SELECT value FROM Translations WHERE key = ?", (k,)) - if res := c.fetchone(): - return res[0] + with conn: + c = conn.cursor() + c.execute(f"SELECT value FROM Translations WHERE key = ?", (k,)) + if res := c.fetchone(): + return res[0] - return split_at_upper(k) + return split_at_upper(k) From 87732b308b3a0ce0984ed83d2546e3ba3633e7db Mon Sep 17 00:00:00 2001 From: Alejandro Date: Thu, 14 May 2026 06:01:28 -0600 Subject: [PATCH 2/5] fix: render impuesto exento rows in invoice PDF template When TipoFactor is 'Exento', the SAT spec does not include TasaOCuota or Importe attributes. The template was silently skipping these rows because the Importe cell rendered as empty/undefined. This fix adds a conditional check for TipoFactor == 'Exento' in both the per-concept tax breakdown and the totals section, displaying 'Exento' label instead of empty tasa/importe cells. --- satcfdi/render/templates/Conceptos.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/satcfdi/render/templates/Conceptos.html b/satcfdi/render/templates/Conceptos.html index 8b07fd3..586caef 100644 --- a/satcfdi/render/templates/Conceptos.html +++ b/satcfdi/render/templates/Conceptos.html @@ -51,8 +51,13 @@
Conceptos
{% if loop.first %}+ Traslados{% endif %} {{ v.Base }} {{ v.Impuesto | desc }} + {% if v.TipoFactor == 'Exento' %} + Exento + + {% else %} {{ tasa_cuota( v.TipoFactor, v.TasaOCuota) }} {{ v.Importe }} + {% endif %} {% endfor %} {% endif %} @@ -100,8 +105,13 @@
Conceptos
{% endif %} {{ v.Impuesto | desc }} + {% if v.TipoFactor == 'Exento' %} + Exento + + {% else %} {{ tasa_cuota( v.TipoFactor, v.TasaOCuota) }} {{ v.Importe }} + {% endif %} {% endfor %} {% endif %} From 333f9b8b1d1c8b00157c7ee8927eca0ce8509b30 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Thu, 14 May 2026 06:05:28 -0600 Subject: [PATCH 3/5] chore: sync catalogs/__init__.py with upstream main (resolve merge conflict) Accept upstream changes: use try/finally for cursor cleanup and pickle protocol=4 in select(). No logic changes from our fix branch. --- satcfdi/catalogs/__init__.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/satcfdi/catalogs/__init__.py b/satcfdi/catalogs/__init__.py index 0c37863..c0c5b93 100644 --- a/satcfdi/catalogs/__init__.py +++ b/satcfdi/catalogs/__init__.py @@ -11,18 +11,22 @@ def select(catalog_name, key): - with conn: - c = conn.cursor() - c.execute(f"SELECT value FROM {catalog_name} WHERE key = ?", (pickle.dumps(key),)) + c = conn.cursor() + try: + c.execute(f"SELECT value FROM {catalog_name} WHERE key = ?", (pickle.dumps(key, protocol=4),)) if ds := c.fetchone(): return pickle.loads(ds[0]) + finally: + c.close() def select_all(catalog_name): - with conn: - c = conn.cursor() + c = conn.cursor() + try: c.execute(f"SELECT key, value FROM {catalog_name}") return {pickle.loads(k): pickle.loads(v) for k, v in c.fetchall()} + finally: + c.close() def catalog_code(catalog_name, key, index=None): @@ -63,10 +67,11 @@ def split_at_upper_itr(word: str): def trans(k): - with conn: - c = conn.cursor() + c = conn.cursor() + try: c.execute(f"SELECT value FROM Translations WHERE key = ?", (k,)) if res := c.fetchone(): return res[0] - return split_at_upper(k) + finally: + c.close() From 804262e128a83e37bed5e6f3a781d72d5459dbb7 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Thu, 14 May 2026 06:01:28 -0600 Subject: [PATCH 4/5] fix: render impuesto exento rows in invoice PDF template When TipoFactor is 'Exento', the SAT spec does not include TasaOCuota or Importe attributes. The template was silently skipping these rows because the Importe cell rendered as empty/undefined. This fix adds a conditional check for TipoFactor == 'Exento' in both the per-concept tax breakdown and the totals section, displaying 'Exento' label instead of empty tasa/importe cells. --- satcfdi/render/templates/Conceptos.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/satcfdi/render/templates/Conceptos.html b/satcfdi/render/templates/Conceptos.html index 8b07fd3..586caef 100644 --- a/satcfdi/render/templates/Conceptos.html +++ b/satcfdi/render/templates/Conceptos.html @@ -51,8 +51,13 @@
Conceptos
{% if loop.first %}+ Traslados{% endif %} {{ v.Base }} {{ v.Impuesto | desc }} + {% if v.TipoFactor == 'Exento' %} + Exento + + {% else %} {{ tasa_cuota( v.TipoFactor, v.TasaOCuota) }} {{ v.Importe }} + {% endif %} {% endfor %} {% endif %} @@ -100,8 +105,13 @@
Conceptos
{% endif %} {{ v.Impuesto | desc }} + {% if v.TipoFactor == 'Exento' %} + Exento + + {% else %} {{ tasa_cuota( v.TipoFactor, v.TasaOCuota) }} {{ v.Importe }} + {% endif %} {% endfor %} {% endif %} From de0828c4b7e57f5a9d544ce77e1a8df949637c49 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Thu, 14 May 2026 06:05:28 -0600 Subject: [PATCH 5/5] chore: sync catalogs/__init__.py with upstream main (resolve merge conflict) Accept upstream changes: use try/finally for cursor cleanup and pickle protocol=4 in select(). No logic changes from our fix branch. --- satcfdi/catalogs/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/satcfdi/catalogs/__init__.py b/satcfdi/catalogs/__init__.py index 53b272f..f89ef75 100644 --- a/satcfdi/catalogs/__init__.py +++ b/satcfdi/catalogs/__init__.py @@ -74,6 +74,4 @@ def trans(k): return res[0] return split_at_upper(k) finally: - c.close() - - + c.close() \ No newline at end of file