diff --git a/app/db/normalizations.py b/app/db/normalizations.py index 0ea21ec..d91803c 100644 --- a/app/db/normalizations.py +++ b/app/db/normalizations.py @@ -47,6 +47,7 @@ def normalize_customer_fields(fd: Dict[str, Any]) -> Dict[str, Any]: "is_company": config["CTE_IS_COMPANY"], "tin": clean_tin(str(fd.get("NIF_CIF"))), "name": str(fd.get("NOMBRE")), + "street": str(fd.get("CALLE")), "city": str(fd.get("POBLACION")), "province": str(fd.get("PROVINCIA")), @@ -96,6 +97,9 @@ def normalize_header_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]: "company_id": config['CTE_COMPANY_ID'], "is_proforma": config["CTE_IS_PROFORMA"], "status": config["CTE_STATUS_INVOICE"], + + # + "series": config['CTE_SERIE'], "factuges_id": int(fd['ID_FACTURA']), diff --git a/app/db/sql_sentences.py b/app/db/sql_sentences.py index e6f4907..8a80fc5 100644 --- a/app/db/sql_sentences.py +++ b/app/db/sql_sentences.py @@ -128,7 +128,7 @@ SELECT_FACTUGES_FACTURAS_CLIENTE = ( "WHERE " "(fac.VERIFACTU = 1) " "AND (fac.ID_VERIFACTU is null)" - "ORDER BY (fac.ID)" + "ORDER BY (fac.ID, facdet.POSICION)" ) UPDATE_FACTUGES_LINK = ( diff --git a/app/db/sync_invoices_factuges.py b/app/db/sync_invoices_factuges.py index 4f85ae6..51546f0 100644 --- a/app/db/sync_invoices_factuges.py +++ b/app/db/sync_invoices_factuges.py @@ -3,7 +3,9 @@ from typing import Any, Dict from uuid6 import uuid7 from app.config import load_config, logger -from app.utils import validar_nif +from app.utils import ( + validar_nif_verifacti, +) from . import normalizations as NORMALIZA from . import sql_sentences as SQL @@ -130,7 +132,7 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config): # Validamos que el cif de la factura exista en la AEAT si no es así no se hace la sincro de la factura sync_result = int(config["CTE_SYNC_RESULT_OK"]) sync_notes = None - customer_valid = validar_nif( + customer_valid = validar_nif_verifacti( customer_fields["tin"], customer_fields["name"], config ) if customer_valid: @@ -141,7 +143,6 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config): str(factura_detalle["ID_CLIENTE"]), customer_fields, ) - # ---- forma de pago pm_id = get_or_create_payment_method( cursorMySQL, @@ -161,14 +162,12 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config): str(factura_detalle["DES_FORMA_PAGO"]), config, ) - # ---- impuestos cabecera insert_header_taxes_if_any( cursorMySQL, invoice_id, header_invoice_fields, ) - # ---- registro verifactu insert_verifactu_record( cursorMySQL, header_invoice_fields, invoice_id, config diff --git a/app/db/sync_invoices_verifactu.py b/app/db/sync_invoices_verifactu.py index ff59d65..712d4a0 100644 --- a/app/db/sync_invoices_verifactu.py +++ b/app/db/sync_invoices_verifactu.py @@ -1,7 +1,12 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple from app.config import load_config, logger -from app.utils import TaxCatalog, crear_factura, estado_factura, unscale_to_str +from app.utils import ( + TaxCatalog, + crear_factura_verifacti, + estado_factura, + unscale_to_str, +) from . import sql_sentences as SQL @@ -115,7 +120,7 @@ def procesar_factura_verifactu( # Creamos registro de factura en verifactu if factura.get('uuid') == '': # logger.info(f"Send to create Verifactu: {factura}") - respuesta = crear_factura(factura, config) + respuesta = crear_factura_verifacti(factura, config) if respuesta.get("status") == 200 and respuesta.get("ok"): data = respuesta.get("data") or {} diff --git a/app/utils/__init__.py b/app/utils/__init__.py index bea47b6..3fdb2d7 100644 --- a/app/utils/__init__.py +++ b/app/utils/__init__.py @@ -12,8 +12,17 @@ from .last_execution_helper import ( ) from .mails_helper import corregir_y_validar_email from .password import hashPassword +from .payloads_factuges import ( + create_invoice_header, + insert_item_and_taxes, +) from .send_orders_mail import send_orders_mail -from .send_rest_api import crear_factura, estado_factura, validar_nif +from .send_rest_api import ( + crear_factura_verifacti, + estado_factura, + insert_invoice_REST_API_FACTUGES, + validar_nif_verifacti, +) from .tax_catalog_helper import ( TaxCatalog, calc_item_tax_amount, diff --git a/app/utils/send_rest_api.py b/app/utils/send_rest_api.py index 762510d..37f2775 100644 --- a/app/utils/send_rest_api.py +++ b/app/utils/send_rest_api.py @@ -60,9 +60,9 @@ def estado_factura(uuid_str: str, return {"ok": False, "status": 500, "error": "Respuesta no es JSON válido"} -def crear_factura(payload, - config, - ) -> Dict[str, Any]: +def crear_factura_verifacti(payload, + config, + ) -> Dict[str, Any]: """ Llama al endpoint de Verifacti para crear una factura. @@ -115,7 +115,7 @@ def crear_factura(payload, return {"ok": False, "status": 500, "error": "Respuesta no es JSON válido"} -def validar_nif( +def validar_nif_verifacti( nif: str, nombre: str, config, @@ -157,3 +157,58 @@ def validar_nif( except ValueError as e: logger.error("Respuesta no es JSON válido: %s", e) return False, None, "Respuesta no es JSON válido" + + +def insert_invoice_REST_API_FACTUGES( + payload +) -> Dict[str, Any]: + """ + Llama a la REST API de FACTUGES para insertar una proforma aprobada. + + Retorna: + + """ + url = "http://192.168.0.104:3002/api/v1/proformas" + timeout: int = 10 + headers = {"Content-Type": "application/json", + "Accept": "application/json"} + """headers["Authorization"] = "Bearer " + config['VERIFACTU_NIFS_API_KEY'] + payload = {"nif": nif, + "nombre": nombre, + } + """ + try: + resp = requests.post( + url, json=payload, headers=headers, timeout=timeout) + + if resp.status_code == 200: + try: + data = resp.json() + logger.info(data) + except ValueError: + return {"ok": False, "status": 200, "error": "Respuesta 200 sin JSON válido", "raw": resp.text} + return {"ok": True, "status": 200, "data": data} + + if resp.status_code == 400: + try: + body = resp.json() + msg = body.get( + "error") or "Error de validación (400) sin detalle" + except ValueError: + msg = f"Error de validación (400): {resp.text}" + return {"ok": False, "status": 400, "error": msg} + + # Otros códigos: devuelve mensaje genérico, intenta extraer JSON si existe + try: + body = resp.json() + msg = body.get("error") or body + return {"ok": False, "status": resp.status_code, "error": str(msg)} + except ValueError: + return {"ok": False, "status": resp.status_code, "error": f"HTTP {resp.status_code}", "raw": resp.text} + + except requests.RequestException as e: + logger.error("Error de conexión con la API Verifacti: %s", e) + return {"ok": False, "status": 500, "error": str(e)} + except ValueError as e: + logger.error("Respuesta no es JSON válido: %s", e) + return {"ok": False, "status": 500, "error": "Respuesta no es JSON válido"}