import logging from uuid import uuid4 from config import load_config from decimal import Decimal def sync_invoices(conn_factuges, conn_mysql, last_execution_date): # logging.info("ENTROOO") 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_FACTURAS_CLIENTE = ( f"SELECT fac.VERIFACTU, fac.ID_VERIFACTU, fac.ID || '' AS ID, fac.ID_EMPRESA || '' AS ID_EMPRESA, fac.REFERENCIA, fac.FECHA_FACTURA, fac.ID_CLIENTE || '' as ID_CLIENTE, fac.NIF_CIF, fac.NOMBRE, " f"fac.CALLE, fac.POBLACION, fac.PROVINCIA, fac.CODIGO_POSTAL, fac.FECHA_ALTA, " f"fac.IMPORTE_NETO, fac.DESCUENTO, fac.IMPORTE_DESCUENTO, fac.BASE_IMPONIBLE, fac.IMPORTE_IVA, fac.IMPORTE_TOTAL, " f"fac.ID_FORMA_PAGO, fp.DESCRIPCION as DES_FORMA_PAGO, " f"fac.ID_CLIENTE, fac.NIF_CIF, fac.NOMBRE, fac.CALLE, fac.POBLACION, fac.PROVINCIA, fac.CODIGO_POSTAL " f"FROM FACTURAS_CLIENTE AS fac " f"LEFT JOIN FORMAS_PAGO AS fp ON fac.ID_FORMA_PAGO = fp.ID " f"WHERE " f"(fac.VERIFACTU > 0) " f"AND (fac.ID_VERIFACTU is null)" ) # Crear un cursor para ejecutar consultas SQL cursor_FactuGES = None try: cursor_FactuGES = conn_factuges.cursor() # Ejecutar la consulta de FACTURAS_CLIENTE cursor_FactuGES.execute(consulta_sql_FACTURAS_CLIENTE) 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"FACTURAS_CLIENTE rows to be processed: {len(tuplas_seleccionadas)}") # Verificar si hay filas en el resultado if tuplas_seleccionadas: insertar_datos(conn_mysql, tuplas_seleccionadas, conn_factuges, config) else: logging.info( "There are no new or modified FACTURAS 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_FACTURAS_CLIENTE) 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"customer_invoice 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, conn_factuges, config): insert_customer_invoices_query = ( "INSERT INTO customer_invoices (id, invoice_status, invoice_series, invoice_number, invoice_date, operation_date, " "subtotal_amount_value, discount_amount_value, discount_percentaje_value, taxable_amount_value, tax_amount_value, total_amount_value, " "payment_method_id, payment_method_description, " "customer_id, customer_tin, customer_name, customer_street, customer_city, customer_state, customer_postal_code, customer_country, " "subtotal_amount_scale, discount_amount_scale, discount_percentaje_scale, taxable_amount_scale, tax_amount_scale, total_amount_scale, " "invoice_language, invoice_currency, created_at, updated_at) " "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 2, 2, 2, 2, 2, 2, 'es', 'EUR', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" ) insert_customer_query = ( "INSERT INTO customers (id, name, tin, street, city, state, postal_code, country, factuges_id, " # "email, phone, fax, website, legal_record, default_tax," "is_company, lang_code, currency_code, status, created_at, updated_at) " "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, 1, 'es', 'EUR', 'active', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" ) update_FACTURAS_CLIENTE_query = ( "UPDATE FACTURAS_CLIENTE set ID_VERIFACTU = ? WHERE ID = ?") 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_customer_query = ( "SELECT customers.id FROM customers WHERE customers.factuges_id = %s" ) cursorMySQL = None cursor_FactuGES = None try: cursorMySQL = conn_mysql.cursor() cursor_FactuGES = conn_factuges.cursor() # Insertar datos en la tabla 'customer_invoices' for factura in filas: # Generar un ID único para la tabla customer_invoices id_customer_invoice = str(uuid4()) factuges_id = int(factura['ID']) invoice_status = str('draft') invoice_series = str('A') invoice_number = str(factura['REFERENCIA']) invoice_date = str(factura['FECHA_FACTURA']) operation_date = str(factura['FECHA_FACTURA']) # siempre tendrán 2 decimales subtotal_amount_value = (factura['IMPORTE_NETO'])*100 discount_amount_value = (factura['IMPORTE_DESCUENTO'])*100 discount_percentaje_value = ( factura['DESCUENTO'])*100 if (factura['DESCUENTO']) is not None else None taxable_amount_value = (factura['BASE_IMPONIBLE'])*100 tax_amount_value = (factura['IMPORTE_IVA'])*100 total_amount_value = (factura['IMPORTE_TOTAL'])*100 payment_method_id = str(factura['ID_FORMA_PAGO']) payment_method_description = str(factura['DES_FORMA_PAGO']) customer_id = str(uuid4()) factuges_customer_id = str(factura['ID_CLIENTE']) customer_tin = str(factura['NIF_CIF']) customer_name = str(factura['NOMBRE']) customer_street = str(factura['CALLE']) customer_city = str(factura['POBLACION']) customer_state = str(factura['PROVINCIA']) customer_postal_code = str(factura['CODIGO_POSTAL']) customer_country = 'es' # campos pendiente de revisar en un futuro # xxxxxxx = str(factura['ID_EMPRESA']) # xxxxxxx = str(factura['ID_FORMA_PAGO']) según este id se debe de guardar en la factura los vencimiento asociados a la forma de pago # xxxxxxx = str(factura['OBSERVACIONES']) # RE, IMPORTE_RE >> en el caso que este relleno debo trasladarlo a los detalles de la factura # Comprobamos si existe el cliente cursorMySQL.execute(select_customer_query, (factuges_customer_id, )) row = cursorMySQL.fetchone() is_new = (row is None) or (row[0] is None) if is_new: logging.info( f"Inserting customer {factuges_customer_id} {customer_tin} {customer_name}") cursorMySQL.execute(insert_customer_query, (customer_id, customer_name, customer_tin, customer_street, customer_city, customer_state, customer_postal_code, customer_country, factuges_customer_id)) else: # Si ya exite ponemos el id del customer correspondiente customer_id = str(row[0]) logging.info( f"Updating customer {factuges_customer_id} {customer_id}") # cursorMySQL.execute(update_catalog_query, # (points, retail_price, id_article)) # Insertamos la factura logging.info( f"Inserting customer_invoice {invoice_number} {invoice_date}") cursorMySQL.execute(insert_customer_invoices_query, (id_customer_invoice, invoice_status, invoice_series, invoice_number, invoice_date, operation_date, subtotal_amount_value, discount_amount_value, discount_percentaje_value, taxable_amount_value, tax_amount_value, total_amount_value, payment_method_id, payment_method_description, customer_id, customer_tin, customer_name, customer_street, customer_city, customer_state, customer_postal_code, customer_country)) # Guardamos en Factuges el id de la customer_invoice logging.info( f"Updating FACTURAS_CLIENTE {id_customer_invoice} {factuges_id}") cursor_FactuGES.execute( update_FACTURAS_CLIENTE_query, (id_customer_invoice, factuges_id)) # 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() if cursor_FactuGES is not None: cursor_FactuGES.close()