refactorizacion
This commit is contained in:
parent
502c9fc617
commit
58c73ef9b2
@ -1,5 +1,5 @@
|
|||||||
# =========================
|
# =========================
|
||||||
# SQL (constantes)
|
# MYSQL (constantes)
|
||||||
# =========================
|
# =========================
|
||||||
SELECT_CUSTOMER_BY_FACTUGES = (
|
SELECT_CUSTOMER_BY_FACTUGES = (
|
||||||
"SELECT customers.id FROM customers WHERE customers.factuges_id=%s"
|
"SELECT customers.id FROM customers WHERE customers.factuges_id=%s"
|
||||||
@ -30,6 +30,27 @@ INSERT_PAYMENT_METHOD = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
INSERT_INVOICE = (
|
INSERT_INVOICE = (
|
||||||
|
"INSERT INTO customer_invoices (id, company_id, invoice_number, status, series, reference, invoice_date, operation_date, description, "
|
||||||
|
"subtotal_amount_value, discount_amount_value, discount_percentage_value, taxable_amount_value, taxes_amount_value, total_amount_value, "
|
||||||
|
"customer_id, customer_tin, customer_name, customer_street, customer_city, customer_province, customer_postal_code, customer_country, "
|
||||||
|
"payment_method_id, payment_method_description, factuges_id, "
|
||||||
|
"subtotal_amount_scale, discount_amount_scale, discount_percentage_scale, taxable_amount_scale, taxes_amount_scale, total_amount_scale, "
|
||||||
|
"language_code, currency_code, created_at, updated_at) "
|
||||||
|
"SELECT "
|
||||||
|
"%s AS id, "
|
||||||
|
"%s AS company_id, "
|
||||||
|
"COALESCE(MAX(invoice_number + 0),0)+1 AS invoice_number, "
|
||||||
|
"%s AS status, %s AS series, %s AS reference, %s AS invoice_date, %s AS operation_date, %s AS description, "
|
||||||
|
"%s AS subtotal_amount_value, %s AS discount_amount_value, %s AS discount_percentage_value, %s AS taxable_amount_value, %s AS taxes_amount_value, %s AS total_amount_value, "
|
||||||
|
"%s AS customer_id, %s AS customer_tin, %s AS customer_name, %s AS customer_street, %s AS customer_city, %s AS customer_province, %s AS customer_postal_code, %s AS customer_country, "
|
||||||
|
"%s AS payment_method_id, %s AS payment_method_description, %s AS factuges_id, "
|
||||||
|
"2,2,2,2,2,2, 'es','EUR', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP "
|
||||||
|
"FROM customer_invoices "
|
||||||
|
"WHERE company_id = %s "
|
||||||
|
"AND deleted_at is null"
|
||||||
|
)
|
||||||
|
|
||||||
|
INSERT_INVOICE_BAK = (
|
||||||
"INSERT INTO customer_invoices (id, company_id, invoice_number, status, series, reference, invoice_date, operation_date, description, "
|
"INSERT INTO customer_invoices (id, company_id, invoice_number, status, series, reference, invoice_date, operation_date, description, "
|
||||||
"subtotal_amount_value, discount_amount_value, discount_percentage_value, taxable_amount_value, taxes_amount_value, total_amount_value, "
|
"subtotal_amount_value, discount_amount_value, discount_percentage_value, taxable_amount_value, taxes_amount_value, total_amount_value, "
|
||||||
"customer_id, customer_tin, customer_name, customer_street, customer_city, customer_province, customer_postal_code, customer_country, "
|
"customer_id, customer_tin, customer_name, customer_street, customer_city, customer_province, customer_postal_code, customer_country, "
|
||||||
@ -39,6 +60,7 @@ INSERT_INVOICE = (
|
|||||||
"VALUES (%s,%s,%s,%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)"
|
"VALUES (%s,%s,%s,%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_INVOICE_ITEM = (
|
INSERT_INVOICE_ITEM = (
|
||||||
"INSERT INTO customer_invoice_items "
|
"INSERT INTO customer_invoice_items "
|
||||||
"(item_id, invoice_id, position, description, quantity_value, unit_amount_value, "
|
"(item_id, invoice_id, position, description, quantity_value, unit_amount_value, "
|
||||||
@ -59,6 +81,37 @@ INSERT_INVOICE_ITEM_TAX = (
|
|||||||
"VALUES (%s,%s,%s,%s,%s,4,2,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
"VALUES (%s,%s,%s,%s,%s,4,2,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SELECT_INVOICES_DELETED = (
|
||||||
|
"SELECT ci.id "
|
||||||
|
"FROM customer_invoices as ci "
|
||||||
|
"WHERE "
|
||||||
|
"(ci.deleted_at is not null) "
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# FIREBIRD (constantes)
|
||||||
|
# =========================
|
||||||
|
SELECT_FACTUGES_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.IVA, fac.IMPORTE_IVA, fac.IMPORTE_TOTAL, "
|
||||||
|
f"fac.ID_FORMA_PAGO, fp.DESCRIPCION as DES_FORMA_PAGO, fac.ID_TIPO_IVA, ti.REFERENCIA as DES_TIPO_IVA, fac.RECARGO_EQUIVALENCIA, fac.RE, fac.IMPORTE_RE, "
|
||||||
|
f"fac.ID_CLIENTE, fac.NIF_CIF, fac.NOMBRE, fac.CALLE, fac.POBLACION, fac.PROVINCIA, fac.CODIGO_POSTAL, "
|
||||||
|
f"cc.TELEFONO_1, cc.TELEFONO_2, cc.MOVIL_1, cc.MOVIL_2, cc.EMAIL_1, cc.EMAIL_2, cc.PAGINA_WEB, "
|
||||||
|
f"facdet.ID || '' as ID_DET, facdet.ID_FACTURA, facdet.POSICION, facdet.TIPO_DETALLE, facdet.ID_ARTICULO, facdet.CONCEPTO, facdet.CANTIDAD, "
|
||||||
|
f"facdet.IMPORTE_UNIDAD, facdet.DESCUENTO as DESCUENTO_DET, facdet.IMPORTE_TOTAL as IMPORTE_TOTAL_DET, facdet.VISIBLE, facdet.FECHA_ALTA as FECHA_ALTA_DET, facdet.FECHA_MODIFICACION as FECHA_MODIFICACION_DET "
|
||||||
|
f"FROM FACTURAS_CLIENTE AS fac "
|
||||||
|
f"LEFT JOIN CONTACTOS AS cc ON fac.ID_CLIENTE = cc.ID "
|
||||||
|
f"LEFT JOIN FORMAS_PAGO AS fp ON fac.ID_FORMA_PAGO = fp.ID "
|
||||||
|
f"LEFT JOIN TIPOS_IVA AS ti ON fac.ID_TIPO_IVA = ti.ID "
|
||||||
|
f"LEFT JOIN FACTURAS_CLIENTE_DETALLES as facdet ON fac.ID = facdet.ID_FACTURA "
|
||||||
|
f"WHERE "
|
||||||
|
f"(fac.VERIFACTU > 0) "
|
||||||
|
f"AND (fac.ID_VERIFACTU is null)"
|
||||||
|
f"ORDER BY (fac.ID)"
|
||||||
|
)
|
||||||
|
|
||||||
UPDATE_FACTUGES_LINK = (
|
UPDATE_FACTUGES_LINK = (
|
||||||
"UPDATE FACTURAS_CLIENTE "
|
"UPDATE FACTURAS_CLIENTE "
|
||||||
"SET ID_VERIFACTU=? "
|
"SET ID_VERIFACTU=? "
|
||||||
@ -70,5 +123,4 @@ LIMPIAR_FACTUGES_LINK = (
|
|||||||
"SET ID_VERIFACTU = NULL, "
|
"SET ID_VERIFACTU = NULL, "
|
||||||
"VERIFACTU = 0 "
|
"VERIFACTU = 0 "
|
||||||
"WHERE (ID_VERIFACTU = ?)"
|
"WHERE (ID_VERIFACTU = ?)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,64 +1,33 @@
|
|||||||
import logging
|
import logging
|
||||||
import textwrap
|
import textwrap
|
||||||
from . import sql_sentences as SQL
|
from typing import Dict, Any, Optional, Tuple
|
||||||
from uuid6 import uuid7
|
from uuid6 import uuid7
|
||||||
from config import load_config
|
from config import load_config
|
||||||
from decimal import Decimal, ROUND_HALF_UP
|
from decimal import Decimal, ROUND_HALF_UP
|
||||||
from utils import limpiar_cadena, normalizar_telefono_con_plus, corregir_y_validar_email, normalizar_url_para_insert
|
from . import sql_sentences as SQL
|
||||||
|
from utils import limpiar_cadena, normalizar_telefono_con_plus, corregir_y_validar_email, normalizar_url_para_insert, map_tax_code, cents, money_round, tax_fraction_from_code
|
||||||
|
|
||||||
|
|
||||||
def sync_invoices(conn_factuges, conn_mysql, last_execution_date):
|
def sync_invoices(conn_factuges, conn_mysql, last_execution_date):
|
||||||
config = load_config()
|
config = load_config()
|
||||||
|
|
||||||
# Recorrer todas las facturas eliminadas para liberarlas en factuges
|
|
||||||
# VERIFACTU = 0 and ID_VERIFACTU = NULL
|
|
||||||
consulta_sql_customer_invoices_deleted = (
|
|
||||||
f"SELECT ci.id "
|
|
||||||
f"FROM customer_invoices as ci "
|
|
||||||
f"WHERE "
|
|
||||||
f"(ci.deleted_at is not null) "
|
|
||||||
)
|
|
||||||
|
|
||||||
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.IVA, fac.IMPORTE_IVA, fac.IMPORTE_TOTAL, "
|
|
||||||
f"fac.ID_FORMA_PAGO, fp.DESCRIPCION as DES_FORMA_PAGO, fac.ID_TIPO_IVA, ti.REFERENCIA as DES_TIPO_IVA, fac.RECARGO_EQUIVALENCIA, fac.RE, fac.IMPORTE_RE, "
|
|
||||||
f"fac.ID_CLIENTE, fac.NIF_CIF, fac.NOMBRE, fac.CALLE, fac.POBLACION, fac.PROVINCIA, fac.CODIGO_POSTAL, "
|
|
||||||
f"cc.TELEFONO_1, cc.TELEFONO_2, cc.MOVIL_1, cc.MOVIL_2, cc.EMAIL_1, cc.EMAIL_2, cc.PAGINA_WEB, "
|
|
||||||
f"facdet.ID || '' as ID_DET, facdet.ID_FACTURA, facdet.POSICION, facdet.TIPO_DETALLE, facdet.ID_ARTICULO, facdet.CONCEPTO, facdet.CANTIDAD, "
|
|
||||||
f"facdet.IMPORTE_UNIDAD, facdet.DESCUENTO as DESCUENTO_DET, facdet.IMPORTE_TOTAL as IMPORTE_TOTAL_DET, facdet.VISIBLE, facdet.FECHA_ALTA as FECHA_ALTA_DET, facdet.FECHA_MODIFICACION as FECHA_MODIFICACION_DET "
|
|
||||||
f"FROM FACTURAS_CLIENTE AS fac "
|
|
||||||
f"LEFT JOIN CONTACTOS AS cc ON fac.ID_CLIENTE = cc.ID "
|
|
||||||
f"LEFT JOIN FORMAS_PAGO AS fp ON fac.ID_FORMA_PAGO = fp.ID "
|
|
||||||
f"LEFT JOIN TIPOS_IVA AS ti ON fac.ID_TIPO_IVA = ti.ID "
|
|
||||||
f"LEFT JOIN FACTURAS_CLIENTE_DETALLES as facdet ON fac.ID = facdet.ID_FACTURA "
|
|
||||||
f"WHERE "
|
|
||||||
f"(fac.VERIFACTU > 0) "
|
|
||||||
f"AND (fac.ID_VERIFACTU is null)"
|
|
||||||
f"ORDER BY (fac.ID)"
|
|
||||||
)
|
|
||||||
|
|
||||||
# LIMPIAMOS LAS FACTURAS DE FACTUGES QUE HAYAN SIDO ELIMINADAS DEL PROGRAMA NUEVO DE FACTURACION, PARA QUE PUEDAN SER MODIFICADAS
|
# LIMPIAMOS LAS FACTURAS DE FACTUGES QUE HAYAN SIDO ELIMINADAS DEL PROGRAMA NUEVO DE FACTURACION, PARA QUE PUEDAN SER MODIFICADAS
|
||||||
# Crear un cursor para ejecutar consultas SQL
|
# Crear un cursor para ejecutar consultas SQL
|
||||||
cursor_mysql = None
|
cursor_mysql = None
|
||||||
try:
|
try:
|
||||||
cursor_mysql = conn_mysql.cursor()
|
cursor_mysql = conn_mysql.cursor()
|
||||||
# Ejecutar la consulta de FACTURAS_CLIENTE
|
cursor_mysql.execute(SQL.SELECT_INVOICES_DELETED)
|
||||||
cursor_mysql.execute(consulta_sql_customer_invoices_deleted)
|
|
||||||
filas = cursor_mysql.fetchall()
|
filas = cursor_mysql.fetchall()
|
||||||
cursor_mysql.close()
|
cursor_mysql.close()
|
||||||
|
|
||||||
# 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: {
|
# logging.info(f"Customer invoices rows to be deleted: {len(ids_verifactu_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:
|
||||||
eliminar_datos(conn_factuges, ids_verifactu_deleted, config)
|
eliminar_datos(conn_factuges, ids_verifactu_deleted, config)
|
||||||
else:
|
else:
|
||||||
logging.info(f"There are no rows to deleted")
|
logging.info(f"There are customer invoices deleted")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if cursor_mysql is not None:
|
if cursor_mysql is not None:
|
||||||
@ -74,7 +43,7 @@ def sync_invoices(conn_factuges, conn_mysql, last_execution_date):
|
|||||||
try:
|
try:
|
||||||
cursor_FactuGES = conn_factuges.cursor()
|
cursor_FactuGES = conn_factuges.cursor()
|
||||||
# Ejecutar la consulta de FACTURAS_CLIENTE
|
# Ejecutar la consulta de FACTURAS_CLIENTE
|
||||||
cursor_FactuGES.execute(consulta_sql_FACTURAS_CLIENTE)
|
cursor_FactuGES.execute(SQL.SELECT_FACTUGES_FACTURAS_CLIENTE)
|
||||||
filas = cursor_FactuGES.fetchall()
|
filas = cursor_FactuGES.fetchall()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if cursor_FactuGES is not None:
|
if cursor_FactuGES is not None:
|
||||||
@ -93,10 +62,6 @@ def sync_invoices(conn_factuges, conn_mysql, last_execution_date):
|
|||||||
for fila in filas:
|
for fila in filas:
|
||||||
tupla = dict(zip(columnas, fila))
|
tupla = dict(zip(columnas, fila))
|
||||||
tuplas_seleccionadas.append(tupla)
|
tuplas_seleccionadas.append(tupla)
|
||||||
|
|
||||||
logging.info(
|
|
||||||
f"FACTURAS_CLIENTE_DETALLE rows to be processed: {len(tuplas_seleccionadas)}")
|
|
||||||
|
|
||||||
# Verificar si hay filas en el resultado
|
# Verificar si hay filas en el resultado
|
||||||
if tuplas_seleccionadas:
|
if tuplas_seleccionadas:
|
||||||
insertar_datos(conn_mysql, tuplas_seleccionadas, conn_factuges, config)
|
insertar_datos(conn_mysql, tuplas_seleccionadas, conn_factuges, config)
|
||||||
@ -106,8 +71,8 @@ def sync_invoices(conn_factuges, conn_mysql, last_execution_date):
|
|||||||
|
|
||||||
|
|
||||||
def eliminar_datos(conn_factuges, ids_verifactu_deleted, config):
|
def eliminar_datos(conn_factuges, ids_verifactu_deleted, config):
|
||||||
# Eliminamos todos los IDs de verifacti que han sido eliminados así liberaremos la factura borrador y podermos modificarla de nuevo, para volverla a subir una vez hechos los cambios.
|
# Eliminamos todos los IDs asociados en FactuGES que han sido eliminados así liberaremos la factura borrador y podermos modificarla de nuevo, para volverla a subir una vez hechos los cambios.
|
||||||
|
# VERIFACTU = 0 and ID_VERIFACTU = NULL
|
||||||
cursor_FactuGES = None
|
cursor_FactuGES = None
|
||||||
try:
|
try:
|
||||||
cursor_FactuGES = conn_factuges.cursor()
|
cursor_FactuGES = conn_factuges.cursor()
|
||||||
@ -130,79 +95,36 @@ def eliminar_datos(conn_factuges, ids_verifactu_deleted, config):
|
|||||||
|
|
||||||
def insertar_datos(conn_mysql, filas, conn_factuges, config):
|
def insertar_datos(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)}")
|
||||||
|
|
||||||
# Compañia RODAX
|
# Compañia RODAX
|
||||||
cte_company_id = '5e4dc5b3-96b9-4968-9490-14bd032fec5f'
|
cte_company_id = '5e4dc5b3-96b9-4968-9490-14bd032fec5f'
|
||||||
|
|
||||||
cursorMySQL = None
|
cursorMySQL = None
|
||||||
cursor_FactuGES = None
|
cursor_FactuGES = None
|
||||||
factuges_id_anterior = None
|
factuges_id_anterior = None
|
||||||
id_customer_invoice = None
|
id_customer_invoice = None
|
||||||
num_fac_procesed = 0
|
num_fac_procesed = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursorMySQL = conn_mysql.cursor()
|
cursorMySQL = conn_mysql.cursor()
|
||||||
cursor_FactuGES = conn_factuges.cursor()
|
cursor_FactuGES = conn_factuges.cursor()
|
||||||
contador_serie = 0
|
|
||||||
# Insertar datos en la tabla 'customer_invoices'
|
# Insertar datos en la tabla 'customer_invoices'
|
||||||
for factura_detalle in filas:
|
for factura_detalle in filas:
|
||||||
|
|
||||||
factuges_id = int(factura_detalle['ID_FACTURA'])
|
|
||||||
invoice_status = str('draft')
|
|
||||||
invoice_series = str('F25/')
|
|
||||||
reference = str(factura_detalle['REFERENCIA'])
|
|
||||||
invoice_date = str(factura_detalle['FECHA_FACTURA'])
|
|
||||||
operation_date = str(factura_detalle['FECHA_FACTURA'])
|
|
||||||
# siempre tendrán 2 decimales
|
|
||||||
subtotal_amount_value = (factura_detalle['IMPORTE_NETO'] or 0)*100
|
|
||||||
discount_amount_value = (
|
|
||||||
factura_detalle['IMPORTE_DESCUENTO'] or 0)*100
|
|
||||||
discount_percentage_value = (
|
|
||||||
factura_detalle['DESCUENTO'] or 0)*100 if (factura_detalle['DESCUENTO']) is not None else 0
|
|
||||||
taxable_amount_value = (factura_detalle['BASE_IMPONIBLE'] or 0)*100
|
|
||||||
# Preparamos el tipo de IVA, en FactuGES es único
|
# Preparamos el tipo de IVA, en FactuGES es único
|
||||||
tax_code = str(factura_detalle['DES_TIPO_IVA'])
|
# Map tax code (cabecera)
|
||||||
if (tax_code == 'IVA21') or (tax_code == 'IVA 21'):
|
tax_code = map_tax_code(str(factura_detalle.get("DES_TIPO_IVA")))
|
||||||
tax_code = 'iva_21'
|
|
||||||
elif tax_code == 'IVA18' or (tax_code == 'IVA 18'):
|
|
||||||
tax_code = 'iva_18'
|
|
||||||
elif tax_code == 'IVA16' or (tax_code == 'IVA 16'):
|
|
||||||
tax_code = 'iva_16'
|
|
||||||
elif tax_code == 'IVA10' or (tax_code == 'IVA 10'):
|
|
||||||
tax_code = 'iva_10'
|
|
||||||
elif tax_code == 'IVA4' or (tax_code == 'IVA 4'):
|
|
||||||
tax_code = 'iva_4'
|
|
||||||
elif tax_code == 'EXENTO':
|
|
||||||
tax_code = 'iva_exenta'
|
|
||||||
else:
|
|
||||||
tax_code = ''
|
|
||||||
# La cuota de impuestos es el IVA + RE
|
# La cuota de impuestos es el IVA + RE
|
||||||
tax_amount_value = (
|
tax_amount_value = (
|
||||||
(factura_detalle['IMPORTE_IVA'] or 0) + (factura_detalle['IMPORTE_RE'] or 0))*100
|
(factura_detalle['IMPORTE_IVA'] or 0) + (factura_detalle['IMPORTE_RE'] or 0))*100
|
||||||
|
|
||||||
total_amount_value = (factura_detalle['IMPORTE_TOTAL'] or 0)*100
|
|
||||||
|
|
||||||
payment_method_id = str(uuid7())
|
|
||||||
factuges_payment_method_id = str(factura_detalle['ID_FORMA_PAGO'])
|
factuges_payment_method_id = str(factura_detalle['ID_FORMA_PAGO'])
|
||||||
payment_method_description = str(factura_detalle['DES_FORMA_PAGO'])
|
payment_method_description = str(factura_detalle['DES_FORMA_PAGO'])
|
||||||
|
|
||||||
customer_id = str(uuid7())
|
|
||||||
factuges_customer_id = str(factura_detalle['ID_CLIENTE'])
|
factuges_customer_id = str(factura_detalle['ID_CLIENTE'])
|
||||||
customer_tin = limpiar_cadena(str(factura_detalle['NIF_CIF']))
|
customer_tin = limpiar_cadena(str(factura_detalle['NIF_CIF']))
|
||||||
customer_name = str(factura_detalle['NOMBRE'])
|
|
||||||
customer_street = str(factura_detalle['CALLE'])
|
|
||||||
customer_city = str(factura_detalle['POBLACION'])
|
|
||||||
customer_province = str(factura_detalle['PROVINCIA'])
|
|
||||||
customer_postal_code = str(factura_detalle['CODIGO_POSTAL'])
|
|
||||||
customer_phone_primary = factura_detalle['TELEFONO_1']
|
|
||||||
customer_phone_secondary = factura_detalle['TELEFONO_2']
|
|
||||||
customer_mobile_primary = factura_detalle['MOVIL_1']
|
|
||||||
customer_mobile_secondary = factura_detalle['MOVIL_2']
|
|
||||||
customer_email_primary = factura_detalle['EMAIL_1']
|
|
||||||
customer_email_secondary = factura_detalle['EMAIL_2']
|
|
||||||
customer_webside = str(factura_detalle['PAGINA_WEB'])
|
|
||||||
customer_country = 'es'
|
|
||||||
description = textwrap.shorten(
|
|
||||||
f"{reference or ''} - {customer_name or ''}", width=50, placeholder="…")
|
|
||||||
|
|
||||||
item_position = int(factura_detalle['POSICION'])
|
item_position = int(factura_detalle['POSICION'])
|
||||||
item_description = str(factura_detalle['CONCEPTO'])
|
item_description = str(factura_detalle['CONCEPTO'])
|
||||||
@ -213,10 +135,6 @@ def insertar_datos(conn_mysql, filas, conn_factuges, config):
|
|||||||
Descuento = factura_detalle['DESCUENTO_DET']
|
Descuento = factura_detalle['DESCUENTO_DET']
|
||||||
item_discount_percentage_value = None if Descuento is None else None if Descuento == 0 else (
|
item_discount_percentage_value = None if Descuento is None else None if Descuento == 0 else (
|
||||||
factura_detalle['DESCUENTO_DET'])*100
|
factura_detalle['DESCUENTO_DET'])*100
|
||||||
item_discount_amount = None
|
|
||||||
# Se calcula en el objeto de negocio de nuevo, comprobar si coincide
|
|
||||||
# item_discount_amount = (
|
|
||||||
# (factura_detalle['IMPORTE_UNIDAD'] or 0)*((factura_detalle['DESCUENTO'] or 0)/100))*100
|
|
||||||
item_total_amount = (factura_detalle['IMPORTE_TOTAL_DET'] or 0)*100
|
item_total_amount = (factura_detalle['IMPORTE_TOTAL_DET'] or 0)*100
|
||||||
|
|
||||||
# None if factura_detalle['IMPORTE_TOTAL_DET'] is None else (
|
# None if factura_detalle['IMPORTE_TOTAL_DET'] is None else (
|
||||||
@ -227,123 +145,256 @@ def insertar_datos(conn_mysql, filas, conn_factuges, config):
|
|||||||
# xxxxxxx = str(factura_detalle['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_detalle['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_detalle['OBSERVACIONES'])
|
# xxxxxxx = str(factura_detalle['OBSERVACIONES'])
|
||||||
|
|
||||||
|
factuges_id = int(factura_detalle['ID_FACTURA'])
|
||||||
|
|
||||||
if factuges_id_anterior is None or factuges_id_anterior != factuges_id:
|
if factuges_id_anterior is None or factuges_id_anterior != factuges_id:
|
||||||
# Comprobamos si existe el cliente del primer item de la factura
|
# Comprobamos si existe el cliente del primer item de la factura
|
||||||
cursorMySQL.execute(SQL.SELECT_CUSTOMER_BY_FACTUGES,
|
# cursorMySQL.execute(SQL.SELECT_CUSTOMER_BY_FACTUGES,
|
||||||
(factuges_customer_id, ))
|
# (factuges_customer_id, ))
|
||||||
row = cursorMySQL.fetchone()
|
# row = cursorMySQL.fetchone()
|
||||||
is_new = (row is None) or (row[0] is None)
|
# is_new = (row is None) or (row[0] is None)
|
||||||
|
|
||||||
#Validamos los campos que pueden dar conflicto
|
# ---- cliente
|
||||||
customer_phone_primary_tratado = normalizar_telefono_con_plus(customer_phone_primary)
|
customer_fields = normalize_customer_fields(factura_detalle)
|
||||||
customer_phone_secondary_tratado = normalizar_telefono_con_plus(customer_phone_secondary)
|
customer_id = get_or_create_customer(
|
||||||
customer_mobile_primary_tratado = normalizar_telefono_con_plus(customer_mobile_primary)
|
cursorMySQL,
|
||||||
customer_mobile_secondary_tratado = normalizar_telefono_con_plus(customer_mobile_secondary)
|
cte_company_id,
|
||||||
customer_webside_tratado = normalizar_url_para_insert(customer_webside)
|
str(factura_detalle["ID_CLIENTE"]),
|
||||||
customer_email_primary_ok, customer_email_primary_tratado = corregir_y_validar_email(customer_email_primary)
|
customer_fields,
|
||||||
customer_email_secondary_ok, customer_email_secondary_tratado = corregir_y_validar_email(customer_email_secondary)
|
)
|
||||||
|
|
||||||
|
|
||||||
logging.info(f"La ficha de cliente {customer_tin} se modifican los siguientes campos:")
|
# ---- forma de pago
|
||||||
if (customer_phone_primary_tratado != customer_phone_primary):
|
pm_id = get_or_create_payment_method(
|
||||||
logging.info(f"phone_primary ({customer_phone_primary}) se cambia por ({customer_phone_primary_tratado})")
|
cursorMySQL,
|
||||||
if (customer_phone_secondary_tratado != customer_phone_secondary):
|
str(factura_detalle["ID_FORMA_PAGO"]),
|
||||||
logging.info(f"phone_secondary ({customer_phone_secondary}) se cambia por ({customer_phone_secondary_tratado})")
|
str(factura_detalle["DES_FORMA_PAGO"]),
|
||||||
if (customer_mobile_primary_tratado != customer_mobile_primary):
|
)
|
||||||
logging.info(f"mobile_primary ({customer_mobile_primary}) se cambia por ({customer_mobile_primary_tratado})")
|
|
||||||
if (customer_mobile_secondary_tratado != customer_mobile_secondary):
|
|
||||||
logging.info(f"mobile_secondary ({customer_mobile_secondary}) se cambia por ({customer_mobile_secondary_tratado})")
|
|
||||||
if (customer_webside_tratado != customer_webside):
|
|
||||||
logging.info(f"customer_webside ({customer_webside}) se cambia por ({customer_webside_tratado})")
|
|
||||||
|
|
||||||
if customer_email_primary_ok:
|
# ---- cabecera factura
|
||||||
if (customer_email_primary_tratado != customer_email_primary):
|
invoice_id, tax_code = insert_invoice_header(
|
||||||
logging.info(f"email_primary ({customer_email_primary}) se cambia por ({customer_email_primary_tratado})")
|
cursorMySQL, cte_company_id, factura_detalle, customer_id, pm_id, str(
|
||||||
else:
|
factura_detalle["DES_FORMA_PAGO"])
|
||||||
logging.info(f"Omitimos email_primary invalido({customer_email_primary})")
|
)
|
||||||
if customer_email_secondary_ok:
|
|
||||||
if (customer_email_secondary_tratado != customer_email_secondary):
|
|
||||||
logging.info(f"email_secondary ({customer_email_secondary}) se cambia por ({customer_email_secondary_tratado})")
|
|
||||||
else:
|
|
||||||
logging.info(f"Omitimos email_secondary invalido({customer_email_secondary})")
|
|
||||||
|
|
||||||
|
# ---- impuestos cabecera
|
||||||
if is_new:
|
insert_header_taxes_if_any(
|
||||||
logging.info(
|
cursorMySQL, invoice_id, factura_detalle, tax_code)
|
||||||
f"Inserting customer {factuges_customer_id} {customer_tin} {customer_name}")
|
|
||||||
cursorMySQL.execute(SQL.INSERT_CUSTOMER, (customer_id, customer_name, customer_tin, customer_street, customer_city, customer_province,
|
|
||||||
customer_postal_code, customer_country, customer_phone_primary_tratado, customer_phone_secondary_tratado, customer_mobile_primary_tratado,
|
|
||||||
customer_mobile_secondary_tratado, customer_email_primary_tratado, customer_email_secondary_tratado, customer_webside_tratado, factuges_customer_id, cte_company_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(SQL.UPDATE_CUSTOMER, (customer_name, customer_tin, customer_street, customer_city, customer_province, customer_postal_code, customer_country,
|
|
||||||
customer_phone_primary_tratado, customer_phone_secondary_tratado, customer_mobile_primary_tratado, customer_mobile_secondary_tratado,
|
|
||||||
customer_email_primary_tratado, customer_email_secondary_tratado, customer_webside_tratado, customer_id))
|
|
||||||
|
|
||||||
|
|
||||||
# Comprobamos si existe la forma de pago del primer item de la factura
|
|
||||||
cursorMySQL.execute(SQL.SELECT_PAYMENT_METHOD_BY_FACTUGES,
|
|
||||||
(factuges_payment_method_id, ))
|
|
||||||
row = cursorMySQL.fetchone()
|
|
||||||
is_new = (row is None) or (row[0] is None)
|
|
||||||
|
|
||||||
if is_new:
|
|
||||||
logging.info(
|
|
||||||
f"Inserting cuspayment method {factuges_payment_method_id} {payment_method_id} {payment_method_description}")
|
|
||||||
cursorMySQL.execute(SQL.INSERT_PAYMENT_METHOD, (
|
|
||||||
payment_method_id, payment_method_description, factuges_payment_method_id))
|
|
||||||
else:
|
|
||||||
# Si ya exite ponemos el id del customer correspondiente
|
|
||||||
payment_method_id = str(row[0])
|
|
||||||
logging.info(
|
|
||||||
f"Updating customer {factuges_payment_method_id} {payment_method_id}")
|
|
||||||
# cursorMySQL.execute(update_customer_query, .....)
|
|
||||||
|
|
||||||
# Insertamos cabecera de la factura
|
|
||||||
# Generar un ID único para la tabla customer_invoices
|
|
||||||
id_customer_invoice = str(uuid7())
|
|
||||||
contador_serie = contador_serie + 1
|
|
||||||
logging.info(
|
|
||||||
f"Inserting customer_invoice {id_customer_invoice} {reference} {invoice_date}")
|
|
||||||
cursorMySQL.execute(SQL.INSERT_INVOICE, (id_customer_invoice, cte_company_id, contador_serie, invoice_status, invoice_series, reference, invoice_date, operation_date, description,
|
|
||||||
subtotal_amount_value, discount_amount_value, discount_percentage_value, taxable_amount_value, tax_amount_value, total_amount_value,
|
|
||||||
customer_id, customer_tin, customer_name, customer_street, customer_city, customer_province, customer_postal_code, customer_country,
|
|
||||||
payment_method_id, payment_method_description))
|
|
||||||
|
|
||||||
|
|
||||||
# Insertamos el IVA y RE si viene
|
|
||||||
if (factura_detalle['IVA'] >= 0):
|
|
||||||
taxable_amount_value = (
|
|
||||||
factura_detalle['BASE_IMPONIBLE'])*100
|
|
||||||
tax_amount_value = (factura_detalle['IMPORTE_IVA'])*100
|
|
||||||
cursorMySQL.execute(SQL.INSERT_INVOICE_TAX, (str(uuid7()),
|
|
||||||
id_customer_invoice, tax_code, taxable_amount_value, tax_amount_value))
|
|
||||||
|
|
||||||
if (factura_detalle['RECARGO_EQUIVALENCIA'] > 0):
|
|
||||||
tax_code = 're_5_2'
|
|
||||||
taxable_amount_value = (
|
|
||||||
factura_detalle['BASE_IMPONIBLE'])*100
|
|
||||||
tax_amount_value = (factura_detalle['IMPORTE_RE'])*100
|
|
||||||
cursorMySQL.execute(SQL.INSERT_INVOICE_TAX, (str(uuid7()),
|
|
||||||
id_customer_invoice, tax_code, taxable_amount_value, tax_amount_value))
|
|
||||||
|
|
||||||
# Guardamos en Factuges el id de la customer_invoice
|
# Guardamos en Factuges el id de la customer_invoice
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Updating FACTURAS_CLIENTE {id_customer_invoice} {factuges_id}")
|
f"Updating FACTURAS_CLIENTE {invoice_id} {factuges_id}")
|
||||||
cursor_FactuGES.execute(
|
cursor_FactuGES.execute(
|
||||||
SQL.UPDATE_FACTUGES_LINK, (id_customer_invoice, factuges_id))
|
SQL.UPDATE_FACTUGES_LINK, (invoice_id, factuges_id))
|
||||||
num_fac_procesed += 1
|
num_fac_procesed += 1
|
||||||
|
|
||||||
# Insertamos detalles y taxes correspondientes siempre
|
# Insertamos detalles y taxes correspondientes siempre
|
||||||
# Generar un ID único para la tabla customer_invoice_items
|
# Siempre insertamos la línea
|
||||||
item_id = str(uuid7())
|
insert_item_and_taxes(cursorMySQL, invoice_id,
|
||||||
logging.info(
|
factura_detalle, tax_code)
|
||||||
f"Inserting customer_invoice_items {id_customer_invoice} {item_position} {item_quantity_value}")
|
|
||||||
cursorMySQL.execute(SQL.INSERT_INVOICE_ITEM, (item_id, id_customer_invoice, item_position, item_description,
|
# Asignamos el id factura anterior para no volver a inserta cabecera
|
||||||
item_quantity_value, item_unit_amount_value, item_discount_percentage_value, item_discount_amount, item_total_amount))
|
factuges_id_anterior = factuges_id
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
f"FACTURAS_CLIENTE rows to be processed: {str(num_fac_procesed)}")
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_customer_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Normaliza campos de cliente del registro de origen 'fd' (factura_detalle).
|
||||||
|
"""
|
||||||
|
# >>> aquí usa tus helpers reales:
|
||||||
|
def clean_phone(v): return normalizar_telefono_con_plus(v)
|
||||||
|
def clean_web(v): return normalizar_url_para_insert(v)
|
||||||
|
def clean_tin(v): return limpiar_cadena(v)
|
||||||
|
|
||||||
|
email1_ok, email1 = corregir_y_validar_email(fd.get("EMAIL_1"))
|
||||||
|
email2_ok, email2 = corregir_y_validar_email(fd.get("EMAIL_2"))
|
||||||
|
|
||||||
|
return {
|
||||||
|
"tin": clean_tin(str(fd.get("NIF_CIF"))),
|
||||||
|
"name": str(fd.get("NOMBRE")),
|
||||||
|
"street": str(fd.get("CALLE")),
|
||||||
|
"city": str(fd.get("POBLACION")),
|
||||||
|
"province": str(fd.get("PROVINCIA")),
|
||||||
|
"postal_code": str(fd.get("CODIGO_POSTAL")),
|
||||||
|
"country": "es",
|
||||||
|
"phone_primary": clean_phone(fd.get("TELEFONO_1")),
|
||||||
|
"phone_secondary": clean_phone(fd.get("TELEFONO_2")),
|
||||||
|
"mobile_primary": clean_phone(fd.get("MOVIL_1")),
|
||||||
|
"mobile_secondary": clean_phone(fd.get("MOVIL_2")),
|
||||||
|
"email_primary": email1 if email1_ok else None,
|
||||||
|
"email_secondary": email2 if email2_ok else None,
|
||||||
|
"website": clean_web(str(fd.get("PAGINA_WEB"))),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_customer(cur, company_id: str, factuges_customer_id: str, fields: Dict[str, Any]) -> str:
|
||||||
|
cur.execute(SQL.SELECT_CUSTOMER_BY_FACTUGES, (factuges_customer_id,))
|
||||||
|
row = cur.fetchone()
|
||||||
|
if not row or not row[0]:
|
||||||
|
customer_id = str(uuid7())
|
||||||
|
logging.info("Inserting customer %s %s %s",
|
||||||
|
factuges_customer_id, fields["tin"], fields["name"])
|
||||||
|
cur.execute(
|
||||||
|
SQL.INSERT_CUSTOMER,
|
||||||
|
(
|
||||||
|
customer_id, fields["name"], fields["tin"], fields["street"], fields["city"], fields["province"],
|
||||||
|
fields["postal_code"], fields["country"], fields["phone_primary"], fields["phone_secondary"],
|
||||||
|
fields["mobile_primary"], fields["mobile_secondary"], fields["email_primary"], fields["email_secondary"],
|
||||||
|
fields["website"], factuges_customer_id, company_id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return customer_id
|
||||||
|
customer_id = str(row[0])
|
||||||
|
logging.info("Updating customer %s %s", factuges_customer_id, customer_id)
|
||||||
|
cur.execute(
|
||||||
|
SQL.UPDATE_CUSTOMER,
|
||||||
|
(
|
||||||
|
fields["name"], fields["tin"], fields["street"], fields["city"], fields["province"], fields["postal_code"],
|
||||||
|
fields["country"], fields["phone_primary"], fields["phone_secondary"],
|
||||||
|
fields["mobile_primary"], fields["mobile_secondary"], fields["email_primary"], fields["email_secondary"],
|
||||||
|
fields["website"], customer_id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return customer_id
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_payment_method(cur, factuges_payment_id: str, description: str) -> str:
|
||||||
|
cur.execute(SQL.SELECT_PAYMENT_METHOD_BY_FACTUGES, (factuges_payment_id,))
|
||||||
|
row = cur.fetchone()
|
||||||
|
if not row or not row[0]:
|
||||||
|
pm_id = str(uuid7())
|
||||||
|
logging.info("Inserting payment method %s %s %s",
|
||||||
|
factuges_payment_id, pm_id, description)
|
||||||
|
cur.execute(SQL.INSERT_PAYMENT_METHOD,
|
||||||
|
(pm_id, description, factuges_payment_id))
|
||||||
|
return pm_id
|
||||||
|
pm_id = str(row[0])
|
||||||
|
logging.info("Payment method exists %s -> %s", factuges_payment_id, pm_id)
|
||||||
|
return pm_id
|
||||||
|
|
||||||
|
|
||||||
|
def insert_invoice_header(cur, company_id: str, fd: Dict[str, Any], customer_id: str,
|
||||||
|
payment_method_id: str, payment_method_description: str) -> Tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Inserta cabecera y devuelve (invoice_id, tax_code_normalized)
|
||||||
|
"""
|
||||||
|
invoice_id = str(uuid7())
|
||||||
|
|
||||||
|
reference = str(fd['REFERENCIA'])
|
||||||
|
invoice_date = str(fd['FECHA_FACTURA'])
|
||||||
|
operation_date = str(fd['FECHA_FACTURA'])
|
||||||
|
description = textwrap.shorten(
|
||||||
|
f"{reference or ''} - {str(fd.get('NOMBRE')) or ''}", width=50, placeholder="…")
|
||||||
|
|
||||||
|
# siempre tendrán 2 decimales
|
||||||
|
subtotal_amount_value = cents(fd.get('IMPORTE_NETO'))
|
||||||
|
discount_amount_value = cents(fd.get('IMPORTE_DESCUENTO'))
|
||||||
|
discount_percentage_val = int((Decimal(str(fd.get('DESCUENTO') or 0)) * 100).to_integral_value()) \
|
||||||
|
if fd.get('DESCUENTO') is not None else 0
|
||||||
|
taxable_amount_value = cents(fd.get('BASE_IMPONIBLE'))
|
||||||
|
taxes_amount_value = cents(
|
||||||
|
(fd.get('IMPORTE_IVA') or 0) + (fd.get('IMPORTE_RE') or 0))
|
||||||
|
total_amount_value = cents(fd.get('IMPORTE_TOTAL'))
|
||||||
|
|
||||||
|
# Mapea tax_code cabecera
|
||||||
|
tax_code = map_tax_code(str(fd.get("DES_TIPO_IVA")))
|
||||||
|
|
||||||
|
logging.info("Inserting invoice %s %s %s",
|
||||||
|
invoice_id, reference, invoice_date)
|
||||||
|
cur.execute(
|
||||||
|
SQL.INSERT_INVOICE,
|
||||||
|
(
|
||||||
|
invoice_id, company_id, 'draft', 'F25/', reference, invoice_date, operation_date, description,
|
||||||
|
subtotal_amount_value, discount_amount_value, discount_percentage_val, taxable_amount_value,
|
||||||
|
taxes_amount_value, total_amount_value,
|
||||||
|
customer_id, fd.get('NIF_CIF'), fd.get(
|
||||||
|
'NOMBRE'), fd.get('CALLE'), fd.get('POBLACION'),
|
||||||
|
fd.get('PROVINCIA'), fd.get('CODIGO_POSTAL'), 'es',
|
||||||
|
payment_method_id, payment_method_description, fd.get(
|
||||||
|
'ID_FACTURA'), company_id
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return invoice_id, tax_code
|
||||||
|
|
||||||
|
|
||||||
|
def insert_header_taxes_if_any(cur, invoice_id: str, fd: Dict[str, Any], tax_code: str) -> None:
|
||||||
|
base = cents(fd.get('BASE_IMPONIBLE'))
|
||||||
|
iva = cents(fd.get('IMPORTE_IVA'))
|
||||||
|
re = cents(fd.get('IMPORTE_RE'))
|
||||||
|
|
||||||
|
# IVA (>= 0 acepta 0% también, si no quieres registrar 0, cambia condición a > 0)
|
||||||
|
if (fd.get('IVA') or 0) >= 0:
|
||||||
|
cur.execute(SQL.INSERT_INVOICE_TAX,
|
||||||
|
(str(uuid7()), invoice_id, tax_code, base, iva))
|
||||||
|
|
||||||
|
# Recargo equivalencia
|
||||||
|
if (fd.get('RECARGO_EQUIVALENCIA') or 0) > 0:
|
||||||
|
cur.execute(SQL.INSERT_INVOICE_TAX,
|
||||||
|
(str(uuid7()), invoice_id, 'rec_5_2', base, re))
|
||||||
|
|
||||||
|
|
||||||
|
def insert_item_and_taxes(cur, invoice_id: str, fd: Dict[str, Any], tax_code: str) -> None:
|
||||||
|
"""
|
||||||
|
Inserta línea y sus impuestos derivados.
|
||||||
|
"""
|
||||||
|
item_id = str(uuid7())
|
||||||
|
position = int(fd['POSICION'])
|
||||||
|
description = str(fd['CONCEPTO'])
|
||||||
|
quantity_value = None if fd['CANTIDAD'] is None else int(
|
||||||
|
Decimal(str(fd['CANTIDAD'] or 0)) * 100)
|
||||||
|
unit_value = None if fd['IMPORTE_UNIDAD'] is None else int(
|
||||||
|
Decimal(str(fd['IMPORTE_UNIDAD'] or 0)) * 10000)
|
||||||
|
disc_pct = fd.get('DESCUENTO_DET')
|
||||||
|
discount_percentage_value = None if disc_pct in (
|
||||||
|
None, 0) else int(Decimal(str(disc_pct)) * 100)
|
||||||
|
total_value = cents(fd.get('IMPORTE_TOTAL_DET'))
|
||||||
|
|
||||||
|
logging.info("Inserting item %s pos=%s qty=%s",
|
||||||
|
item_id, position, quantity_value)
|
||||||
|
cur.execute(
|
||||||
|
SQL.INSERT_INVOICE_ITEM,
|
||||||
|
(item_id, invoice_id, position, description, quantity_value,
|
||||||
|
unit_value, discount_percentage_value, None, total_value)
|
||||||
|
)
|
||||||
|
# Se calcula en el objeto de negocio de nuevo, comprobar si coincide
|
||||||
|
# item_discount_amount = (
|
||||||
|
# (factura_detalle['IMPORTE_UNIDAD'] or 0)*((factura_detalle['DESCUENTO'] or 0)/100))*100
|
||||||
|
|
||||||
|
# Calcular cuota de IVA de la línea
|
||||||
|
frac = tax_fraction_from_code(tax_code) # 0.21, 0.10, 0…
|
||||||
|
# si tu total_det ya incluye descuento
|
||||||
|
line_base = Decimal(str(fd.get('IMPORTE_TOTAL_DET') or 0))
|
||||||
|
tax_amount = int((money_round(line_base * frac, 2)
|
||||||
|
* 100).to_integral_value())
|
||||||
|
logging.info("Inserting item tax %s code=%s base=%s tax=%s",
|
||||||
|
item_id, tax_code, total_value, tax_amount)
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
SQL.INSERT_INVOICE_ITEM_TAX,
|
||||||
|
(str(uuid7()), item_id, tax_code, total_value, tax_amount)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
logging.info(
|
||||||
|
f"Inserting customer_invoice_item_taxes {item_id} {item_position} {tax_code} {item_total_amount} {tax_amount_value}")
|
||||||
|
cursorMySQL.execute(SQL.INSERT_INVOICE_ITEM_TAX, (str(uuid7()), item_id, tax_code,
|
||||||
|
item_total_amount, tax_amount_value))
|
||||||
|
|
||||||
|
|
||||||
if tax_code == 'iva_21':
|
if tax_code == 'iva_21':
|
||||||
tax_amount_value = (
|
tax_amount_value = (
|
||||||
@ -364,25 +415,4 @@ def insertar_datos(conn_mysql, filas, conn_factuges, config):
|
|||||||
tax_amount_value = (
|
tax_amount_value = (
|
||||||
factura_detalle['IMPORTE_TOTAL_DET'] or 0)*100
|
factura_detalle['IMPORTE_TOTAL_DET'] or 0)*100
|
||||||
|
|
||||||
logging.info(
|
"""
|
||||||
f"Inserting customer_invoice_item_taxes {item_id} {item_position} {tax_code} {item_total_amount} {tax_amount_value}")
|
|
||||||
cursorMySQL.execute(SQL.INSERT_INVOICE_ITEM_TAX, (str(uuid7()), item_id, tax_code,
|
|
||||||
item_total_amount, tax_amount_value))
|
|
||||||
|
|
||||||
# Asignamos el id factura anterior para no volver a inserta cabecera
|
|
||||||
factuges_id_anterior = factuges_id
|
|
||||||
|
|
||||||
logging.info(
|
|
||||||
f"FACTURAS_CLIENTE rows to be processed: {str(num_fac_procesed)}")
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|||||||
@ -42,7 +42,8 @@ def main():
|
|||||||
conn_mysql = get_mysql_connection(config)
|
conn_mysql = get_mysql_connection(config)
|
||||||
|
|
||||||
# Sync invoices
|
# Sync invoices
|
||||||
logging.info(f">>>>>>>>>>> Sync invoices FactuGES escritorio to FactuGES web")
|
logging.info(
|
||||||
|
f">>>>>>>>>>> Sync invoices FactuGES escritorio to FactuGES web")
|
||||||
sync_invoices(conn_factuges, conn_mysql, last_execution_date_local_tz)
|
sync_invoices(conn_factuges, conn_mysql, last_execution_date_local_tz)
|
||||||
|
|
||||||
# Confirmar los cambios
|
# Confirmar los cambios
|
||||||
@ -57,8 +58,9 @@ def main():
|
|||||||
conn_mysql = get_mysql_connection(config)
|
conn_mysql = get_mysql_connection(config)
|
||||||
|
|
||||||
# Sync Verifactu
|
# Sync Verifactu
|
||||||
logging.info(f">>>>>>>>>> Sync facturas emitidas en FactuGES web to Verifactu")
|
logging.info(
|
||||||
sync_invoices_verifactu(conn_mysql, last_execution_date_local_tz)
|
f">>>>>>>>>> Sync facturas emitidas en FactuGES web to Verifactu")
|
||||||
|
# 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 Verifactu >>>>>>>>>>")
|
logging.info(f"FIN Sync Verifactu >>>>>>>>>>")
|
||||||
|
|||||||
@ -4,8 +4,8 @@ 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
|
||||||
from .send_rest_api import validar_nif, estado_factura, crear_factura
|
from .send_rest_api import validar_nif, estado_factura, crear_factura
|
||||||
from .tax_catalog_helper import TaxCatalog, get_default_tax_catalog
|
from .tax_catalog_helper import TaxCatalog, get_default_tax_catalog, map_tax_code, calc_item_tax_amount, tax_fraction_from_code
|
||||||
from .importes_helper import unscale_to_str, unscale_to_decimal
|
from .importes_helper import unscale_to_str, unscale_to_decimal, cents, money_round
|
||||||
from .telefonos_helper import normalizar_telefono_con_plus
|
from .telefonos_helper import normalizar_telefono_con_plus
|
||||||
from .mails_helper import corregir_y_validar_email
|
from .mails_helper import corregir_y_validar_email
|
||||||
from .websites_helper import normalizar_url_para_insert
|
from .websites_helper import normalizar_url_para_insert
|
||||||
|
|||||||
@ -2,6 +2,18 @@ from decimal import Decimal, ROUND_HALF_UP
|
|||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
|
||||||
|
def cents(value: Optional[Decimal | float | int]) -> int:
|
||||||
|
"""Convierte a centésimas (valor * 100). Soporta None."""
|
||||||
|
v = Decimal(str(value or 0))
|
||||||
|
return int((v * 100).to_integral_value(rounding=ROUND_HALF_UP))
|
||||||
|
|
||||||
|
|
||||||
|
def money_round(value: Decimal, ndigits: int = 2) -> Decimal:
|
||||||
|
"""Redondeo bancario a n decimales (por defecto 2)."""
|
||||||
|
q = Decimal((0, (1,), -ndigits)) # 10^-ndigits
|
||||||
|
return value.quantize(q, rounding=ROUND_HALF_UP)
|
||||||
|
|
||||||
|
|
||||||
def unscale_to_decimal(value: Any, scale: Any) -> Decimal:
|
def unscale_to_decimal(value: Any, scale: Any) -> Decimal:
|
||||||
"""
|
"""
|
||||||
Convierte un valor escalado (p. ej. 24200 con scale=2) a su valor en unidades (242).
|
Convierte un valor escalado (p. ej. 24200 con scale=2) a su valor en unidades (242).
|
||||||
|
|||||||
@ -5,6 +5,60 @@ from functools import lru_cache
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def map_tax_code(raw: str) -> str:
|
||||||
|
t = (raw or "").strip().lower().replace(" ", "")
|
||||||
|
mapping = {
|
||||||
|
"iva21": "iva_21",
|
||||||
|
"iva18": "iva_18",
|
||||||
|
"iva16": "iva_16",
|
||||||
|
"iva10": "iva_10",
|
||||||
|
"iva4": "iva_4",
|
||||||
|
"exento": "iva_exenta",
|
||||||
|
}
|
||||||
|
return mapping.get(t, "")
|
||||||
|
|
||||||
|
|
||||||
|
def calc_item_tax_amount(tax_code: str, importe_total_det: Optional[Decimal | float | int]) -> int:
|
||||||
|
"""
|
||||||
|
Devuelve el impuesto de línea escalado a céntimos (x100) como entero.
|
||||||
|
"""
|
||||||
|
base = Decimal(str(importe_total_det or 0))
|
||||||
|
rates = {
|
||||||
|
"iva_21": Decimal("0.21"),
|
||||||
|
"iva_18": Decimal("0.18"),
|
||||||
|
"iva_16": Decimal("0.16"),
|
||||||
|
"iva_10": Decimal("0.10"),
|
||||||
|
"iva_4": Decimal("0.04"),
|
||||||
|
"iva_exenta": Decimal("0.00"),
|
||||||
|
}
|
||||||
|
rate = rates.get(tax_code)
|
||||||
|
if rate is None:
|
||||||
|
# fallback: copiar total como “impuesto” (mantiene tu comportamiento previo)
|
||||||
|
return int(base * 100)
|
||||||
|
return int((base * rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) * 100)
|
||||||
|
|
||||||
|
|
||||||
|
def tax_fraction_from_code(tax_code: str) -> Decimal:
|
||||||
|
"""
|
||||||
|
Devuelve la fracción (0.21, 0.18, ...) según el tax_code normalizado.
|
||||||
|
Exenta -> 0.
|
||||||
|
"""
|
||||||
|
# Si ya tienes TaxCatalog, puedes delegarlo allí; lo dejo inline para simplicidad.
|
||||||
|
mapping = {
|
||||||
|
'iva_21': Decimal('0.21'),
|
||||||
|
'iva_18': Decimal('0.18'),
|
||||||
|
'iva_16': Decimal('0.16'),
|
||||||
|
'iva_10': Decimal('0.10'),
|
||||||
|
'iva_7_5': Decimal('0.075'),
|
||||||
|
'iva_5': Decimal('0.05'),
|
||||||
|
'iva_4': Decimal('0.04'),
|
||||||
|
'iva_2': Decimal('0.02'),
|
||||||
|
'iva_0': Decimal('0.00'),
|
||||||
|
'iva_exenta': Decimal('0.00'),
|
||||||
|
}
|
||||||
|
return mapping.get(tax_code or '', Decimal('0.00'))
|
||||||
|
|
||||||
|
|
||||||
def reduce_scale_pair(value: int, scale: int, *, min_scale: int = 0) -> Tuple[int, int]:
|
def reduce_scale_pair(value: int, scale: int, *, min_scale: int = 0) -> Tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
Reduce la escala todo lo posible eliminando ceros finales de 'value',
|
Reduce la escala todo lo posible eliminando ceros finales de 'value',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user