commit inicial
This commit is contained in:
commit
2ae6a66a4e
38
.env.development
Normal file
38
.env.development
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
|
||||||
|
ENVIRONMENT = development
|
||||||
|
LOCAL_TZ = Europe/Madrid
|
||||||
|
#LOG_PATH = ./app.log
|
||||||
|
|
||||||
|
FACTUGES_HOST = 192.168.0.135
|
||||||
|
FACTUGES_PORT = 3050
|
||||||
|
FACTUGES_DATABASE = C:\Codigo\Output\Debug\Database\FACTUGES.FDB
|
||||||
|
FACTUGES_USER = sysdba
|
||||||
|
FACTUGES_PASSWORD = masterkey
|
||||||
|
|
||||||
|
FACTUGES_ID_EMPRESA = 1
|
||||||
|
FACTUGES_CONTRATO_ID_TIENDA = 1
|
||||||
|
FACTUGES_CONTRATO_SITUACION = "PENDIENTE"
|
||||||
|
FACTUGES_CONTRATO_ENVIADA_REVISADA = 10
|
||||||
|
FACTUGES_CONTRATO_TIPO_DETALLE = "Concepto"
|
||||||
|
FACTUGES_NOMBRE_TARIFA = TARIFA 2024
|
||||||
|
FACTUGES_PRECIO_PUNTO = 3.31
|
||||||
|
|
||||||
|
UECKO_MYSQL_HOST = 192.168.0.116
|
||||||
|
UECKO_MYSQL_PORT = 3306
|
||||||
|
UECKO_MYSQL_DATABASE = uecko
|
||||||
|
UECKO_MYSQL_USER = rodax
|
||||||
|
UECKO_MYSQL_PASSWORD = rodax
|
||||||
|
|
||||||
|
UECKO_DEFAULT_IVA = 2100
|
||||||
|
UECKO_DEFAULT_CURRENCY_CODE = EUR
|
||||||
|
UECKO_DEFAULT_VALIDEZ = "30 días"
|
||||||
|
UECKO_DEFAULT_LOPD = ""
|
||||||
|
UECKO_DEFAULT_NOTAS = ""
|
||||||
|
UECKO_DEFAULT_FORMA_PAGO = "50% a la aceptación y 50% a la finalización"
|
||||||
|
|
||||||
|
BREVO_API_KEY = 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
|
||||||
|
BREVO_EMAIL_TEMPLATE = 1
|
||||||
|
MAIL_FROM = 'no-reply@presupuestos.uecko.com'
|
||||||
|
MAIL_TO = 'soporte@rodax-software.com'
|
||||||
|
|
||||||
36
.env.production
Normal file
36
.env.production
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
ENVIRONMENT = production
|
||||||
|
LOCAL_TZ = Europe/Madrid
|
||||||
|
#LOG_PATH = /var/log/uecko_sync_app/uecko_sync_app.log
|
||||||
|
|
||||||
|
FACTUGES_HOST = 83.48.36.69
|
||||||
|
FACTUGES_PORT = 3050
|
||||||
|
FACTUGES_DATABASE = D:\RODAX\FACTUGES\BD\FACTUGES_FABRICA.FDB
|
||||||
|
FACTUGES_USER = sysdba
|
||||||
|
FACTUGES_PASSWORD = abeto2010
|
||||||
|
|
||||||
|
FACTUGES_ID_EMPRESA = 1
|
||||||
|
FACTUGES_CONTRATO_ID_TIENDA = 1
|
||||||
|
FACTUGES_CONTRATO_SITUACION = "PENDIENTE"
|
||||||
|
FACTUGES_CONTRATO_ENVIADA_REVISADA = 10
|
||||||
|
FACTUGES_CONTRATO_TIPO_DETALLE = "Concepto"
|
||||||
|
FACTUGES_NOMBRE_TARIFA = TARIFA 2024
|
||||||
|
FACTUGES_PRECIO_PUNTO = 3.31
|
||||||
|
|
||||||
|
UECKO_MYSQL_HOST = mariadb
|
||||||
|
UECKO_MYSQL_PORT = 3306
|
||||||
|
UECKO_MYSQL_DATABASE = uecko
|
||||||
|
UECKO_MYSQL_USER = uecko
|
||||||
|
UECKO_MYSQL_PASSWORD = u8Ax5Nw3%sjd
|
||||||
|
|
||||||
|
UECKO_DEFAULT_IVA = 2100
|
||||||
|
UECKO_DEFAULT_CURRENCY_CODE = EUR
|
||||||
|
UECKO_DEFAULT_VALIDEZ = "30 días"
|
||||||
|
UECKO_DEFAULT_LOPD = ""
|
||||||
|
UECKO_DEFAULT_NOTAS = ""
|
||||||
|
UECKO_DEFAULT_FORMA_PAGO = "50% a la aceptación y 50% a la finalización"
|
||||||
|
|
||||||
|
BREVO_API_KEY = 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
|
||||||
|
BREVO_EMAIL_TEMPLATE = 1
|
||||||
|
MAIL_FROM = 'no-reply@presupuestos.uecko.com'
|
||||||
|
MAIL_TO = 'pedidos@uecko.com'
|
||||||
|
|
||||||
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
venv/
|
||||||
|
__pycache__
|
||||||
|
.env
|
||||||
|
input/
|
||||||
|
output/
|
||||||
|
FACTUGES.FDB
|
||||||
|
last_execution*.txt
|
||||||
|
*.json
|
||||||
|
*.log
|
||||||
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"useTabs": false,
|
||||||
|
"printWidth": 100,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"rcVerbose": true
|
||||||
|
}
|
||||||
38
Dockerfile
Normal file
38
Dockerfile
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# syntax=docker/dockerfile:1.4
|
||||||
|
|
||||||
|
# Usa una imagen base de Python
|
||||||
|
FROM python:3.12.6-slim-bookworm AS python_script
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install libfbclient2 -y
|
||||||
|
|
||||||
|
# Establece el directorio de trabajo dentro del contenedor
|
||||||
|
WORKDIR /opt/uecko_sync_app
|
||||||
|
|
||||||
|
# Copia los archivos del proyecto al contenedor
|
||||||
|
COPY . .
|
||||||
|
COPY ./.env.production ./.env
|
||||||
|
|
||||||
|
# Instala las dependencias de Python
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Instala cron en el contenedor
|
||||||
|
RUN apt-get update && apt-get install -y cron nano
|
||||||
|
|
||||||
|
# Copia el archivo de cron dentro del contenedor
|
||||||
|
COPY cronjob /etc/cron.d/cronjob
|
||||||
|
|
||||||
|
# Da permisos de ejecución al archivo cronjob
|
||||||
|
RUN chmod 0644 /etc/cron.d/cronjob
|
||||||
|
|
||||||
|
# Aplica la configuración de cronjob
|
||||||
|
RUN crontab /etc/cron.d/cronjob
|
||||||
|
|
||||||
|
# Crea un archivo log para cron
|
||||||
|
RUN touch /var/log/cron.log
|
||||||
|
|
||||||
|
#RUN mkdir -p /var/log/uecko_sync_app
|
||||||
|
#RUN touch /var/log/uecko_sync_app/uecko_sync_app.log
|
||||||
|
|
||||||
|
# Comando para iniciar cron y mantener el contenedor en ejecución
|
||||||
|
CMD cron && tail -f /var/log/cron.log
|
||||||
43
Dockerfile.firebird
Normal file
43
Dockerfile.firebird
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# syntax=docker/dockerfile:1.4
|
||||||
|
|
||||||
|
FROM debian:bookworm-slim AS build
|
||||||
|
|
||||||
|
ENV FIREBIRD_PATH=/opt/firebird
|
||||||
|
ENV FIREBIRD_DB_PASSWORD=masterkey
|
||||||
|
ENV FIREBIRD_DB_PASSWORD_DEFAULT=masterkey
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install wget -y
|
||||||
|
RUN apt-get install libstdc++5 -y
|
||||||
|
RUN apt-get install xinetd -y
|
||||||
|
RUN wget http://sourceforge.net/projects/firebird/files/firebird-linux-amd64/2.1.7-Release/FirebirdCS-2.1.7.18553-0.amd64.tar.gz
|
||||||
|
RUN tar -vzxf FirebirdCS-2.1.7.18553-0.amd64.tar.gz
|
||||||
|
RUN rm FirebirdCS-2.1.7.18553-0.amd64.tar.gz
|
||||||
|
RUN rm FirebirdCS-2.1.7.18553-0.amd64/install.sh
|
||||||
|
RUN rm FirebirdCS-2.1.7.18553-0.amd64/scripts/postinstall.sh
|
||||||
|
|
||||||
|
COPY ./firebird/install.sh FirebirdCS-2.1.7.18553-0.amd64
|
||||||
|
COPY ./firebird/postinstall.sh FirebirdCS-2.1.7.18553-0.amd64/scripts
|
||||||
|
|
||||||
|
# Otorgar permisos de ejecución al script install.sh
|
||||||
|
RUN chmod +x FirebirdCS-2.1.7.18553-0.amd64/install.sh
|
||||||
|
RUN chmod +x FirebirdCS-2.1.7.18553-0.amd64/scripts/postinstall.sh
|
||||||
|
|
||||||
|
RUN cd FirebirdCS-2.1.7.18553-0.amd64 && ./install.sh ${FIREBIRD_DB_PASSWORD_DEFAULT}
|
||||||
|
RUN rm -r FirebirdCS-2.1.7.18553-0.amd64
|
||||||
|
|
||||||
|
COPY ./firebird/fbudflib2.so ${FIREBIRD_PATH}/UDF
|
||||||
|
|
||||||
|
COPY ./firebird/launch.sh ${FIREBIRD_PATH}
|
||||||
|
RUN chmod +x ${FIREBIRD_PATH}/launch.sh
|
||||||
|
|
||||||
|
RUN cd ${FIREBIRD_PATH} && mkdir DBA && chown firebird:firebird DBA && chmod -R 770 DBA
|
||||||
|
RUN cd / && mkdir dba && chown firebird:firebird dba && chmod -R 770 dba
|
||||||
|
|
||||||
|
#RUN cp ${FIREBIRD_PATH}/lib/libfbclient.so.2.1.7 /usr/lib/libfbclient.so.2.1.7
|
||||||
|
#RUN ln -s /usr/lib/libfbclient.so.2.1.7 /usr/lib/libfbclient.so.2
|
||||||
|
#RUN ln -s /usr/lib//usr/lib/libfbclient.so.2 /usr/lib/libfbclient.so
|
||||||
|
|
||||||
|
EXPOSE 3050/tcp
|
||||||
|
WORKDIR ${FIREBIRD_PATH}
|
||||||
|
ENTRYPOINT ${FIREBIRD_PATH}/launch.sh
|
||||||
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
1
app/__version__.py
Normal file
1
app/__version__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
__version__ = "1.0.8"
|
||||||
3
app/config/__init__.py
Normal file
3
app/config/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from .settings import load_config
|
||||||
|
from .setup_logging import setup_logging
|
||||||
|
from .setup_brevo import setup_brevo
|
||||||
46
app/config/settings.py
Normal file
46
app/config/settings.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import os
|
||||||
|
from os.path import join, dirname
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
dotenv_path = join(dirname(__file__), '../../.env')
|
||||||
|
load_dotenv(dotenv_path)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'ENVIRONMENT': os.getenv('ENVIRONMENT'),
|
||||||
|
'LOCAL_TZ': os.getenv('LOCAL_TZ', 'Europe/Madrid'),
|
||||||
|
# 'LOG_PATH': os.getenv('LOG_PATH', 'app.log'),
|
||||||
|
|
||||||
|
'FACTUGES_HOST': os.getenv('FACTUGES_HOST'),
|
||||||
|
'FACTUGES_PORT': os.getenv('FACTUGES_PORT'),
|
||||||
|
'FACTUGES_DATABASE': os.getenv('FACTUGES_DATABASE'),
|
||||||
|
'FACTUGES_USER': os.getenv('FACTUGES_USER'),
|
||||||
|
'FACTUGES_PASSWORD': os.getenv('FACTUGES_PASSWORD'),
|
||||||
|
|
||||||
|
'UECKO_MYSQL_HOST': os.getenv('UECKO_MYSQL_HOST'),
|
||||||
|
'UECKO_MYSQL_PORT': os.getenv('UECKO_MYSQL_PORT', 3306),
|
||||||
|
'UECKO_MYSQL_DATABASE': os.getenv('UECKO_MYSQL_DATABASE'),
|
||||||
|
'UECKO_MYSQL_USER': os.getenv('UECKO_MYSQL_USER'),
|
||||||
|
'UECKO_MYSQL_PASSWORD': os.getenv('UECKO_MYSQL_PASSWORD'),
|
||||||
|
|
||||||
|
'FACTUGES_ID_EMPRESA': os.getenv('FACTUGES_ID_EMPRESA'),
|
||||||
|
'FACTUGES_PRECIO_PUNTO': os.getenv('FACTUGES_PRECIO_PUNTO'),
|
||||||
|
'FACTUGES_NOMBRE_TARIFA': os.getenv('FACTUGES_NOMBRE_TARIFA'),
|
||||||
|
'FACTUGES_CONTRATO_ID_TIENDA': os.getenv('FACTUGES_CONTRATO_ID_TIENDA'),
|
||||||
|
'FACTUGES_CONTRATO_SITUACION': os.getenv('FACTUGES_CONTRATO_SITUACION'),
|
||||||
|
'FACTUGES_CONTRATO_ENVIADA_REVISADA': os.getenv('FACTUGES_CONTRATO_ENVIADA_REVISADA'),
|
||||||
|
'FACTUGES_CONTRATO_TIPO_DETALLE': os.getenv('FACTUGES_CONTRATO_TIPO_DETALLE'),
|
||||||
|
|
||||||
|
'UECKO_DEFAULT_IVA': os.getenv('UECKO_IVA', 2100),
|
||||||
|
'UECKO_DEFAULT_CURRENCY_CODE': os.getenv('UECKO_CURRENCY_CODE', "EUR"),
|
||||||
|
'UECKO_DEFAULT_VALIDEZ': os.getenv('UECKO_DEFAULT_VALIDEZ', ""),
|
||||||
|
'UECKO_DEFAULT_LOPD': os.getenv('UECKO_DEFAULT_LOPD', ""),
|
||||||
|
'UECKO_DEFAULT_NOTAS': os.getenv('UECKO_DEFAULT_NOTAS', ""),
|
||||||
|
'UECKO_DEFAULT_FORMA_PAGO': os.getenv('UECKO_DEFAULT_FORMA_PAGO', ""),
|
||||||
|
|
||||||
|
'BREVO_API_KEY': os.getenv('BREVO_API_KEY'),
|
||||||
|
'BREVO_EMAIL_TEMPLATE': os.getenv("BREVO_EMAIL_TEMPLATE"),
|
||||||
|
'MAIL_FROM': os.getenv('MAIL_FROM'),
|
||||||
|
'MAIL_TO': os.getenv('MAIL_TO'),
|
||||||
|
}
|
||||||
10
app/config/setup_brevo.py
Normal file
10
app/config/setup_brevo.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
import brevo_python
|
||||||
|
|
||||||
|
def setup_brevo(config):
|
||||||
|
|
||||||
|
# Configure API key authorization: api-key
|
||||||
|
configuration = brevo_python.Configuration()
|
||||||
|
configuration.api_key['api-key'] = config['BREVO_API_KEY']
|
||||||
|
|
||||||
|
return configuration
|
||||||
18
app/config/setup_logging.py
Normal file
18
app/config/setup_logging.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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'
|
||||||
|
)
|
||||||
5
app/db/__init__.py
Normal file
5
app/db/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from .db_connection import get_factuges_connection
|
||||||
|
from .db_connection import get_mysql_connection
|
||||||
|
from .sync_catalog import sync_catalog
|
||||||
|
from .sync_dealers import sync_dealers
|
||||||
|
from .sync_orders import sync_orders
|
||||||
44
app/db/db_connection.py
Normal file
44
app/db/db_connection.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import fdb
|
||||||
|
import mysql.connector
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def get_factuges_connection(config):
|
||||||
|
try:
|
||||||
|
conn = fdb.connect(
|
||||||
|
host=config['FACTUGES_HOST'],
|
||||||
|
port=int(config['FACTUGES_PORT']),
|
||||||
|
database=config['FACTUGES_DATABASE'],
|
||||||
|
user=config['FACTUGES_USER'],
|
||||||
|
password=config['FACTUGES_PASSWORD'],
|
||||||
|
charset='UTF8'
|
||||||
|
)
|
||||||
|
logging.info(
|
||||||
|
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
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Error al conectar a la base de datos FactuGES.")
|
||||||
|
logging.error(
|
||||||
|
f"(ERROR) Failed to establish connection to: {config['FACTUGES_HOST']} with database:{config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}")
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
def get_mysql_connection(config):
|
||||||
|
try:
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=config['UECKO_MYSQL_HOST'],
|
||||||
|
port=config['UECKO_MYSQL_PORT'],
|
||||||
|
database=config['UECKO_MYSQL_DATABASE'],
|
||||||
|
user=config['UECKO_MYSQL_USER'],
|
||||||
|
password=config['UECKO_MYSQL_PASSWORD']
|
||||||
|
)
|
||||||
|
logging.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']}")
|
||||||
|
return conn
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Error al conectar a la base de datos MySQL.")
|
||||||
|
logging.error(
|
||||||
|
f"(ERROR) Failed to establish connection to: {config['UECKO_MYSQL_HOST']} with database:{config['UECKO_MYSQL_DATABASE']} - using user:{config['UECKO_MYSQL_USER']}")
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e
|
||||||
219
app/db/sync_catalog.py
Normal file
219
app/db/sync_catalog.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import logging
|
||||||
|
from uuid import uuid4
|
||||||
|
from config import load_config
|
||||||
|
|
||||||
|
|
||||||
|
def sync_catalog(conn_factuges, conn_mysql, last_execution_date):
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
logging.info(f"Tarifa: {config['FACTUGES_NOMBRE_TARIFA']}")
|
||||||
|
logging.info(f"Precio punto: {config['FACTUGES_PRECIO_PUNTO']}")
|
||||||
|
|
||||||
|
# Construir la consulta SQL con la condición de fecha de modificación
|
||||||
|
consulta_sql_art_modificados = (
|
||||||
|
f"SELECT art.id || '' AS id, art.tarifa as tarifa, COALESCE(art.referencia,'') AS referencia, "
|
||||||
|
f"TRIM(COALESCE(art.familia, '') || ' ' || COALESCE(art.referencia_prov, '') || ' ' || COALESCE(art.descripcion, '')) as descripcion_es, "
|
||||||
|
f"TRIM(COALESCE(art_idioma_en.descripcion, '')) AS descripcion_en, "
|
||||||
|
f"TRUNC(art.precio_coste * 100) || '' AS puntos, "
|
||||||
|
f"TRUNC(ROUND(art.precio_coste * {
|
||||||
|
config['FACTUGES_PRECIO_PUNTO']}, 2) * 100) || '' AS pvp "
|
||||||
|
f"FROM articulos AS art "
|
||||||
|
f"LEFT JOIN ARTICULOS_IDIOMAS AS art_idioma_en ON art.id = art_idioma_en.id_articulo AND art_idioma_en.id_idioma = 2 "
|
||||||
|
f"WHERE "
|
||||||
|
f"(art.eliminado = 0) AND "
|
||||||
|
f"(art.tarifa = '{config['FACTUGES_NOMBRE_TARIFA']}') "
|
||||||
|
f"AND (art.FECHA_MODIFICACION > '{last_execution_date}')"
|
||||||
|
)
|
||||||
|
|
||||||
|
consulta_sql_all_tarifa = (
|
||||||
|
f"SELECT art.id || '' AS id, art.tarifa as tarifa "
|
||||||
|
f"FROM articulos AS art "
|
||||||
|
f"WHERE "
|
||||||
|
f"(art.eliminado = 0) AND "
|
||||||
|
f"(art.tarifa = '{config['FACTUGES_NOMBRE_TARIFA']}') "
|
||||||
|
)
|
||||||
|
|
||||||
|
# Crear un cursor para ejecutar consultas SQL
|
||||||
|
cursor_FactuGES = None
|
||||||
|
try:
|
||||||
|
cursor_FactuGES = conn_factuges.cursor()
|
||||||
|
# Ejecutar la consulta de articulos modificados
|
||||||
|
cursor_FactuGES.execute(consulta_sql_art_modificados)
|
||||||
|
filas = cursor_FactuGES.fetchall()
|
||||||
|
except Exception as e:
|
||||||
|
if cursor_FactuGES is not None:
|
||||||
|
cursor_FactuGES.close()
|
||||||
|
logging.error(f"(ERROR) Failed to fetch from database:{
|
||||||
|
config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}")
|
||||||
|
logging.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# Obtener los nombres de las columnas
|
||||||
|
columnas = [desc[0] for desc in cursor_FactuGES.description]
|
||||||
|
cursor_FactuGES.close()
|
||||||
|
|
||||||
|
# Convertir las filas en diccionarios con nombres de columnas como claves
|
||||||
|
tuplas_seleccionadas = []
|
||||||
|
for fila in filas:
|
||||||
|
tupla = dict(zip(columnas, fila))
|
||||||
|
tuplas_seleccionadas.append(tupla)
|
||||||
|
|
||||||
|
logging.info(f"Catalog rows to be processed: {len(tuplas_seleccionadas)}")
|
||||||
|
|
||||||
|
# Verificar si hay filas en el resultado
|
||||||
|
if tuplas_seleccionadas:
|
||||||
|
insertar_datos(conn_mysql, tuplas_seleccionadas, config)
|
||||||
|
else:
|
||||||
|
logging.info(
|
||||||
|
"There are no new or modified catalog rows since the last run.")
|
||||||
|
|
||||||
|
# Verificamos que en el catálogo de mysql solo hay los artículos del catálogo de FactuGES
|
||||||
|
# es decir, que si un artículo lo asignan a otra tarifa debe desaparecer del catálogo mysql
|
||||||
|
try:
|
||||||
|
cursor_FactuGES.execute(consulta_sql_all_tarifa)
|
||||||
|
filas = cursor_FactuGES.fetchall()
|
||||||
|
cursor_FactuGES.close()
|
||||||
|
|
||||||
|
# Crear un conjunto con los IDs [0] de los artículos en FactuGES para una búsqueda rápida
|
||||||
|
ids_factuges = {str(fila[0]) for fila in filas}
|
||||||
|
logging.info(f"{config['FACTUGES_NOMBRE_TARIFA']} rows to be processed: {
|
||||||
|
len(ids_factuges)}")
|
||||||
|
|
||||||
|
# Verificar si hay filas en el resultado
|
||||||
|
if ids_factuges:
|
||||||
|
eliminar_datos(conn_mysql, ids_factuges, config)
|
||||||
|
else:
|
||||||
|
logging.info(f"There are no rows in the {
|
||||||
|
config['FACTUGES_NOMBRE_TARIFA']}.")
|
||||||
|
except Exception as e:
|
||||||
|
if cursor_FactuGES is not None:
|
||||||
|
cursor_FactuGES.close()
|
||||||
|
logging.error(f"(ERROR) Failed to fetch from database:{
|
||||||
|
config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}")
|
||||||
|
logging.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
def eliminar_datos(conn_mysql, ids_factuges, config):
|
||||||
|
# Recorrer todos los articulos del catálogo web para ver si estan en filas, si no están se eliminan
|
||||||
|
|
||||||
|
select_all_catalog_query = (
|
||||||
|
"SELECT catalog.id, catalog.id_article FROM catalog"
|
||||||
|
)
|
||||||
|
|
||||||
|
delete_catalog_query = (
|
||||||
|
"DELETE FROM catalog WHERE catalog.id_article = %s"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursorMySQL = None
|
||||||
|
try:
|
||||||
|
cursorMySQL = conn_mysql.cursor()
|
||||||
|
cursorMySQL.execute(select_all_catalog_query)
|
||||||
|
catalog_rows = cursorMySQL.fetchall()
|
||||||
|
logging.info(
|
||||||
|
f">>>>Comprobar que todos los artículos del catálogo existen en FactuGES")
|
||||||
|
|
||||||
|
ids_a_eliminar = [
|
||||||
|
catalog_row[1] # id_article
|
||||||
|
for catalog_row in catalog_rows
|
||||||
|
if str(catalog_row[1]) not in ids_factuges
|
||||||
|
]
|
||||||
|
|
||||||
|
if ids_a_eliminar:
|
||||||
|
logging.info(f"Deleting articles: {ids_a_eliminar}")
|
||||||
|
cursorMySQL.executemany(delete_catalog_query, [(
|
||||||
|
id_article,) for id_article in ids_a_eliminar])
|
||||||
|
else:
|
||||||
|
logging.info("No articles to delete.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
# Cerrar la conexión
|
||||||
|
if cursorMySQL is not None:
|
||||||
|
cursorMySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def insertar_datos(conn_mysql, filas, config):
|
||||||
|
|
||||||
|
insert_catalog_query = (
|
||||||
|
"INSERT INTO catalog (id, catalog_name, id_article, points, retail_price, created_at, updated_at) "
|
||||||
|
"VALUES (%s, %s, %s, %s, %s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)"
|
||||||
|
)
|
||||||
|
|
||||||
|
insert_translation_query = (
|
||||||
|
"INSERT INTO catalog_translations (id, lang_code, catalog_id, description) "
|
||||||
|
"VALUES (%s, %s, %s, %s)"
|
||||||
|
)
|
||||||
|
|
||||||
|
update_catalog_query = (
|
||||||
|
"UPDATE catalog set "
|
||||||
|
"points = %s, "
|
||||||
|
"retail_price = %s, "
|
||||||
|
"updated_at = Now() "
|
||||||
|
"WHERE id_article=%s"
|
||||||
|
)
|
||||||
|
|
||||||
|
update_translation_query = (
|
||||||
|
"UPDATE catalog_translations SET "
|
||||||
|
"description = %s "
|
||||||
|
"WHERE lang_code = %s AND "
|
||||||
|
"catalog_id IN (SELECT catalog.id FROM catalog WHERE catalog.id_article = %s)"
|
||||||
|
)
|
||||||
|
|
||||||
|
select_catalog_query = (
|
||||||
|
"SELECT count(catalog.id) FROM catalog WHERE catalog.id_article = %s"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursorMySQL = None
|
||||||
|
try:
|
||||||
|
cursorMySQL = conn_mysql.cursor()
|
||||||
|
# Insertar datos en la tabla 'catalog'
|
||||||
|
for articulo in filas:
|
||||||
|
# Generar un ID único para la tabla catalog
|
||||||
|
id_catalog = str(uuid4())
|
||||||
|
id_article = int(articulo['ID'])
|
||||||
|
points = int(articulo['PUNTOS'])
|
||||||
|
retail_price = int(articulo['PVP'])
|
||||||
|
tarifa = config['FACTUGES_NOMBRE_TARIFA']
|
||||||
|
|
||||||
|
cursorMySQL.execute(select_catalog_query, (id_article, ))
|
||||||
|
row_count = cursorMySQL.fetchone()
|
||||||
|
is_new = row_count[0] < 1
|
||||||
|
|
||||||
|
if is_new:
|
||||||
|
logging.info(f"Inserting article {id_article} {tarifa}")
|
||||||
|
cursorMySQL.execute(
|
||||||
|
insert_catalog_query, (id_catalog, tarifa, id_article, points, retail_price))
|
||||||
|
else:
|
||||||
|
logging.info(f"Updating article {id_article} {tarifa}")
|
||||||
|
cursorMySQL.execute(update_catalog_query,
|
||||||
|
(points, retail_price, id_article))
|
||||||
|
|
||||||
|
# Insertar traducciones en la tabla 'catalog_translations'
|
||||||
|
for lang_code, desc_key in [('es', 'DESCRIPCION_ES'), ('en', 'DESCRIPCION_EN')]:
|
||||||
|
descripcion_traducida = articulo.get(desc_key, '')
|
||||||
|
if descripcion_traducida:
|
||||||
|
if (is_new):
|
||||||
|
logging.info(f"Inserting translation {
|
||||||
|
lang_code} {descripcion_traducida}")
|
||||||
|
# Generar un ID único para cada traducción
|
||||||
|
id_translation = str(uuid4())
|
||||||
|
cursorMySQL.execute(
|
||||||
|
insert_translation_query, (id_translation, lang_code, id_catalog, descripcion_traducida))
|
||||||
|
else:
|
||||||
|
logging.info(f"Updating translation {
|
||||||
|
lang_code} {descripcion_traducida}")
|
||||||
|
cursorMySQL.execute(
|
||||||
|
update_translation_query, (descripcion_traducida, lang_code, id_article))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
# Cerrar la conexión
|
||||||
|
if cursorMySQL is not None:
|
||||||
|
cursorMySQL.close()
|
||||||
320
app/db/sync_dealers.py
Normal file
320
app/db/sync_dealers.py
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
import logging
|
||||||
|
from uuid import uuid4
|
||||||
|
from config import load_config
|
||||||
|
from utils import hashPassword
|
||||||
|
|
||||||
|
|
||||||
|
def sync_dealers(conn_factuges, conn_mysql, last_execution_date):
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
consulta_factuges = (
|
||||||
|
f"select V_CONTACTOS.ID as ID, V_CONTACTOS.NOMBRE, V_CONTACTOS.IDIOMA_ISO, "
|
||||||
|
f"CLIENTES_DATOS.DIST_EMAIL, CLIENTES_DATOS.DIST_PASSWORD, CLIENTES_DATOS.BLOQUEADO "
|
||||||
|
f"from V_CONTACTOS "
|
||||||
|
f"left OUTER JOIN CLIENTES_DATOS on (V_CONTACTOS.ID = CLIENTES_DATOS.ID_CLIENTE) "
|
||||||
|
f"where (V_CONTACTOS.ID_CATEGORIA = 1) "
|
||||||
|
f"and (V_CONTACTOS.ID_EMPRESA = '{config['FACTUGES_ID_EMPRESA']}') "
|
||||||
|
f"and (CLIENTES_DATOS.TIENDA_WEB = 1) "
|
||||||
|
f"and (V_CONTACTOS.FECHA_MODIFICACION is not null) "
|
||||||
|
f"and (V_CONTACTOS.FECHA_MODIFICACION > '{last_execution_date}')"
|
||||||
|
)
|
||||||
|
|
||||||
|
consulta_dealer_uecko = (
|
||||||
|
"SELECT dealers.id, dealers.id_contact, dealers.user_id, dealers.status, dealers.updated_at "
|
||||||
|
"FROM dealers "
|
||||||
|
"WHERE dealers.id_contact = %s"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor_FactuGES = None
|
||||||
|
try:
|
||||||
|
cursor_FactuGES = conn_factuges.cursor()
|
||||||
|
# Ejecutar la consulta
|
||||||
|
cursor_FactuGES.execute(consulta_factuges)
|
||||||
|
contactos = cursor_FactuGES.fetchall()
|
||||||
|
except Exception as e:
|
||||||
|
if cursor_FactuGES is not None:
|
||||||
|
cursor_FactuGES.close()
|
||||||
|
logging.error(f"(ERROR) Failed to fetch from database:{
|
||||||
|
config['FACTUGES_DATABASE']} - using user:{config['FACTUGES_USER']}")
|
||||||
|
logging.error(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
columnas_contacto = [desc[0] for desc in cursor_FactuGES.description]
|
||||||
|
cursor_FactuGES.close()
|
||||||
|
|
||||||
|
contactos_seleccionados = []
|
||||||
|
for contacto in contactos:
|
||||||
|
tupla = dict(zip(columnas_contacto, contacto))
|
||||||
|
contactos_seleccionados.append(tupla)
|
||||||
|
|
||||||
|
logging.info(f"Contacts rows to be processed: {
|
||||||
|
len(contactos_seleccionados)}")
|
||||||
|
|
||||||
|
if contactos_seleccionados:
|
||||||
|
for contacto in contactos_seleccionados:
|
||||||
|
cursor_MySQL = None
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
cursor_MySQL.execute(consulta_dealer_uecko, (contacto['ID'],))
|
||||||
|
dealer = cursor_MySQL.fetchone()
|
||||||
|
|
||||||
|
if (dealer is None):
|
||||||
|
user_id = insert_user(conn_mysql, contacto, config)
|
||||||
|
insert_dealer(conn_mysql, user_id, contacto, config)
|
||||||
|
logging.info(f"Inserted user and dealer from contact {
|
||||||
|
contacto['ID']} {contacto['NOMBRE']}")
|
||||||
|
else:
|
||||||
|
# 0 => 'ID'
|
||||||
|
# 2 => 'USER_ID'
|
||||||
|
# Casos:
|
||||||
|
# - Cambio en el nombre del distribuidor
|
||||||
|
# - Distribuidor bloqueado / desbloqueado
|
||||||
|
# - Usuario con baja lógica
|
||||||
|
|
||||||
|
id = dealer[0]
|
||||||
|
user_id = dealer[2]
|
||||||
|
|
||||||
|
update_dealer(conn_mysql, id, contacto, config)
|
||||||
|
update_user(conn_mysql, user_id, contacto, config)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
# Cerrar la conexión
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
else:
|
||||||
|
logging.info(
|
||||||
|
"There are no new or modified contacts rows since the last run.")
|
||||||
|
|
||||||
|
# Revisar todos los distribuidores dados de alta y
|
||||||
|
# comprobar si en FactuGES siguen estando activos (tienda_web = 1)
|
||||||
|
#
|
||||||
|
# - USUARIO DISABLED
|
||||||
|
dealers = fetch_all_dealers(conn_mysql, config)
|
||||||
|
for dealer in dealers:
|
||||||
|
# dealer[1] => id_contact
|
||||||
|
if (dealer[1] is not None) and (not is_valid_dealer(conn_factuges, dealer[1], config)):
|
||||||
|
user_id = dealer[7] # 7 => user_id
|
||||||
|
|
||||||
|
# Desactivar el distribuidor
|
||||||
|
disable_dealer(conn_mysql, dealer[0], config) # 0 => id
|
||||||
|
|
||||||
|
# Baja lógica del usuario del dealer
|
||||||
|
soft_delete_user(conn_mysql, user_id, config)
|
||||||
|
logging.info(f"Deleted dealer and user from contact {dealer[1]}")
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_all_dealers(conn_mysql, config):
|
||||||
|
consulta = (
|
||||||
|
f"SELECT dealers.id, dealers.id_contact, dealers.default_payment_method, dealers.default_notes, "
|
||||||
|
f"dealers.default_legal_terms, dealers.default_quote_validity, dealers.status, dealers.user_id "
|
||||||
|
f"FROM dealers "
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor_MySQL = None
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
cursor_MySQL.execute(consulta)
|
||||||
|
return cursor_MySQL.fetchall()
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_dealer(conn_factuges, id_contact, config):
|
||||||
|
consulta = (
|
||||||
|
f"select CLIENTES_DATOS.ID_CLIENTE from CLIENTES_DATOS where CLIENTES_DATOS.ID_CLIENTE = {
|
||||||
|
id_contact} and CLIENTES_DATOS.TIENDA_WEB = 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor_FactuGES = None
|
||||||
|
try:
|
||||||
|
cursor_FactuGES = conn_factuges.cursor()
|
||||||
|
cursor_FactuGES.execute(consulta)
|
||||||
|
exists = cursor_FactuGES.fetchone()
|
||||||
|
return exists is not None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_FactuGES is not None:
|
||||||
|
cursor_FactuGES.close()
|
||||||
|
|
||||||
|
|
||||||
|
def insert_user(conn_mysql, data, config):
|
||||||
|
cursor_MySQL = None
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
id = str(uuid4())
|
||||||
|
name = str(data['NOMBRE'])
|
||||||
|
email = str(data['DIST_EMAIL'])
|
||||||
|
password = hashPassword(str(data['DIST_PASSWORD']))
|
||||||
|
lang_code = str(data['IDIOMA_ISO'])
|
||||||
|
|
||||||
|
insert_data = (
|
||||||
|
"INSERT INTO users (id, name, email, password, lang_code, roles, created_at, updated_at) VALUES ("
|
||||||
|
"%s, %s, %s, %s, %s, 'ROLE_USER', Now(), Now()"
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor_MySQL.execute(
|
||||||
|
insert_data, (id, name, email, password, lang_code))
|
||||||
|
return id
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def update_user(conn_mysql, user_id, data, config):
|
||||||
|
cursor_MySQL = None
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
name = str(data['NOMBRE'])
|
||||||
|
email = str(data['DIST_EMAIL'])
|
||||||
|
password = hashPassword(str(data['DIST_PASSWORD']))
|
||||||
|
lang_code = str(data['IDIOMA_ISO'])
|
||||||
|
|
||||||
|
update_data = (
|
||||||
|
"UPDATE users set "
|
||||||
|
"name = %s, "
|
||||||
|
"email = %s, "
|
||||||
|
"password = %s, "
|
||||||
|
"lang_code = %s, "
|
||||||
|
"updated_at = Now(), "
|
||||||
|
"deleted_at = NULL "
|
||||||
|
"WHERE id = %s"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor_MySQL.execute(
|
||||||
|
update_data, (name, email, password, lang_code, user_id))
|
||||||
|
logging.info(f"Updated user from contact {data['ID']} {name}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def insert_dealer(conn_mysql, user_id, data, config):
|
||||||
|
cursor_MySQL = None
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
id = str(uuid4())
|
||||||
|
id_contact = str(data['ID'])
|
||||||
|
name = str(data['NOMBRE'])
|
||||||
|
default_payment_method = str(config['UECKO_DEFAULT_FORMA_PAGO'])
|
||||||
|
default_notes = str(config['UECKO_DEFAULT_NOTAS'])
|
||||||
|
default_legal_terms = str(config['UECKO_DEFAULT_LOPD'])
|
||||||
|
default_quote_validity = str(config['UECKO_DEFAULT_VALIDEZ'])
|
||||||
|
default_tax = str(config["UECKO_DEFAULT_IVA"])
|
||||||
|
lang_code = str(data['IDIOMA_ISO'])
|
||||||
|
currency_code = str(config["UECKO_DEFAULT_CURRENCY_CODE"])
|
||||||
|
|
||||||
|
insert_data = (
|
||||||
|
"INSERT INTO dealers (id, id_contact, name, default_payment_method, default_notes, "
|
||||||
|
"default_legal_terms, default_quote_validity, default_tax, lang_code, "
|
||||||
|
"currency_code, user_id, status, created_at, updated_at ) values ("
|
||||||
|
"%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'actived', Now(), Now()"
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor_MySQL.execute(insert_data, (id, id_contact, name, default_payment_method, default_notes,
|
||||||
|
default_legal_terms, default_quote_validity, default_tax, lang_code, currency_code, user_id))
|
||||||
|
return id
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def update_dealer(conn_mysql, dealer_id, data, config):
|
||||||
|
cursor_MySQL = None
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
name = str(data['NOMBRE'])
|
||||||
|
status = 'disabled' if data['BLOQUEADO'] == 1 else 'actived'
|
||||||
|
|
||||||
|
insert_data = (
|
||||||
|
"UPDATE dealers SET name = %s, status = %s WHERE dealers.id = %s"
|
||||||
|
)
|
||||||
|
cursor_MySQL.execute(insert_data, (name, status, dealer_id))
|
||||||
|
logging.info(f"Dealer with id = {dealer_id} name = {name} updated")
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def soft_delete_user(conn_mysql, user_id, config):
|
||||||
|
consulta_sql = "UPDATE users SET users.deleted_at = NOW() WHERE users.id = %s AND users.roles = 'ROLE_USER'"
|
||||||
|
cursor_MySQL = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
cursor_MySQL.execute(consulta_sql, (user_id, ))
|
||||||
|
logging.info(f"User with id = {id} soft delete")
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def active_dealer(conn_mysql, id, config):
|
||||||
|
consulta_sql = "UPDATE dealers SET status = 'actived' WHERE dealers.id = %s"
|
||||||
|
cursor_MySQL = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
cursor_MySQL.execute(consulta_sql, (id, ))
|
||||||
|
logging.info(f"Dealer with id = {id} actived")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def disable_dealer(conn_mysql, id, config):
|
||||||
|
consulta_sql = "UPDATE dealers SET status = 'disabled' WHERE dealers.id = %s"
|
||||||
|
cursor_MySQL = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
cursor_MySQL.execute(consulta_sql, (id, ))
|
||||||
|
logging.info(f"Dealer with id = {id} disabled")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(str(e))
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
216
app/db/sync_orders.py
Normal file
216
app/db/sync_orders.py
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
import logging
|
||||||
|
from decimal import Decimal
|
||||||
|
from config import load_config
|
||||||
|
from utils import text_converter
|
||||||
|
|
||||||
|
|
||||||
|
def sync_orders(conn_factuges, conn_mysql):
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
consulta_quotes_uecko = (
|
||||||
|
"SELECT quotes.id, quotes.date_sent, quotes.reference, quotes.customer_reference, "
|
||||||
|
"quotes.customer_information, quotes.dealer_id, dealers.id_contact, dealers.name "
|
||||||
|
"FROM quotes INNER JOIN dealers ON (dealers.id = quotes.dealer_id) "
|
||||||
|
"WHERE quotes.date_sent IS NOT NULL AND "
|
||||||
|
"quotes.id_contract IS NULL"
|
||||||
|
)
|
||||||
|
|
||||||
|
update_quotes_uecko = (
|
||||||
|
"UPDATE quotes SET "
|
||||||
|
"id_contract = %s "
|
||||||
|
"WHERE id = %s"
|
||||||
|
)
|
||||||
|
|
||||||
|
inserted_orders = []
|
||||||
|
cursor_MySQL = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
cursor_MySQL.execute(consulta_quotes_uecko)
|
||||||
|
quotes = cursor_MySQL.fetchall()
|
||||||
|
|
||||||
|
quote_columns = [desc[0] for desc in cursor_MySQL.description]
|
||||||
|
selected_quotes = []
|
||||||
|
|
||||||
|
for quote in quotes:
|
||||||
|
tupla = dict(zip(quote_columns, quote))
|
||||||
|
selected_quotes.append(tupla)
|
||||||
|
|
||||||
|
logging.info(f"Quotes rows to be processed: {len(selected_quotes)}")
|
||||||
|
if selected_quotes:
|
||||||
|
|
||||||
|
for quote in selected_quotes:
|
||||||
|
logging.info(f"Quote reference: {quote['reference']}")
|
||||||
|
|
||||||
|
if (quote['id_contact'] is None):
|
||||||
|
logging.info(
|
||||||
|
f"Error: Quote unprocesable (id_contact missing)")
|
||||||
|
continue
|
||||||
|
|
||||||
|
items = fetch_quote_items(conn_mysql, quote['id'])
|
||||||
|
|
||||||
|
id_contrato = insert_quote_to_factuges(
|
||||||
|
conn_factuges, quote, items, config)
|
||||||
|
cursor_MySQL.execute(update_quotes_uecko,
|
||||||
|
(int(id_contrato), str(quote['id'])))
|
||||||
|
|
||||||
|
inserted_orders.append({
|
||||||
|
"customer_reference": quote['customer_reference'],
|
||||||
|
"dealer_name": quote['name'],
|
||||||
|
})
|
||||||
|
|
||||||
|
cursor_MySQL.close()
|
||||||
|
return inserted_orders
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(msg=e, stack_info=True)
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
# Cerrar la conexión
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_quote_items(conn_mysql, quote_id):
|
||||||
|
consulta_quotes_items_uecko = (
|
||||||
|
"SELECT quote_items.item_id, quote_items.id_article, quote_items.position, "
|
||||||
|
"quote_items.description, quote_items.quantity, quote_items.unit_price, "
|
||||||
|
"quote_items.discount, quote_items.total_price "
|
||||||
|
"FROM quote_items "
|
||||||
|
"WHERE quote_items.quote_id = %s "
|
||||||
|
"ORDER BY quote_items.position"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor_MySQL = None
|
||||||
|
try:
|
||||||
|
cursor_MySQL = conn_mysql.cursor()
|
||||||
|
cursor_MySQL.execute(consulta_quotes_items_uecko, (quote_id, ))
|
||||||
|
items = cursor_MySQL.fetchall()
|
||||||
|
|
||||||
|
items_columns = [desc[0] for desc in cursor_MySQL.description]
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
selected_items = []
|
||||||
|
for item in items:
|
||||||
|
tupla = dict(zip(items_columns, item))
|
||||||
|
selected_items.append(tupla)
|
||||||
|
return selected_items
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(msg=e, stack_info=True)
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_MySQL is not None:
|
||||||
|
cursor_MySQL.close()
|
||||||
|
|
||||||
|
|
||||||
|
def insert_quote_to_factuges(conn_factuges, quote, items, config):
|
||||||
|
id_empresa = int(config['FACTUGES_ID_EMPRESA'])
|
||||||
|
situacion = str(config['FACTUGES_CONTRATO_SITUACION'])
|
||||||
|
id_tienda = int(config['FACTUGES_CONTRATO_ID_TIENDA'])
|
||||||
|
enviada_revisada = int(config['FACTUGES_CONTRATO_ENVIADA_REVISADA'])
|
||||||
|
id_cliente = int(quote['id_contact'])
|
||||||
|
# nombre_clliente = str(quote['name'])
|
||||||
|
fecha_presupuesto = quote['date_sent'].date()
|
||||||
|
persona_contacto = str(quote['customer_information'])
|
||||||
|
referencia_cliente = str(quote['customer_reference'])
|
||||||
|
|
||||||
|
select_gen_id_contrato_cliente = (
|
||||||
|
"select GEN_ID(GEN_CONTRATOS_CLI_ID, 1) from RDB$DATABASE"
|
||||||
|
)
|
||||||
|
|
||||||
|
select_gen_id_presupuesto_cliente = (
|
||||||
|
"select GEN_ID(GEN_PRESUPUESTOS_CLI_ID, 1) from RDB$DATABASE"
|
||||||
|
)
|
||||||
|
|
||||||
|
insert_contrato_cliente_data = (
|
||||||
|
"insert into CONTRATOS_CLIENTE ("
|
||||||
|
"ID, ID_EMPRESA, ID_TIENDA, ID_CLIENTE, NOMBRE, SITUACION, "
|
||||||
|
"NOTAS_ENVIO, REFERENCIA_CLIENTE, "
|
||||||
|
"ENVIADA_REVISADA, FECHA_CONTRATO "
|
||||||
|
") values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
)
|
||||||
|
|
||||||
|
insert_presupuesto_cliente_data = (
|
||||||
|
"insert into PRESUPUESTOS_CLIENTE ("
|
||||||
|
"ID, ID_EMPRESA, ID_TIENDA, ID_CLIENTE, SITUACION, "
|
||||||
|
"OBSERVACIONES, REFERENCIA_CLIENTE, "
|
||||||
|
"ENVIADA_REVISADA, FECHA_PRESUPUESTO "
|
||||||
|
") values (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
)
|
||||||
|
|
||||||
|
insert_contrato_cliente_detalles_data = (
|
||||||
|
"insert into CONTRATOS_CLIENTE_DETALLES ("
|
||||||
|
"ID, ID_CONTRATO, POSICION, ID_ARTICULO, TIPO_DETALLE, "
|
||||||
|
"CONCEPTO, CANTIDAD, IMPORTE_UNIDAD, "
|
||||||
|
"VALORADO, VISIBLE, FECHA_ALTA "
|
||||||
|
") values ("
|
||||||
|
"GEN_ID(GEN_CONTRATOS_CLI_DETALLE_ID, 1), ?, ?, ?, ?, "
|
||||||
|
"?, ?, ?, "
|
||||||
|
"1, 1, CURRENT_TIMESTAMP"
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
insert_presupuesto_cliente_detalles_data = (
|
||||||
|
"insert into PRESUPUESTOS_CLIENTE_DETALLES ("
|
||||||
|
"ID, ID_PRESUPUESTO, POSICION, ID_ARTICULO, TIPO_DETALLE, "
|
||||||
|
"CONCEPTO, CANTIDAD, IMPORTE_UNIDAD, "
|
||||||
|
"VALORADO, VISIBLE, FECHA_ALTA "
|
||||||
|
") values ("
|
||||||
|
"GEN_ID(GEN_PRESUPUESTOS_CLI_DETALLE_ID, 1), ?, ?, ?, ?, "
|
||||||
|
"?, ?, ?, "
|
||||||
|
"1, 1, CURRENT_TIMESTAMP"
|
||||||
|
")"
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor_FactuGES = None
|
||||||
|
try:
|
||||||
|
cursor_FactuGES = conn_factuges.cursor()
|
||||||
|
cursor_FactuGES.execute(select_gen_id_presupuesto_cliente)
|
||||||
|
id_presupuesto = int(cursor_FactuGES.fetchone()[0])
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
f"Inserting quote on FactuGES -> id_preupuesto = {str(id_presupuesto)}")
|
||||||
|
logging.info(insert_presupuesto_cliente_data)
|
||||||
|
logging.info((id_presupuesto, id_empresa, id_tienda, id_cliente,
|
||||||
|
situacion, fecha_presupuesto, persona_contacto,
|
||||||
|
referencia_cliente, enviada_revisada, fecha_presupuesto))
|
||||||
|
|
||||||
|
cursor_FactuGES.execute(insert_presupuesto_cliente_data,
|
||||||
|
(id_presupuesto, id_empresa, id_tienda, id_cliente,
|
||||||
|
situacion, persona_contacto,
|
||||||
|
referencia_cliente, enviada_revisada, fecha_presupuesto))
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
f"Inserting items. Quote items length to be processed: {len(items)}")
|
||||||
|
for item in items:
|
||||||
|
descripcion_iso = text_converter(
|
||||||
|
item['description'], charset_destino='ISO8859_1', longitud_maxima=2000)
|
||||||
|
quantity = Decimal(
|
||||||
|
int(item['quantity'])) / Decimal(100) if item['quantity'] is not None else None
|
||||||
|
unit_price = Decimal(int(
|
||||||
|
item['unit_price'])) / Decimal(100) if item['unit_price'] is not None else None
|
||||||
|
# total_price = item['total_price']
|
||||||
|
|
||||||
|
logging.info(str(insert_presupuesto_cliente_detalles_data))
|
||||||
|
logging.info((
|
||||||
|
id_presupuesto, item['position'], item['id_article'], config['FACTUGES_CONTRATO_TIPO_DETALLE'],
|
||||||
|
descripcion_iso, quantity, unit_price
|
||||||
|
))
|
||||||
|
|
||||||
|
cursor_FactuGES.execute(insert_presupuesto_cliente_detalles_data, (
|
||||||
|
id_presupuesto, item['position'], item['id_article'], config['FACTUGES_CONTRATO_TIPO_DETALLE'],
|
||||||
|
descripcion_iso, quantity, unit_price
|
||||||
|
))
|
||||||
|
|
||||||
|
cursor_FactuGES.close()
|
||||||
|
return id_presupuesto
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Escribir el error en el archivo de errores
|
||||||
|
logging.error(msg=e, stack_info=True)
|
||||||
|
raise e # Re-lanzar la excepción para detener el procesamiento
|
||||||
|
finally:
|
||||||
|
if cursor_FactuGES is not None:
|
||||||
|
cursor_FactuGES.close()
|
||||||
82
app/main.py
Normal file
82
app/main.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import __version__
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from dateutil import tz
|
||||||
|
from config import setup_logging, load_config
|
||||||
|
from db import get_mysql_connection, get_factuges_connection, sync_catalog, sync_dealers, sync_orders
|
||||||
|
from utils import obtener_fecha_ultima_ejecucion, actualizar_fecha_ultima_ejecucion, log_system_metrics, send_orders_mail
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
# Cargar la configuración
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
local_tz = tz.gettz(config['LOCAL_TZ'])
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
setup_logging()
|
||||||
|
|
||||||
|
logging.info("== START ==")
|
||||||
|
logging.info(f"Version: {__version__.__version__}")
|
||||||
|
logging.info(f"Environment: {config['ENVIRONMENT']}")
|
||||||
|
log_system_metrics()
|
||||||
|
|
||||||
|
conn_factuges = None
|
||||||
|
conn_mysql = None
|
||||||
|
try:
|
||||||
|
# Obtener la fecha de la última ejecución del programa
|
||||||
|
last_execution_date_utc = obtener_fecha_ultima_ejecucion()
|
||||||
|
last_execution_date_local_tz = last_execution_date_utc.astimezone(
|
||||||
|
tz=local_tz).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
logging.info("Last execution (UTC): %s",
|
||||||
|
last_execution_date_utc.strftime("%Y-%m-%d %H:%M:%S %Z"))
|
||||||
|
logging.info("Last execution (Local time): %s",
|
||||||
|
last_execution_date_local_tz)
|
||||||
|
|
||||||
|
conn_factuges = get_factuges_connection(config)
|
||||||
|
conn_mysql = get_mysql_connection(config)
|
||||||
|
|
||||||
|
# Sync catalog
|
||||||
|
sync_catalog(conn_factuges, conn_mysql, last_execution_date_local_tz)
|
||||||
|
sync_dealers(conn_factuges, conn_mysql, last_execution_date_local_tz)
|
||||||
|
inserted_orders = sync_orders(
|
||||||
|
conn_factuges, conn_mysql)
|
||||||
|
|
||||||
|
actualizar_fecha_ultima_ejecucion()
|
||||||
|
|
||||||
|
# Confirmar los cambios
|
||||||
|
conn_mysql.commit()
|
||||||
|
conn_factuges.commit()
|
||||||
|
|
||||||
|
# Enviar email
|
||||||
|
send_orders_mail(inserted_orders)
|
||||||
|
|
||||||
|
logging.info("== END (0) ==")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Se ha producido un error en la última ejecución.")
|
||||||
|
logging.error(e)
|
||||||
|
logging.info("== END (1) ==")
|
||||||
|
|
||||||
|
if conn_mysql is not None:
|
||||||
|
conn_mysql.rollback()
|
||||||
|
|
||||||
|
if conn_factuges is not None:
|
||||||
|
conn_factuges.rollback()
|
||||||
|
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if conn_factuges:
|
||||||
|
conn_factuges.close()
|
||||||
|
if conn_mysql:
|
||||||
|
conn_mysql.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
5
app/utils/__init__.py
Normal file
5
app/utils/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
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 .send_orders_mail import send_orders_mail
|
||||||
|
from .text_converter import text_converter
|
||||||
21
app/utils/last_execution_helper.py
Normal file
21
app/utils/last_execution_helper.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from datetime import datetime, timezone
|
||||||
|
from dateutil import tz
|
||||||
|
|
||||||
|
# Función para obtener la fecha de la última ejecución del programa desde un archivo de texto
|
||||||
|
|
||||||
|
|
||||||
|
def obtener_fecha_ultima_ejecucion():
|
||||||
|
try:
|
||||||
|
with open('./last_execution.txt', 'r', encoding="utf8") as f:
|
||||||
|
fecha_str = f.read().strip()
|
||||||
|
return datetime.strptime(fecha_str, '%Y-%m-%d %H:%M:%S').astimezone(tz=tz.UTC)
|
||||||
|
except FileNotFoundError:
|
||||||
|
# Si el archivo no existe, se asume que el programa nunca se ha ejecutado antes
|
||||||
|
return datetime(2024, 1, 1, 0, 0, 0).astimezone(tz=tz.UTC)
|
||||||
|
|
||||||
|
# Función para actualizar la fecha de la última ejecución del programa en el archivo de texto
|
||||||
|
|
||||||
|
|
||||||
|
def actualizar_fecha_ultima_ejecucion():
|
||||||
|
with open('./last_execution.txt', 'w', encoding="utf8") as f:
|
||||||
|
f.write(datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
9
app/utils/log_system_metrics.py
Normal file
9
app/utils/log_system_metrics.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
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}%')
|
||||||
5
app/utils/password.py
Normal file
5
app/utils/password.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import bcrypt
|
||||||
|
|
||||||
|
def hashPassword(plain_text_password):
|
||||||
|
salt = bcrypt.gensalt(rounds=10)
|
||||||
|
return bcrypt.hashpw(plain_text_password.encode('utf-8'), salt)
|
||||||
29
app/utils/send_orders_mail.py
Normal file
29
app/utils/send_orders_mail.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import logging
|
||||||
|
import brevo_python
|
||||||
|
|
||||||
|
from brevo_python.rest import ApiException
|
||||||
|
from config import setup_brevo
|
||||||
|
from brevo_python.rest import ApiException
|
||||||
|
from config import load_config
|
||||||
|
|
||||||
|
def send_orders_mail(inserted_orders):
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
try:
|
||||||
|
configuration = setup_brevo(config)
|
||||||
|
api_instance = brevo_python.TransactionalEmailsApi(brevo_python.ApiClient(configuration))
|
||||||
|
|
||||||
|
for order in inserted_orders:
|
||||||
|
send_smtp_email = brevo_python.SendSmtpEmail(
|
||||||
|
to=[{'email':config['MAIL_TO']}],
|
||||||
|
subject=f"Nuevo pedido del distribuidor {order["dealer_name"]}",
|
||||||
|
template_id=int(config["BREVO_EMAIL_TEMPLATE"]),
|
||||||
|
params={
|
||||||
|
"customer_reference": order["customer_reference"],
|
||||||
|
"dealer_name": order["dealer_name"]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
api_response = api_instance.send_transac_email(send_smtp_email)
|
||||||
|
logging.info(msg=api_response)
|
||||||
|
except ApiException as e:
|
||||||
|
logging.error(msg=e)
|
||||||
38
app/utils/text_converter.py
Normal file
38
app/utils/text_converter.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def text_converter(texto, charset_destino='ISO8859_1', longitud_maxima=None):
|
||||||
|
"""
|
||||||
|
Convierte un texto al charset especificado, eliminando caracteres incompatibles.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
texto (str): El texto a convertir.
|
||||||
|
charset_destino (str): El charset de destino (por defecto 'ISO8859_1').
|
||||||
|
longitud_maxima (int, opcional): La longitud máxima permitida para el texto convertido.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: El texto convertido al charset de destino.
|
||||||
|
"""
|
||||||
|
if not texto:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Convertir el texto al charset especificado
|
||||||
|
texto_convertido = texto.encode(
|
||||||
|
charset_destino, 'ignore').decode(charset_destino)
|
||||||
|
|
||||||
|
# Si se especifica una longitud máxima, truncar el texto
|
||||||
|
if longitud_maxima and len(texto_convertido) > longitud_maxima:
|
||||||
|
logging.warning(
|
||||||
|
f"El texto ha sido truncado de {len(texto_convertido)} a {longitud_maxima} caracteres.")
|
||||||
|
texto_convertido = texto_convertido[:longitud_maxima]
|
||||||
|
|
||||||
|
return texto_convertido
|
||||||
|
|
||||||
|
except UnicodeEncodeError as e:
|
||||||
|
logging.error(
|
||||||
|
f"Error al convertir texto a {charset_destino}: {str(e)}")
|
||||||
|
return ""
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error inesperado al convertir texto: {str(e)}")
|
||||||
|
return ""
|
||||||
1
cronjob
Normal file
1
cronjob
Normal file
@ -0,0 +1 @@
|
|||||||
|
*/5 * * * * /usr/local/bin/python /opt/uecko_sync_app/app/main.py >> /var/log/cron.log 2>&1
|
||||||
17
docker-compose.yml
Normal file
17
docker-compose.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
services:
|
||||||
|
uecko_sync:
|
||||||
|
container_name: uecko_sync_app:v1.0.8
|
||||||
|
env_file: ".env.production"
|
||||||
|
build: .
|
||||||
|
#volumes:
|
||||||
|
#- logs:/var/log/uecko_sync_app
|
||||||
|
networks:
|
||||||
|
- presupuestador-uecko_private
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
#volumes:
|
||||||
|
# logs:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
presupuestador-uecko_private:
|
||||||
|
external: true
|
||||||
BIN
firebird/fbudflib2.so
Normal file
BIN
firebird/fbudflib2.so
Normal file
Binary file not shown.
1171
firebird/install.sh
Normal file
1171
firebird/install.sh
Normal file
File diff suppressed because it is too large
Load Diff
7
firebird/launch.sh
Normal file
7
firebird/launch.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
/etc/init.d/xinetd start
|
||||||
|
$FIREBIRD_PATH/bin/gsec -user SYSDBA -password $FIREBIRD_DB_PASSWORD_DEFAULT -modify SYSDBA -pw $FIREBIRD_DB_PASSWORD
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
echo "[`date`] --> Firebird running..."
|
||||||
|
done
|
||||||
1191
firebird/postinstall.sh
Normal file
1191
firebird/postinstall.sh
Normal file
File diff suppressed because it is too large
Load Diff
61
readme.md
Normal file
61
readme.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
Crear el entorno por primera vez:
|
||||||
|
---------------------------------
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate <-- en linux
|
||||||
|
.\venv\Scripts\activat <-- en Windows
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
|
||||||
|
Lanzar el entorno para hacer pruebas del script:
|
||||||
|
-----------------------------------------------
|
||||||
|
source venv/bin/activate <-- en linux
|
||||||
|
.\venv\Scripts\activat <-- en Windows
|
||||||
|
python app\main.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
git clone ssh://git@wopr.rodax-software.com:30001/uecko/presupuestador-web---scripts-sync.git uecko-sync-scripts
|
||||||
|
cd uecko-sync-scripts/
|
||||||
|
cp .env-sample .env
|
||||||
|
pip install -r requirements.txt
|
||||||
|
python3 -m venv env
|
||||||
|
sudo apt install python3.11-venv
|
||||||
|
python3 -m venv env
|
||||||
|
source venv/bin/activate
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
python3 factuges_catalog_to_json_file.py
|
||||||
|
|
||||||
|
> Reconstruir imagen docker
|
||||||
|
docker compose up --build -d
|
||||||
|
|
||||||
|
|
||||||
|
> Instalar Firebird 2.1
|
||||||
|
1. Descargar paquete: https://master.dl.sourceforge.net/project/firebird/firebird-linux-amd64/2.1.7-Release/FirebirdSS-2.1.7.18553-0.amd64.tar.gz?viasf=1
|
||||||
|
2. Descomprimir: tar -xvf <paquete>
|
||||||
|
3. Lanzar instalación: sudo ./install.sh
|
||||||
|
4. Si da error, da igual. El caso es que en /opt/firebird estén los ficheros y en <lib> las librerías.
|
||||||
|
5. Crear enlaces simbólicos:
|
||||||
|
Busque la librería libfbclient.so.2.m.n (m.n es el nro. menor de versión más el nro. de actualización) en /opt/firebird/lib del equipo donde está instalado el servidor Firebird. Cópiela a /usr/lib en el cliente.
|
||||||
|
|
||||||
|
Cree enlaces simbólicos usando los siguientes comandos:
|
||||||
|
|
||||||
|
ln -s /usr/lib/libfbclient.so.2.m.n /usr/lib/libfbclient.so.2
|
||||||
|
|
||||||
|
ln -s /usr/lib/libfbclient.so.2 /usr/lib/libfbclient.so
|
||||||
|
|
||||||
|
reemplazando 2.m.n con su número de versión, por ejemplo 2.1.7
|
||||||
|
|
||||||
|
Si Ud. está ejecutando aplicaciones que esperan que las librerías antiguas estén presentes, cree también los siguientes enlaces simbólicos:
|
||||||
|
|
||||||
|
ln -s /usr/lib/libfbclient.so /usr/lib/libgds.so.0
|
||||||
|
|
||||||
|
ln -s /usr/lib/libfbclient.so /usr/lib/libgds.so
|
||||||
|
|
||||||
|
Copie el archivo firebird.msg a /opt/firebird
|
||||||
|
|
||||||
|
En el perfil por defecto del sistema, o usando setenv() desde una consola, cree la variable de entorno FIREBIRD y apúntela al directorio /opt/firebird, para permitir a las rutinas de la API localizar los mensajes.
|
||||||
|
Para ello, editar con sudo nano /etc/profile y añadir FIREBIRD=/opt/firebird
|
||||||
|
|
||||||
|
6. sudo apt-get install libncurses5
|
||||||
27
requeriments.txt
Normal file
27
requeriments.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
about-time==4.2.1
|
||||||
|
alive-progress==3.1.5
|
||||||
|
bcrypt==4.1.3
|
||||||
|
black==24.8.0
|
||||||
|
brevo-python==1.1.2
|
||||||
|
certifi==2024.8.30
|
||||||
|
cffi==1.16.0
|
||||||
|
click==8.1.7
|
||||||
|
colorama==0.4.6
|
||||||
|
cryptography==42.0.8
|
||||||
|
fdb==2.0.2
|
||||||
|
future==0.18.3
|
||||||
|
grapheme==0.6.0
|
||||||
|
mypy-extensions==1.0.0
|
||||||
|
mysql-connector-python==8.4.0
|
||||||
|
packaging==24.1
|
||||||
|
paramiko==3.4.0
|
||||||
|
pathspec==0.12.1
|
||||||
|
platformdirs==4.3.6
|
||||||
|
psutil==6.0.0
|
||||||
|
pycparser==2.22
|
||||||
|
PyNaCl==1.5.0
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
python-dotenv==1.0.0
|
||||||
|
six==1.16.0
|
||||||
|
sshtunnel==0.4.0
|
||||||
|
urllib3==2.2.3
|
||||||
Loading…
Reference in New Issue
Block a user