Subida a producción

This commit is contained in:
David Arranz 2025-11-30 19:28:44 +01:00
parent 973391ec68
commit 51bd9fe7af
33 changed files with 339 additions and 232 deletions

View File

@ -56,7 +56,7 @@ Esto crea el comando factuges-sync disponible en tu entorno virtual.
El código carga automáticamente: El código carga automáticamente:
* environment/dev.env cuando ENV=dev * environment/dev.env cuando ENV=dev
* Variables del contenedor en producción (ENV=prod) * Variables del contenedor en producción (ENV=production)
Por tanto, **en desarrollo**: Por tanto, **en desarrollo**:
@ -122,5 +122,5 @@ Build:
Cron job: Cron job:
```bash ```bash
*/5 * * * * docker run --rm -e ENV=prod -e SYNC_MODE=factuges myimage:latest */5 * * * * docker run --rm -e ENV=production -e SYNC_MODE=factuges myimage:latest
``` ```

View File

@ -1 +1 @@
2025-11-30 09:07:15 2025-11-30 18:28:15

View File

@ -1 +0,0 @@
__version__ = "1.0.0"

View File

@ -1 +0,0 @@
__version__ = "1.0.0"

View File

@ -28,7 +28,7 @@ def main():
print("Error: debes indicar modo: 'factuges' o 'verifactu'") print("Error: debes indicar modo: 'factuges' o 'verifactu'")
sys.exit(1) sys.exit(1)
if os.getenv("ENV") == "dev": if os.getenv("ENV") == "developement":
print("Running in development mode (no docker)") print("Running in development mode (no docker)")
module = ( module = (

View File

@ -1,3 +1,4 @@
from .settings import load_config from .settings import load_config, log_config
from .setup_logging import setup_logging from .version import get_package_version
from .setup_brevo import setup_brevo from .setup_logger import create_logger, logger
from .setup_brevo import setup_brevo

View File

@ -1,7 +1,7 @@
import logging
import os import os
from os.path import join, dirname from os.path import join, dirname
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
from .setup_logger import logger
from dotenv import load_dotenv from dotenv import load_dotenv
@ -23,16 +23,16 @@ def load_config() -> Dict[str, Any]:
Carga la configuración desde variables de entorno. Carga la configuración desde variables de entorno.
- En dev: carga dev.env y luego valida. - En dev: carga dev.env y luego valida.
- En prod: NO carga ningún .env, solo usa entorno del sistema/contendor. - En production: NO carga ningún .env, solo usa entorno del sistema/contendor.
- Si falta alguna variable requerida -> RuntimeError. - Si falta alguna variable requerida -> RuntimeError.
""" """
env = os.getenv("ENV", "dev") env = os.getenv("ENV", "development")
if env == "dev": if env == "development":
dotenv_path = join(dirname(__file__), "../../enviroment/dev.env") dotenv_path = join(dirname(__file__), "../../enviroment/dev.env")
load_dotenv(dotenv_path) load_dotenv(dotenv_path)
elif env == "prod": elif env == "production":
# En producción NO se carga archivo .env # En producción NO se carga archivo .env
# Las variables vienen del contenedor (docker run -e VAR=...) # Las variables vienen del contenedor (docker run -e VAR=...)
pass pass
@ -42,7 +42,6 @@ def load_config() -> Dict[str, Any]:
config: Dict[str, Any] = { config: Dict[str, Any] = {
# Opcionales (con valor por defecto) # Opcionales (con valor por defecto)
"ENV": env, "ENV": env,
"ENVIRONMENT": os.getenv("ENVIRONMENT", env),
"LOCAL_TZ": os.getenv("LOCAL_TZ", "Europe/Madrid"), "LOCAL_TZ": os.getenv("LOCAL_TZ", "Europe/Madrid"),
"LAST_RUN_PATH": _required("LAST_RUN_PATH"), "LAST_RUN_PATH": _required("LAST_RUN_PATH"),
@ -54,11 +53,11 @@ def load_config() -> Dict[str, Any]:
"FACTUGES_PASSWORD": _required("FACTUGES_PASSWORD"), "FACTUGES_PASSWORD": _required("FACTUGES_PASSWORD"),
# UECKO MySQL (requeridas salvo puerto) # UECKO MySQL (requeridas salvo puerto)
"UECKO_MYSQL_HOST": _required("UECKO_MYSQL_HOST"), "FWEB_MYSQL_HOST": _required("FWEB_MYSQL_HOST"),
"UECKO_MYSQL_PORT": os.getenv("UECKO_MYSQL_PORT", "3306"), "FWEB_MYSQL_PORT": os.getenv("FWEB_MYSQL_PORT", "3306"),
"UECKO_MYSQL_DATABASE": _required("UECKO_MYSQL_DATABASE"), "FWEB_MYSQL_DATABASE": _required("FWEB_MYSQL_DATABASE"),
"UECKO_MYSQL_USER": _required("UECKO_MYSQL_USER"), "FWEB_MYSQL_USER": _required("FWEB_MYSQL_USER"),
"UECKO_MYSQL_PASSWORD": _required("UECKO_MYSQL_PASSWORD"), "FWEB_MYSQL_PASSWORD": _required("FWEB_MYSQL_PASSWORD"),
# Constantes/CTE (requeridas) # Constantes/CTE (requeridas)
"CTE_COMPANY_ID": _required("CTE_COMPANY_ID"), "CTE_COMPANY_ID": _required("CTE_COMPANY_ID"),
@ -79,3 +78,14 @@ def load_config() -> Dict[str, Any]:
} }
return config return config
def log_config(config: dict) -> None:
"""Loguea la configuración con una clave por línea."""
lines = ["Loaded configuration:"]
for key in sorted(config.keys()):
value = config[key]
if any(t in key.lower() for t in ("pass", "password", "api_key", "token", "secret")):
value = "***"
lines.append(f" {key} = {value!r}")
logger.info("\n".join(lines))

View File

@ -0,0 +1,55 @@
import logging
import sys
import os
from logging.handlers import RotatingFileHandler
from pathlib import Path
def create_logger(
name: str = "factuges-sync",
*,
level: int = logging.INFO,
log_path: str | None = None,
) -> logging.Logger:
"""
Crea un logger:
- SIEMPRE stdout (Docker-friendly)
- SOLO EN PRODUCCIÓN añade RotatingFileHandler si log_path no es None
"""
logger = logging.getLogger(name)
logger.setLevel(level)
# No duplicar handlers si ya existe el logger
if logger.handlers:
return logger
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
# ------------------------------------------------------------------
# 1) Consola → SIEMPRE (para docker logs)
# ------------------------------------------------------------------
h_console = logging.StreamHandler(sys.stdout)
h_console.setFormatter(formatter)
logger.addHandler(h_console)
# ------------------------------------------------------------------
# 2) Fichero → SOLO en prod + si se define log_path
# ------------------------------------------------------------------
environment = os.getenv("ENV", "development")
if environment == "production" and log_path:
Path(log_path).parent.mkdir(parents=True, exist_ok=True)
h_file = RotatingFileHandler(
log_path,
maxBytes=5 * 1024 * 1024,
backupCount=15,
encoding="utf8",
)
h_file.setFormatter(formatter)
logger.addHandler(h_file)
return logger
# logger "global" ya creado
logger = create_logger()

View File

@ -1,18 +0,0 @@
import logging
import sys
from logging.handlers import RotatingFileHandler
def setup_logging():
logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def setup_rotating_logging(log_path):
# Rotación de logs con un tamaño máximo de 5 MB y mantiene 15 archivos de backup
handler = RotatingFileHandler(log_path, maxBytes=5*1024*1024, backupCount=15, encoding="utf8")
# Configuración básica de logging
logging.basicConfig(
handlers=[handler],
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)

8
app/config/version.py Normal file
View File

@ -0,0 +1,8 @@
from importlib.metadata import version, PackageNotFoundError
def get_package_version() -> str:
try:
return version("factuges-sync") # nombre del paquete en [metadata].name
except PackageNotFoundError:
return "unknown"

View File

@ -1,6 +1,6 @@
import fdb import fdb
import mysql.connector import mysql.connector
import logging from app.config import logger
def get_factuges_connection(config): def get_factuges_connection(config):
@ -13,32 +13,32 @@ def get_factuges_connection(config):
password=config['FACTUGES_PASSWORD'], password=config['FACTUGES_PASSWORD'],
charset='UTF8' charset='UTF8'
) )
logging.info( logger.info(
f"Conexión a la base de datos FactuGES establecida: {config['FACTUGES_HOST']} with database:{config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}") f"Conexión a la base de datos FactuGES establecida: {config['FACTUGES_HOST']} with database:{config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}")
return conn return conn
except Exception as e: except Exception as e:
logging.error("Error al conectar a la base de datos FactuGES.") logger.error("Error al conectar a la base de datos FactuGES.")
logging.error( logger.error(
f"(ERROR) Failed to establish connection to: {config['FACTUGES_HOST']} with database:{config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}") f"(ERROR) Failed to establish connection to: {config['FACTUGES_HOST']} with database:{config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}")
logging.error(str(e)) logger.error(str(e))
raise e raise e
def get_mysql_connection(config): def get_mysql_connection(config):
try: try:
conn = mysql.connector.connect( conn = mysql.connector.connect(
host=config['UECKO_MYSQL_HOST'], host=config['FWEB_MYSQL_HOST'],
port=config['UECKO_MYSQL_PORT'], port=config['FWEB_MYSQL_PORT'],
database=config['UECKO_MYSQL_DATABASE'], database=config['FWEB_MYSQL_DATABASE'],
user=config['UECKO_MYSQL_USER'], user=config['FWEB_MYSQL_USER'],
password=config['UECKO_MYSQL_PASSWORD'] password=config['FWEB_MYSQL_PASSWORD']
) )
logging.info( logger.info(
f"Conexión a la base de datos MySQL establecida a: {config['UECKO_MYSQL_HOST']} with database:{config['UECKO_MYSQL_DATABASE']} - using user:{config['UECKO_MYSQL_USER']}") f"Conexión a la base de datos MySQL establecida a: {config['FWEB_MYSQL_HOST']} with database:{config['FWEB_MYSQL_DATABASE']} - using user:{config['FWEB_MYSQL_USER']}")
return conn return conn
except Exception as e: except Exception as e:
logging.error("Error al conectar a la base de datos MySQL.") logger.error("Error al conectar a la base de datos MySQL.")
logging.error( logger.error(
f"(ERROR) Failed to establish connection to: {config['UECKO_MYSQL_HOST']} with database:{config['UECKO_MYSQL_DATABASE']} - using user:{config['UECKO_MYSQL_USER']}") f"(ERROR) Failed to establish connection to: {config['FWEB_MYSQL_HOST']} with database:{config['FWEB_MYSQL_DATABASE']} - using user:{config['FWEB_MYSQL_USER']}")
logging.error(str(e)) logger.error(str(e))
raise e raise e

View File

@ -1,4 +1,4 @@
import logging from app.config import logger
from datetime import date from datetime import date
from app.config import load_config from app.config import load_config
import textwrap import textwrap

View File

@ -1,4 +1,4 @@
import logging from app.config import logger
from typing import Dict, Any from typing import Dict, Any
from uuid6 import uuid7 from uuid6 import uuid7
from app.config import load_config from app.config import load_config
@ -21,20 +21,20 @@ def sync_invoices_factuges(conn_factuges, conn_mysql, last_execution_date):
# Crear un conjunto con los IDs [0] de los customer_inovices que debo liberar en FactuGES, porque han sido eliminadas en programa de facturación nuevo # Crear un conjunto con los IDs [0] de los customer_inovices que debo liberar en FactuGES, porque han sido eliminadas en programa de facturación nuevo
ids_verifactu_deleted = {str(fila[0]) for fila in filas} ids_verifactu_deleted = {str(fila[0]) for fila in filas}
# logging.info(f"Customer invoices rows to be deleted: {len(ids_verifactu_deleted)}") # logger.info(f"Customer invoices rows to be deleted: {len(ids_verifactu_deleted)}")
# Verificar si hay filas en el resultado # Verificar si hay filas en el resultado
if ids_verifactu_deleted: if ids_verifactu_deleted:
sync_delete_invoices(conn_factuges, ids_verifactu_deleted, config) sync_delete_invoices(conn_factuges, ids_verifactu_deleted, config)
else: else:
logging.info( logger.info(
f"There are NOT customer invoices deleted since the last run") f"There are NOT customer invoices deleted since the last run")
except Exception as e: except Exception as e:
if cursor_mysql is not None: if cursor_mysql is not None:
cursor_mysql.close() cursor_mysql.close()
logging.error(f"(ERROR) Failed to fetch from database:{ logger.error(f"(ERROR) Failed to fetch from database:{
config['UECKO_MYSQL_DATABASE']} - using user:{config['UECKO_MYSQL_USER']}") config['FWEB_MYSQL_DATABASE']} - using user:{config['FWEB_MYSQL_USER']}")
logging.error(e) logger.error(e)
raise e raise e
# BUSCAMOS FACTURAS ENVIADAS A VERIFACTU EN FACTUGES, PARA SUBIRLAS AL NUEVO PROGRAMA DE FACTURACIÓN # BUSCAMOS FACTURAS ENVIADAS A VERIFACTU EN FACTUGES, PARA SUBIRLAS AL NUEVO PROGRAMA DE FACTURACIÓN
@ -48,9 +48,9 @@ def sync_invoices_factuges(conn_factuges, conn_mysql, last_execution_date):
except Exception as e: except Exception as e:
if cursor_FactuGES is not None: if cursor_FactuGES is not None:
cursor_FactuGES.close() cursor_FactuGES.close()
logging.error(f"(ERROR) Failed to fetch from database:{ logger.error(f"(ERROR) Failed to fetch from database:{
config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}") config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}")
logging.error(e) logger.error(e)
raise e raise e
# Obtener los nombres de las columnas # Obtener los nombres de las columnas
@ -67,7 +67,7 @@ def sync_invoices_factuges(conn_factuges, conn_mysql, last_execution_date):
sync_invoices_from_FACTUGES( sync_invoices_from_FACTUGES(
conn_mysql, tuplas_seleccionadas, conn_factuges, config) conn_mysql, tuplas_seleccionadas, conn_factuges, config)
else: else:
logging.info( logger.info(
"There are NOT new FACTURAS rows since the last run.") "There are NOT new FACTURAS rows since the last run.")
@ -78,15 +78,15 @@ def sync_delete_invoices(conn_factuges, ids_verifactu_deleted, config):
try: try:
cursor_FactuGES = conn_factuges.cursor() cursor_FactuGES = conn_factuges.cursor()
if ids_verifactu_deleted: if ids_verifactu_deleted:
logging.info(f"Liberate factuGES: {ids_verifactu_deleted}") logger.info(f"Liberate factuGES: {ids_verifactu_deleted}")
cursor_FactuGES.executemany(SQL.LIMPIAR_FACTUGES_LINK, [( cursor_FactuGES.executemany(SQL.LIMPIAR_FACTUGES_LINK, [(
id_verifactu,) for id_verifactu in ids_verifactu_deleted]) id_verifactu,) for id_verifactu in ids_verifactu_deleted])
else: else:
logging.info("No articles to delete.") logger.info("No articles to delete.")
except Exception as e: except Exception as e:
# Escribir el error en el archivo de errores # Escribir el error en el archivo de errores
logging.error(str(e)) logger.error(str(e))
raise e # Re-lanzar la excepción para detener el procesamiento raise e # Re-lanzar la excepción para detener el procesamiento
finally: finally:
# Cerrar la conexión # Cerrar la conexión
@ -96,7 +96,7 @@ def sync_delete_invoices(conn_factuges, ids_verifactu_deleted, config):
def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config): def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config):
# Insertaremos cada factura existente en las filas a la nueva estructura de tablas del programa nuevo de facturacion. # Insertaremos cada factura existente en las filas a la nueva estructura de tablas del programa nuevo de facturacion.
# logging.info(f"FACTURAS_CLIENTE_DETALLE rows to be processed: {len(filas)}") # logger.info(f"FACTURAS_CLIENTE_DETALLE rows to be processed: {len(filas)}")
cursorMySQL = None cursorMySQL = None
cursor_FactuGES = None cursor_FactuGES = None
@ -163,10 +163,10 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config):
sync_notes = (f">>> Factura {header_invoice_fields['reference']} no cumple requisitos para ser mandada a Verifactu: " sync_notes = (f">>> Factura {header_invoice_fields['reference']} no cumple requisitos para ser mandada a Verifactu: "
f">>>>>> El NIF/NOMBRE ({customer_fields['tin']}/{customer_fields['name']}) no está registrado en la AEAT. " f">>>>>> El NIF/NOMBRE ({customer_fields['tin']}/{customer_fields['name']}) no está registrado en la AEAT. "
f"El NIF/CIF debe estar registrado en la AEAT y el nombre debe ser suficientemente parecido al nombre registrado en la AEAT") f"El NIF/CIF debe estar registrado en la AEAT y el nombre debe ser suficientemente parecido al nombre registrado en la AEAT")
logging.info(sync_notes) logger.info(sync_notes)
# Guardamos en Factuges el id de la customer_invoice # Guardamos en Factuges el id de la customer_invoice
logging.info( logger.info(
f"Updating FACTURAS_CLIENTE {sync_result} {invoice_id} {factuges_id} {sync_notes}") f"Updating FACTURAS_CLIENTE {sync_result} {invoice_id} {factuges_id} {sync_notes}")
cursor_FactuGES.execute(SQL.UPDATE_FACTUGES_LINK, (sync_result, invoice_id, sync_notes, factuges_id)) cursor_FactuGES.execute(SQL.UPDATE_FACTUGES_LINK, (sync_result, invoice_id, sync_notes, factuges_id))
num_fac_procesed += 1 num_fac_procesed += 1
@ -178,12 +178,12 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config):
# Asignamos el id factura anterior para no volver a inserta cabecera # Asignamos el id factura anterior para no volver a inserta cabecera
factuges_id_anterior = factuges_id factuges_id_anterior = factuges_id
logging.info( logger.info(
f"FACTURAS_CLIENTE rows to be processed: {str(num_fac_procesed)}") f"FACTURAS_CLIENTE rows to be processed: {str(num_fac_procesed)}")
except Exception as e: except Exception as e:
# Escribir el error en el archivo de errores # Escribir el error en el archivo de errores
logging.error(str(e)) logger.error(str(e))
raise e # Re-lanzar la excepción para detener el procesamiento raise e # Re-lanzar la excepción para detener el procesamiento
finally: finally:
@ -202,8 +202,8 @@ def get_or_create_customer(cur, company_id: str, factuges_customer_id: str, fiel
row = cur.fetchone() row = cur.fetchone()
if not row or not row[0]: if not row or not row[0]:
customer_id = str(uuid7()) customer_id = str(uuid7())
logging.info("Inserting customer %s %s %s", logger.info("Inserting customer %s %s %s",
factuges_customer_id, fields["tin"], fields["name"]) factuges_customer_id, fields["tin"], fields["name"])
cur.execute( cur.execute(
SQL.INSERT_CUSTOMER, SQL.INSERT_CUSTOMER,
( (
@ -215,7 +215,7 @@ def get_or_create_customer(cur, company_id: str, factuges_customer_id: str, fiel
) )
return customer_id return customer_id
customer_id = str(row[0]) customer_id = str(row[0])
logging.info("Updating customer %s %s", factuges_customer_id, customer_id) logger.info("Updating customer %s %s", factuges_customer_id, customer_id)
cur.execute( cur.execute(
SQL.UPDATE_CUSTOMER, SQL.UPDATE_CUSTOMER,
( (
@ -236,13 +236,13 @@ def get_or_create_payment_method(cur, factuges_payment_id: str, description: str
row = cur.fetchone() row = cur.fetchone()
if not row or not row[0]: if not row or not row[0]:
pm_id = str(uuid7()) pm_id = str(uuid7())
logging.info("Inserting payment method %s %s %s", logger.info("Inserting payment method %s %s %s",
factuges_payment_id, pm_id, description) factuges_payment_id, pm_id, description)
cur.execute(SQL.INSERT_PAYMENT_METHOD, cur.execute(SQL.INSERT_PAYMENT_METHOD,
(pm_id, description, factuges_payment_id)) (pm_id, description, factuges_payment_id))
return pm_id return pm_id
pm_id = str(row[0]) pm_id = str(row[0])
logging.info("Payment method exists %s -> %s", factuges_payment_id, pm_id) logger.info("Payment method exists %s -> %s", factuges_payment_id, pm_id)
return pm_id return pm_id
@ -253,8 +253,8 @@ def insert_invoice_header(cur: str, cf: Dict[str, Any], hif: Dict[str, Any], cus
""" """
invoice_id = str(uuid7()) invoice_id = str(uuid7())
logging.info("Inserting invoice %s %s %s %s %s", logger.info("Inserting invoice %s %s %s %s %s",
invoice_id, hif.get('reference'), hif.get('invoice_date'), hif.get('operation_date'), config['CTE_STATUS_INVOICE']) invoice_id, hif.get('reference'), hif.get('invoice_date'), hif.get('operation_date'), config['CTE_STATUS_INVOICE'])
cur.execute( cur.execute(
SQL.INSERT_INVOICE, SQL.INSERT_INVOICE,
( (
@ -279,8 +279,8 @@ def insert_verifactu_record(cur: str, hif: Dict[str, Any], invoice_id: str, conf
""" """
id = str(uuid7()) id = str(uuid7())
logging.info("Inserting verifactu record %s %s %s", logger.info("Inserting verifactu record %s %s %s",
id, hif.get('reference'), hif.get('invoice_date')) id, hif.get('reference'), hif.get('invoice_date'))
cur.execute( cur.execute(
SQL.INSERT_VERIFACTU_RECORD, SQL.INSERT_VERIFACTU_RECORD,
( (
@ -311,7 +311,7 @@ def insert_item_and_taxes(cur, invoice_id: str, fields: Dict[str, Any]) -> None:
""" """
item_id = str(uuid7()) item_id = str(uuid7())
# logging.info("Inserting item %s pos=%s qty=%s", item_id, fields.get('position'), fields.get('quantity_value')) # logger.info("Inserting item %s pos=%s qty=%s", item_id, fields.get('position'), fields.get('quantity_value'))
cur.execute( cur.execute(
SQL.INSERT_INVOICE_ITEM, SQL.INSERT_INVOICE_ITEM,
(item_id, invoice_id, fields.get('position'), fields.get('description'), fields.get('quantity_value'), (item_id, invoice_id, fields.get('position'), fields.get('description'), fields.get('quantity_value'),
@ -321,7 +321,7 @@ def insert_item_and_taxes(cur, invoice_id: str, fields: Dict[str, Any]) -> None:
) )
) )
# logging.info("Inserting item tax %s code=%s base=%s tax=%s", # logger.info("Inserting item tax %s code=%s base=%s tax=%s",
# item_id, fields.get('tax_code'), fields.get('total_value'), fields.get('tax_amount')) # item_id, fields.get('tax_code'), fields.get('total_value'), fields.get('tax_amount'))
# cur.execute( # cur.execute(
# SQL.INSERT_INVOICE_ITEM_TAX, (str(uuid7()), item_id, fields.get('tax_code'), # SQL.INSERT_INVOICE_ITEM_TAX, (str(uuid7()), item_id, fields.get('tax_code'),

View File

@ -1,4 +1,4 @@
import logging from app.config import logger
from typing import Dict, Any, Tuple, Optional, List, Iterable from typing import Dict, Any, Tuple, Optional, List, Iterable
from app.config import load_config from app.config import load_config
from decimal import Decimal from decimal import Decimal
@ -26,22 +26,22 @@ def sync_invoices_verifactu(conn_mysql, last_execution_date):
tuplas_seleccionadas.append(tupla) tuplas_seleccionadas.append(tupla)
# invoices_to_verifactu = {str(fila[0]) for fila in filas} # invoices_to_verifactu = {str(fila[0]) for fila in filas}
logging.info( logger.info(
f"Customer invoices rows to be send Verifactu: {len(tuplas_seleccionadas)}") f"Customer invoices rows to be send Verifactu: {len(tuplas_seleccionadas)}")
# Verificar si hay filas en el resultado # Verificar si hay filas en el resultado
if tuplas_seleccionadas: if tuplas_seleccionadas:
enviar_datos(tuplas_seleccionadas, cursor_mysql, config) enviar_datos(tuplas_seleccionadas, cursor_mysql, config)
logging.info(f"Ok send Verifactu") logger.info(f"Ok send Verifactu")
else: else:
logging.info(f"There are no rows to send") logger.info(f"There are no rows to send")
except Exception as error: except Exception as error:
if cursor_mysql is not None: if cursor_mysql is not None:
cursor_mysql.close() cursor_mysql.close()
logging.error( logger.error(
f"(ERROR) Failed to fetch from database:{config['UECKO_MYSQL_DATABASE']} - using user:{config['UECKO_MYSQL_USER']}") f"(ERROR) Failed to fetch from database:{config['FWEB_MYSQL_DATABASE']} - using user:{config['FWEB_MYSQL_USER']}")
logging.error(error) logger.error(error)
raise error raise error
@ -61,9 +61,9 @@ def enviar_datos(invoices_to_verifactu, cursor_mysql, config):
# preparamos nueva factura # preparamos nueva factura
ok, respuesta = preparar_factura(fila) ok, respuesta = preparar_factura(fila)
if not ok: if not ok:
logging.info( logger.info(
f"ERROR >>>>>> Factura {fila['reference']} no cumple requisitos para ser mandada a Verifactu:") f"ERROR >>>>>> Factura {fila['reference']} no cumple requisitos para ser mandada a Verifactu:")
logging.info( logger.info(
f">>>>>> Faltan campos requeridos: {respuesta}") f">>>>>> Faltan campos requeridos: {respuesta}")
factura = None factura = None
continue continue
@ -71,9 +71,9 @@ def enviar_datos(invoices_to_verifactu, cursor_mysql, config):
ok, linea = preparar_linea(fila) ok, linea = preparar_linea(fila)
if not ok: if not ok:
logging.info( logger.info(
f"ERROR >>>>>> Factura {factura.get('reference')} no cumple requisitos para ser mandada a Verifactu:") f"ERROR >>>>>> Factura {factura.get('reference')} no cumple requisitos para ser mandada a Verifactu:")
logging.info(f">>>>>> Faltan campos requeridos: {linea}") logger.info(f">>>>>> Faltan campos requeridos: {linea}")
factura = None factura = None
else: else:
factura["lineas"].append(linea) factura["lineas"].append(linea)
@ -82,7 +82,7 @@ def enviar_datos(invoices_to_verifactu, cursor_mysql, config):
except Exception as e: except Exception as e:
# Escribir el error en el archivo de errores # Escribir el error en el archivo de errores
logging.error(str(e)) logger.error(str(e))
raise e # Re-lanzar la excepción para detener el procesamiento raise e # Re-lanzar la excepción para detener el procesamiento
@ -95,29 +95,29 @@ def procesar_factura_verifactu(
if factura != None: if factura != None:
# Creamos registro de factura en verifactu # Creamos registro de factura en verifactu
if factura.get('uuid') == '': if factura.get('uuid') == '':
# logging.info(f"Send to create Verifactu: {factura}") # logger.info(f"Send to create Verifactu: {factura}")
respuesta = crear_factura(factura, config) respuesta = crear_factura(factura, config)
if respuesta.get("status") == 200 and respuesta.get("ok"): if respuesta.get("status") == 200 and respuesta.get("ok"):
data = respuesta.get("data") data = respuesta.get("data")
qr_verifactu = f"data:image/png;base64,{data.get('qr', '')}" qr_verifactu = f"data:image/png;base64,{data.get('qr', '')}"
cursor_mysql.execute(SQL.update_verifactu_records_with_invoiceId, (data.get("estado"), data.get( cursor_mysql.execute(SQL.update_verifactu_records_with_invoiceId, (data.get("estado"), data.get(
"uuid"), data.get("url"), qr_verifactu, factura.get("id"))) "uuid"), data.get("url"), qr_verifactu, factura.get("id")))
logging.info( logger.info(
f">>> Factura {factura.get("reference")} registrada en Verifactu") f">>> Factura {factura.get("reference")} registrada en Verifactu")
return True return True
else: else:
logging.info( logger.info(
f">>> Factura {factura.get("reference")} enviada a Verifactu con error {respuesta}") f">>> Factura {factura.get("reference")} enviada a Verifactu con error {respuesta}")
return False return False
# Actualizamos registro de factura en verifactu # Actualizamos registro de factura en verifactu
else: else:
# logging.info(f"Send to update Verifactu: {factura}") # logger.info(f"Send to update Verifactu: {factura}")
respuesta = estado_factura(factura.get('uuid'), config) respuesta = estado_factura(factura.get('uuid'), config)
if respuesta.get("status") == 200 and respuesta.get("ok"): if respuesta.get("status") == 200 and respuesta.get("ok"):
data = respuesta.get("data") data = respuesta.get("data")
cursor_mysql.execute(SQL.update_verifactu_records_with_uuid, (data.get( cursor_mysql.execute(SQL.update_verifactu_records_with_uuid, (data.get(
'estado'), data.get('operacion'), factura.get('uuid'))) 'estado'), data.get('operacion'), factura.get('uuid')))
logging.info( logger.info(
f">>> Factura {factura.get("reference")} actualizado registro de Verifactu") f">>> Factura {factura.get("reference")} actualizado registro de Verifactu")
return True return True
else: else:

View File

@ -1,28 +1,32 @@
import sys import sys
import logging
from app.__version_sync_factuges__ import __version__ from app.config import get_package_version
from datetime import datetime from datetime import datetime
from dateutil import tz from dateutil import tz
from app.config import setup_logging, load_config from app.config import create_logger, load_config, log_config
from app.db import get_mysql_connection, get_factuges_connection, sync_invoices_factuges from app.db import get_mysql_connection, get_factuges_connection, sync_invoices_factuges
from app.utils import obtener_fecha_ultima_ejecucion, actualizar_fecha_ultima_ejecucion, log_system_metrics, send_orders_mail from app.utils import obtener_fecha_ultima_ejecucion, actualizar_fecha_ultima_ejecucion
def main(): def main():
# Cargar la configuración # Cargar la configuración
config = load_config() config = load_config()
version = get_package_version()
local_tz = tz.gettz(config['LOCAL_TZ']) local_tz = tz.gettz(config['LOCAL_TZ'])
# Logging # Logging
setup_logging() logger = create_logger(
name="factuges-sync",
log_path="/app/logs/sync_factuges.log", # Solo lo genera en producción
)
logging.info("== START SYNC FACTUGES ==") logger.info("============================================================")
logging.info(f"Version: {__version__}") logger.info(" FACTUGES Sync FactuGES - START ")
logging.info(f"Environment: {config['ENVIRONMENT']}") logger.info(" Version: %s", version)
log_system_metrics() logger.info(" UTC Now: %s", datetime.utcnow().isoformat())
logger.info(" Environment: {config['ENV']}")
log_config(config)
conn_factuges = None conn_factuges = None
conn_mysql = None conn_mysql = None
@ -32,17 +36,17 @@ def main():
last_execution_date_local_tz = last_execution_date_utc.astimezone( last_execution_date_local_tz = last_execution_date_utc.astimezone(
tz=local_tz).strftime("%Y-%m-%d %H:%M:%S") tz=local_tz).strftime("%Y-%m-%d %H:%M:%S")
logging.info("Last execution (UTC): %s", logger.info("Last execution (UTC): %s",
last_execution_date_utc.strftime("%Y-%m-%d %H:%M:%S %Z")) last_execution_date_utc.strftime("%Y-%m-%d %H:%M:%S %Z"))
logging.info("Last execution (Local time): %s", logger.info("Last execution (Local time): %s",
last_execution_date_local_tz) last_execution_date_local_tz)
# Abrimos conexiones con una única transacción para que todo esté controlado # Abrimos conexiones con una única transacción para que todo esté controlado
conn_factuges = get_factuges_connection(config) conn_factuges = get_factuges_connection(config)
conn_mysql = get_mysql_connection(config) conn_mysql = get_mysql_connection(config)
# Sincronizamos # Sincronizamos
logging.info( logger.info(
f">>>>>>>>>>> INI Sync invoices FactuGES escritorio to FactuGES web") f">>>>>>>>>>> INI Sync invoices FactuGES escritorio to FactuGES web")
sync_invoices_factuges(conn_factuges, conn_mysql, last_execution_date_local_tz) sync_invoices_factuges(conn_factuges, conn_mysql, last_execution_date_local_tz)
@ -51,21 +55,23 @@ def main():
conn_factuges.commit() conn_factuges.commit()
conn_factuges.close() conn_factuges.close()
conn_mysql.close() conn_mysql.close()
logging.info(f">>>>>>>>>>> FIN Sync invoices FactuGES escritorio to FactuGES web") logger.info(f">>>>>>>>>>> FIN Sync invoices FactuGES escritorio to FactuGES web")
actualizar_fecha_ultima_ejecucion(config['LAST_RUN_PATH']) actualizar_fecha_ultima_ejecucion(config['LAST_RUN_PATH'])
# Enviar email # Enviar email
# send_orders_mail(inserted_orders) # send_orders_mail(inserted_orders)
logging.info("== END (0) ==") logger.info("== END (0) ==")
logger.info("============================================================")
sys.exit(0) sys.exit(0)
except Exception as e: except Exception as e:
logging.error("Se ha producido un error en la última ejecución.") logger.error("Se ha producido un error en la última ejecución.")
logging.error(e) logger.error(e)
logging.error("Traceback:", exc_info=True) logger.error("Traceback:", exc_info=True)
logging.info("== END (1) ==") logger.info("== END (1) ==")
logger.info("============================================================")
if conn_mysql is not None: if conn_mysql is not None:
conn_mysql.rollback() conn_mysql.rollback()

View File

@ -1,28 +1,33 @@
import sys import sys
import logging
from app.__version_sync_factuges__ import __version__ from app.config import get_package_version
from datetime import datetime from datetime import datetime
from dateutil import tz from dateutil import tz
from config import setup_logging, load_config from app.config import create_logger, load_config, log_config
from db import get_mysql_connection, get_factuges_connection, sync_invoices_verifactu from app.db import get_mysql_connection, get_factuges_connection, sync_invoices_verifactu
from utils import obtener_fecha_ultima_ejecucion, actualizar_fecha_ultima_ejecucion, log_system_metrics, send_orders_mail from app.utils import obtener_fecha_ultima_ejecucion, actualizar_fecha_ultima_ejecucion
def main(): def main():
# Cargar la configuración # Cargar la configuración
config = load_config() config = load_config()
version = get_package_version()
local_tz = tz.gettz(config['LOCAL_TZ']) local_tz = tz.gettz(config['LOCAL_TZ'])
# Logging # Logging
setup_logging() logger = create_logger(
name="factuges-sync",
log_path="/app/logs/sync_verifactu.log", # Solo lo genera en producción
)
logging.info("== START SYNC VERIFACTU ==") logger.info("============================================================")
logging.info(f"Version: {__version__}") logger.info(" FACTUGES Sync Verifactu - START ")
logging.info(f"Environment: {config['ENVIRONMENT']}") logger.info(" Version: %s", version)
log_system_metrics() logger.info(" UTC Now: %s", datetime.utcnow().isoformat())
logger.info(" Environment: {config['ENV']}")
log_config(config)
conn_factuges = None conn_factuges = None
conn_mysql = None conn_mysql = None
@ -32,35 +37,37 @@ def main():
last_execution_date_local_tz = last_execution_date_utc.astimezone( last_execution_date_local_tz = last_execution_date_utc.astimezone(
tz=local_tz).strftime("%Y-%m-%d %H:%M:%S") tz=local_tz).strftime("%Y-%m-%d %H:%M:%S")
logging.info("Last execution (UTC): %s", logger.info("Last execution (UTC): %s",
last_execution_date_utc.strftime("%Y-%m-%d %H:%M:%S %Z")) last_execution_date_utc.strftime("%Y-%m-%d %H:%M:%S %Z"))
logging.info("Last execution (Local time): %s", logger.info("Last execution (Local time): %s",
last_execution_date_local_tz) last_execution_date_local_tz)
# Abrimos conexión con una única transacción para que todo esté controlado # Abrimos conexión con una única transacción para que todo esté controlado
conn_mysql = get_mysql_connection(config) conn_mysql = get_mysql_connection(config)
# Sync Verifactu # Sync Verifactu
logging.info( logger.info(
f">>>>>>>>>> INI Sync facturas emitidas to Verifactu") f">>>>>>>>>> INI Sync facturas emitidas to Verifactu")
sync_invoices_verifactu(conn_mysql, last_execution_date_local_tz) sync_invoices_verifactu(conn_mysql, last_execution_date_local_tz)
conn_mysql.commit() conn_mysql.commit()
conn_mysql.close() conn_mysql.close()
logging.info(f">>>>>>>>>> FIN Sync facturas emitidas to Verifactu") logger.info(f">>>>>>>>>> FIN Sync facturas emitidas to Verifactu")
actualizar_fecha_ultima_ejecucion() actualizar_fecha_ultima_ejecucion()
# Enviar email # Enviar email
# send_orders_mail(inserted_orders) # send_orders_mail(inserted_orders)
logging.info("== END (0) ==") logger.info("== END (0) ==")
logger.info("============================================================")
sys.exit(0) sys.exit(0)
except Exception as e: except Exception as e:
logging.error("Se ha producido un error en la última ejecución.") logger.error("Se ha producido un error en la última ejecución.")
logging.error(e) logger.error(e)
logging.error("Traceback:", exc_info=True) logger.error("Traceback:", exc_info=True)
logging.info("== END (1) ==") logger.info("== END (1) ==")
logger.info("============================================================")
if conn_mysql is not None: if conn_mysql is not None:
conn_mysql.rollback() conn_mysql.rollback()

View File

@ -1,5 +1,4 @@
from .last_execution_helper import actualizar_fecha_ultima_ejecucion, obtener_fecha_ultima_ejecucion from .last_execution_helper import actualizar_fecha_ultima_ejecucion, obtener_fecha_ultima_ejecucion
from .log_system_metrics import log_system_metrics
from .password import hashPassword from .password import hashPassword
from .send_orders_mail import send_orders_mail from .send_orders_mail import send_orders_mail
from .text_converter import text_converter, limpiar_cadena from .text_converter import text_converter, limpiar_cadena

View File

@ -1,9 +0,0 @@
import psutil
import logging
def log_system_metrics():
cpu_usage = psutil.cpu_percent(interval=1)
memory_usage = psutil.virtual_memory().percent
logging.info(f'CPU Usage: {cpu_usage}%')
logging.info(f'Memory Usage: {memory_usage}%')

View File

@ -1,4 +1,4 @@
import logging from app.config import logger
import brevo_python import brevo_python
from brevo_python.rest import ApiException from brevo_python.rest import ApiException
@ -25,6 +25,6 @@ def send_orders_mail(inserted_orders):
}, },
) )
api_response = api_instance.send_transac_email(send_smtp_email) api_response = api_instance.send_transac_email(send_smtp_email)
logging.info(msg=api_response) logger.info(msg=api_response)
except ApiException as e: except ApiException as e:
logging.error(msg=e) logger.error(msg=e)

View File

@ -1,5 +1,5 @@
import requests import requests
import logging from app.config import logger
from typing import Optional, Dict, Any, Tuple from typing import Optional, Dict, Any, Tuple
@ -51,10 +51,10 @@ def estado_factura(uuid_str: str,
return {"ok": False, "status": resp.status_code, "error": f"HTTP {resp.status_code}", "raw": resp.text} return {"ok": False, "status": resp.status_code, "error": f"HTTP {resp.status_code}", "raw": resp.text}
except requests.RequestException as e: except requests.RequestException as e:
logging.error("Error de conexión con la API Verifacti: %s", e) logger.error("Error de conexión con la API Verifacti: %s", e)
return False, None, str(e) return False, None, str(e)
except ValueError as e: except ValueError as e:
logging.error("Respuesta no es JSON válido: %s", e) logger.error("Respuesta no es JSON válido: %s", e)
return False, None, "Respuesta no es JSON válido" return False, None, "Respuesta no es JSON válido"
@ -83,7 +83,7 @@ def crear_factura(payload,
if resp.status_code == 200: if resp.status_code == 200:
try: try:
data = resp.json() data = resp.json()
# logging.info(data) # logger.info(data)
except ValueError: except ValueError:
return {"ok": False, "status": 200, "error": "Respuesta 200 sin JSON válido", "raw": resp.text} return {"ok": False, "status": 200, "error": "Respuesta 200 sin JSON válido", "raw": resp.text}
return {"ok": True, "status": 200, "data": data} return {"ok": True, "status": 200, "data": data}
@ -106,10 +106,10 @@ def crear_factura(payload,
return {"ok": False, "status": resp.status_code, "error": f"HTTP {resp.status_code}", "raw": resp.text} return {"ok": False, "status": resp.status_code, "error": f"HTTP {resp.status_code}", "raw": resp.text}
except requests.RequestException as e: except requests.RequestException as e:
logging.error("Error de conexión con la API Verifacti: %s", e) logger.error("Error de conexión con la API Verifacti: %s", e)
return False, None, str(e) return False, None, str(e)
except ValueError as e: except ValueError as e:
logging.error("Respuesta no es JSON válido: %s", e) logger.error("Respuesta no es JSON válido: %s", e)
return False, None, "Respuesta no es JSON válido" return False, None, "Respuesta no es JSON válido"
@ -140,18 +140,18 @@ def validar_nif(
resp = requests.post( resp = requests.post(
url, json=payload, headers=headers, timeout=timeout) url, json=payload, headers=headers, timeout=timeout)
if resp.status_code != 200: if resp.status_code != 200:
logging.info(f"ERRRRRROOOOOOORRRRR LLAMADA REST API") logger.info(f"ERRRRRROOOOOOORRRRR LLAMADA REST API")
# return False, None, f"HTTP {resp.status_code}: {resp.text}" # return False, None, f"HTTP {resp.status_code}: {resp.text}"
data = resp.json() data = resp.json()
resultado = data.get("resultado", "NO IDENTIFICADO") resultado = data.get("resultado", "NO IDENTIFICADO")
logging.info(f"Resultado Verifacti: {resultado}") logger.info(f"Resultado Verifacti: {resultado}")
return resultado == "IDENTIFICADO" return resultado == "IDENTIFICADO"
except requests.RequestException as e: except requests.RequestException as e:
logging.error("Error de conexión con la API Verifacti: %s", e) logger.error("Error de conexión con la API Verifacti: %s", e)
return False, None, str(e) return False, None, str(e)
except ValueError as e: except ValueError as e:
logging.error("Respuesta no es JSON válido: %s", e) logger.error("Respuesta no es JSON válido: %s", e)
return False, None, "Respuesta no es JSON válido" return False, None, "Respuesta no es JSON válido"

View File

@ -1,4 +1,4 @@
import logging from app.config import logger
import json import json
from decimal import Decimal from decimal import Decimal
from typing import Any, Dict, Iterable, Optional, Tuple from typing import Any, Dict, Iterable, Optional, Tuple

View File

@ -1,5 +1,5 @@
import logging
import re import re
from app.config import logger
def text_converter(texto, charset_destino='ISO8859_1', longitud_maxima=None): def text_converter(texto, charset_destino='ISO8859_1', longitud_maxima=None):
@ -24,18 +24,18 @@ def text_converter(texto, charset_destino='ISO8859_1', longitud_maxima=None):
# Si se especifica una longitud máxima, truncar el texto # Si se especifica una longitud máxima, truncar el texto
if longitud_maxima and len(texto_convertido) > longitud_maxima: if longitud_maxima and len(texto_convertido) > longitud_maxima:
logging.warning( logger.warning(
f"El texto ha sido truncado de {len(texto_convertido)} a {longitud_maxima} caracteres.") f"El texto ha sido truncado de {len(texto_convertido)} a {longitud_maxima} caracteres.")
texto_convertido = texto_convertido[:longitud_maxima] texto_convertido = texto_convertido[:longitud_maxima]
return texto_convertido return texto_convertido
except UnicodeEncodeError as e: except UnicodeEncodeError as e:
logging.error( logger.error(
f"Error al convertir texto a {charset_destino}: {str(e)}") f"Error al convertir texto a {charset_destino}: {str(e)}")
return "" return ""
except Exception as e: except Exception as e:
logging.error(f"Error inesperado al convertir texto: {str(e)}") logger.error(f"Error inesperado al convertir texto: {str(e)}")
return "" return ""

View File

@ -1,17 +1,41 @@
services: services:
uecko_sync: factuges_sync:
container_name: uecko_sync_app:v1.0.8 image: "factuges-sync-factuges:acana-latest"
env_file: ".env.production" container_name: "factuges-sync-factuges-acana"
build: . restart: "no"
#volumes: environment:
#- logs:/var/log/uecko_sync_app ENV: "prod"
networks: LOCAL_TZ: "Europe/Madrid"
- presupuestador-uecko_private LAST_RUN_PATH: "${LAST_RUN_PATH}"
restart: unless-stopped
#volumes: FACTUGES_HOST: "${FACTUGES_HOST}"
# logs: FACTUGES_PORT: "${FACTUGES_PORT}"
FACTUGES_DATABASE: "${FACTUGES_DATABASE}"
FACTUGES_USER: "${FACTUGES_USER}"
FACTUGES_PASSWORD: "${FACTUGES_PASSWORD}"
networks: FWEB_MYSQL_HOST: "db"
presupuestador-uecko_private: FWEB_MYSQL_PORT: "${DB_PORT}"
external: true FWEB_MYSQL_DATABASE: "${DB_NAME}"
FWEB_MYSQL_USER: "${DB_USER}"
FWEB_MYSQL_PASSWORD: "${DB_PASS}"
CTE_COMPANY_ID: "${CTE_COMPANY_ID}"
CTE_SERIE: "${CTE_SERIE}"
CTE_STATUS_INVOICE: "${CTE_STATUS_INVOICE}"
CTE_IS_PROFORMA: "${CTE_IS_PROFORMA}"
CTE_STATUS_VERIFACTU: "${CTE_STATUS_VERIFACTU}"
CTE_LANGUAGE_CODE: "${CTE_LANGUAGE_CODE}"
CTE_COUNTRY_CODE: "${CTE_COUNTRY_CODE}"
CTE_IS_COMPANY: "${CTE_IS_COMPANY}"
CTE_SYNC_RESULT_OK: "${CTE_SYNC_RESULT_OK}"
CTE_SYNC_RESULT_FAIL: "${CTE_SYNC_RESULT_FAIL}"
depends_on:
db:
condition: service_healthy
networks:
- internal
- edge
volumes:
- ./volumes/db_sync/logs:/app/logs
- ./volumes/db_sync/last_run_factuges.ini:/app/last_run_factuges.ini:rw

View File

@ -1,4 +1,4 @@
ENVIRONMENT = development ENV = development
LOCAL_TZ = Europe/Madrid LOCAL_TZ = Europe/Madrid
LAST_RUN_PATH = ./app.last_run.txt LAST_RUN_PATH = ./app.last_run.txt
#LOG_PATH = ./app.log #LOG_PATH = ./app.log
@ -25,11 +25,11 @@ CTE_COMPANY_ID = '019a9667-6a65-767a-a737-48234ee50a3a'
VERIFACTU_API_KEY = vf_test_C03HL2F0X5OXSDRunjNFoMxD4IrRfK3kCC8PfcvCENI= VERIFACTU_API_KEY = vf_test_C03HL2F0X5OXSDRunjNFoMxD4IrRfK3kCC8PfcvCENI=
#DESARROLLO #DESARROLLO
UECKO_MYSQL_HOST = localhost FWEB_MYSQL_HOST = localhost
UECKO_MYSQL_PORT = 3306 FWEB_MYSQL_PORT = 3306
UECKO_MYSQL_DATABASE = uecko_erp_sync FWEB_MYSQL_DATABASE = uecko_erp_sync
UECKO_MYSQL_USER = rodax FWEB_MYSQL_USER = rodax
UECKO_MYSQL_PASSWORD = rodax FWEB_MYSQL_PASSWORD = rodax
CTE_SERIE = 'F25/' CTE_SERIE = 'F25/'
CTE_STATUS_INVOICE = 'issued' CTE_STATUS_INVOICE = 'issued'

View File

@ -1,6 +1,6 @@
ENVIRONMENT = development ENV = development
LOCAL_TZ = Europe/Madrid LOCAL_TZ = Europe/Madrid
#LOG_PATH = ./app.log #LOG_PATH = ./app.log
@ -18,17 +18,17 @@ FACTUGES_CONTRATO_TIPO_DETALLE = "Concepto"
FACTUGES_NOMBRE_TARIFA = TARIFA 2024 FACTUGES_NOMBRE_TARIFA = TARIFA 2024
FACTUGES_PRECIO_PUNTO = 3.31 FACTUGES_PRECIO_PUNTO = 3.31
PRO_UECKO_MYSQL_HOST = 192.168.0.250 PRO_FWEB_MYSQL_HOST = 192.168.0.250
PRO_UECKO_MYSQL_PORT = 3306 PRO_FWEB_MYSQL_PORT = 3306
PRO_UECKO_MYSQL_DATABASE = factuges_db PRO_FWEB_MYSQL_DATABASE = factuges_db
PRO_UECKO_MYSQL_USER = root PRO_FWEB_MYSQL_USER = root
PRO_UECKO_MYSQL_PASSWORD = rootpass PRO_FWEB_MYSQL_PASSWORD = rootpass
DEV_UECKO_MYSQL_HOST = 192.168.0.104 DEV_FWEB_MYSQL_HOST = 192.168.0.104
DEV_UECKO_MYSQL_PORT = 3306 DEV_FWEB_MYSQL_PORT = 3306
DEV_UECKO_MYSQL_DATABASE = uecko_erp_sync DEV_FWEB_MYSQL_DATABASE = uecko_erp_sync
DEV_UECKO_MYSQL_USER = rodax DEV_FWEB_MYSQL_USER = rodax
DEV_UECKO_MYSQL_PASSWORD = rodax DEV_FWEB_MYSQL_PASSWORD = rodax
BREVO_API_KEY = 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 BREVO_API_KEY = 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

View File

@ -1,4 +1,4 @@
ENVIRONMENT = development ENV = development
LOCAL_TZ = Europe/Madrid LOCAL_TZ = Europe/Madrid
LAST_RUN_PATH = ./app.last_run.txt LAST_RUN_PATH = ./app.last_run.txt
#LOG_PATH = ./app.log #LOG_PATH = ./app.log
@ -11,11 +11,11 @@ FACTUGES_USER = sysdba
FACTUGES_PASSWORD = masterkey FACTUGES_PASSWORD = masterkey
#DESARROLLO #DESARROLLO
UECKO_MYSQL_HOST = localhost FWEB_MYSQL_HOST = localhost
UECKO_MYSQL_PORT = 3306 FWEB_MYSQL_PORT = 3306
UECKO_MYSQL_DATABASE = uecko_erp_sync FWEB_MYSQL_DATABASE = uecko_erp_sync
UECKO_MYSQL_USER = rodax FWEB_MYSQL_USER = rodax
UECKO_MYSQL_PASSWORD = rodax FWEB_MYSQL_PASSWORD = rodax
#CONFIGURACION ACANA #CONFIGURACION ACANA
CTE_COMPANY_ID = '019a9667-6a65-767a-a737-48234ee50a3a' CTE_COMPANY_ID = '019a9667-6a65-767a-a737-48234ee50a3a'

View File

@ -1,14 +1,14 @@
ENVIRONMENT = development ENV = development
LOCAL_TZ = Europe/Madrid LOCAL_TZ = Europe/Madrid
LAST_RUN_PATH = ./app.last_run.txt LAST_RUN_PATH = ./app.last_run.txt
#LOG_PATH = ./app.log #LOG_PATH = ./app.log
#DESARROLLO #DESARROLLO
UECKO_MYSQL_HOST = localhost FWEB_MYSQL_HOST = localhost
UECKO_MYSQL_PORT = 3306 FWEB_MYSQL_PORT = 3306
UECKO_MYSQL_DATABASE = uecko_erp_sync FWEB_MYSQL_DATABASE = uecko_erp_sync
UECKO_MYSQL_USER = rodax FWEB_MYSQL_USER = rodax
UECKO_MYSQL_PASSWORD = rodax FWEB_MYSQL_PASSWORD = rodax
VERIFACTU_BASE_URL = https://api.verifacti.com/ VERIFACTU_BASE_URL = https://api.verifacti.com/
VERIFACTU_API_KEY = vf_test_ei8WYAvEq5dhSdEyQVjgCS8NZaNpEK2BljSHSUXf+Y0= VERIFACTU_API_KEY = vf_test_ei8WYAvEq5dhSdEyQVjgCS8NZaNpEK2BljSHSUXf+Y0=

30
enviroment/acana.env Normal file
View File

@ -0,0 +1,30 @@
ENV = development
LOCAL_TZ = Europe/Madrid
LAST_RUN_PATH = /usr/share/factuges-app/last_run.txt
FACTUGES_HOST = acana.mywire.org
FACTUGES_PORT = 63050
FACTUGES_DATABASE = D:\Rodax\BD\FACTUGES.FDB
FACTUGES_USER = sysdba
FACTUGES_PASSWORD = masterkey
CTE_COMPANY_ID = '019a9667-6a65-767a-a737-48234ee50a3a'
VERIFACTU_API_KEY = vf_test_ei8WYAvEq5dhSdEyQVjgCS8NZaNpEK2BljSHSUXf+Y0=
FWEB_MYSQL_HOST = db
FWEB_MYSQL_PORT = 3306
FWEB_MYSQL_DATABASE = factuges_acana
FWEB_MYSQL_USER = acana
FWEB_MYSQL_PASSWORD = r@U8%GJ+2e/AWR
CTE_SERIE = 'F25/'
CTE_STATUS_INVOICE = 'issued'
CTE_IS_PROFORMA = 0
CTE_STATUS_VERIFACTU = 'Pendiente'
CTE_LANGUAGE_CODE = 'es' #En uecko vendrá de su ficha
CTE_COUNTRY_CODE = 'es' #En uecko vendrá de su ficha
CTE_IS_COMPANY = 1
CTE_SYNC_RESULT_OK = 1
CTE_SYNC_RESULT_FAIL = 2
VERIFACTU_BASE_URL = https://api.verifacti.com/
VERIFACTU_NIFS_API_KEY = vfn_osYpNdqSzAdTAHpazXG2anz4F3o0gfbSb5FFrCBZcno=

View File

@ -1,4 +1,4 @@
ENVIRONMENT = development ENV = development
LOCAL_TZ = Europe/Madrid LOCAL_TZ = Europe/Madrid
LAST_RUN_PATH = ./app.last_run.txt LAST_RUN_PATH = ./app.last_run.txt
#LOG_PATH = ./app.log #LOG_PATH = ./app.log
@ -25,11 +25,11 @@ CTE_COMPANY_ID = '019a9667-6a65-767a-a737-48234ee50a3a'
VERIFACTU_API_KEY = vf_test_C03HL2F0X5OXSDRunjNFoMxD4IrRfK3kCC8PfcvCENI= VERIFACTU_API_KEY = vf_test_C03HL2F0X5OXSDRunjNFoMxD4IrRfK3kCC8PfcvCENI=
#DESARROLLO #DESARROLLO
UECKO_MYSQL_HOST = localhost FWEB_MYSQL_HOST = localhost
UECKO_MYSQL_PORT = 3306 FWEB_MYSQL_PORT = 3306
UECKO_MYSQL_DATABASE = uecko_erp_sync FWEB_MYSQL_DATABASE = uecko_erp_sync
UECKO_MYSQL_USER = rodax FWEB_MYSQL_USER = rodax
UECKO_MYSQL_PASSWORD = rodax FWEB_MYSQL_PASSWORD = rodax
CTE_SERIE = 'F25/' CTE_SERIE = 'F25/'
CTE_STATUS_INVOICE = 'issued' CTE_STATUS_INVOICE = 'issued'

View File

@ -1,4 +0,0 @@
DB_HOST=prod-db
DB_USER=prod_user
DB_PASS=supersecret
RUN_INTERVAL_MINUTES=5

View File

@ -2,12 +2,12 @@
# Sincronización FactuGES → cada 5 minutos # Sincronización FactuGES → cada 5 minutos
*/5 * * * * docker run --rm \ */5 * * * * docker run --rm \
-e ENV=prod \ -e ENV=production \
factuges-sync-factuges:acme-latest \ factuges-sync-factuges:acme-latest \
>> /var/log/factuges-sync/factuges.log 2>&1 >> /var/log/factuges-sync/factuges.log 2>&1
# Sincronización Verifactu → cada 7 minutos # Sincronización Verifactu → cada 7 minutos
*/7 * * * * docker run --rm \ */7 * * * * docker run --rm \
-e ENV=prod \ -e ENV=production \
factuges-sync-verifactu:acme-latest \ factuges-sync-verifactu:acme-latest \
>> /var/log/factuges-sync/verifactu.log 2>&1 >> /var/log/factuges-sync/verifactu.log 2>&1

View File

@ -23,13 +23,13 @@ cat > "$CRON_FILE" <<EOF
# Sincronización FactuGES → cada 5 minutos # Sincronización FactuGES → cada 5 minutos
*/5 * * * * docker run --rm \ */5 * * * * docker run --rm \
-e ENV=prod \ -e ENV=production \
factuges-sync-factuges:${COMPANY}-latest \ factuges-sync-factuges:${COMPANY}-latest \
>> /var/log/factuges-sync/factuges.log 2>&1 >> /var/log/factuges-sync/factuges.log 2>&1
# Sincronización Verifactu → cada 7 minutos # Sincronización Verifactu → cada 7 minutos
*/7 * * * * docker run --rm \ */7 * * * * docker run --rm \
-e ENV=prod \ -e ENV=production \
factuges-sync-verifactu:${COMPANY}-latest \ factuges-sync-verifactu:${COMPANY}-latest \
>> /var/log/factuges-sync/verifactu.log 2>&1 >> /var/log/factuges-sync/verifactu.log 2>&1
EOF EOF

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = factuges-sync name = factuges-sync
version = 0.1.0 version = 0.0.15
description = ETL job to sync data from legacy DB to MariaDB description = ETL job to sync data from legacy DB to MariaDB
author = Rodax Software author = Rodax Software
author_email = info@rodax-software.com author_email = info@rodax-software.com