.
This commit is contained in:
parent
4a8188d255
commit
9a34e38557
@ -30,12 +30,6 @@ DEV_UECKO_MYSQL_DATABASE = uecko_erp_sync
|
|||||||
DEV_UECKO_MYSQL_USER = rodax
|
DEV_UECKO_MYSQL_USER = rodax
|
||||||
DEV_UECKO_MYSQL_PASSWORD = rodax
|
DEV_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_API_KEY = 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
|
||||||
BREVO_EMAIL_TEMPLATE = 1
|
BREVO_EMAIL_TEMPLATE = 1
|
||||||
|
|||||||
@ -35,12 +35,6 @@ UECKO_MYSQL_DATABASE = uecko
|
|||||||
UECKO_MYSQL_USER = uecko
|
UECKO_MYSQL_USER = uecko
|
||||||
UECKO_MYSQL_PASSWORD = u8Ax5Nw3%sjd
|
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_API_KEY = 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
|
||||||
BREVO_EMAIL_TEMPLATE = 1
|
BREVO_EMAIL_TEMPLATE = 1
|
||||||
|
|||||||
@ -24,27 +24,23 @@ def load_config():
|
|||||||
'UECKO_MYSQL_USER': os.getenv('UECKO_MYSQL_USER'),
|
'UECKO_MYSQL_USER': os.getenv('UECKO_MYSQL_USER'),
|
||||||
'UECKO_MYSQL_PASSWORD': os.getenv('UECKO_MYSQL_PASSWORD'),
|
'UECKO_MYSQL_PASSWORD': os.getenv('UECKO_MYSQL_PASSWORD'),
|
||||||
|
|
||||||
'FACTUGES_ID_EMPRESA': os.getenv('FACTUGES_ID_EMPRESA'),
|
'CTE_COMPANY_ID': os.getenv('CTE_COMPANY_ID'),
|
||||||
'FACTUGES_PRECIO_PUNTO': os.getenv('FACTUGES_PRECIO_PUNTO'),
|
'CTE_SERIE': os.getenv('CTE_SERIE'),
|
||||||
'FACTUGES_NOMBRE_TARIFA': os.getenv('FACTUGES_NOMBRE_TARIFA'),
|
'CTE_STATUS_INVOICE': os.getenv('CTE_STATUS_INVOICE'),
|
||||||
'FACTUGES_CONTRATO_ID_TIENDA': os.getenv('FACTUGES_CONTRATO_ID_TIENDA'),
|
'CTE_IS_PROFORMA': os.getenv('CTE_IS_PROFORMA'),
|
||||||
'FACTUGES_CONTRATO_SITUACION': os.getenv('FACTUGES_CONTRATO_SITUACION'),
|
'CTE_STATUS_VERIFACTU': os.getenv('CTE_STATUS_VERIFACTU'),
|
||||||
'FACTUGES_CONTRATO_ENVIADA_REVISADA': os.getenv('FACTUGES_CONTRATO_ENVIADA_REVISADA'),
|
'CTE_LANGUAGE_CODE': os.getenv('CTE_LANGUAGE_CODE'),
|
||||||
'FACTUGES_CONTRATO_TIPO_DETALLE': os.getenv('FACTUGES_CONTRATO_TIPO_DETALLE'),
|
'CTE_COUNTRY_CODE': os.getenv('CTE_COUNTRY_CODE'),
|
||||||
|
'CTE_IS_COMPANY': os.getenv('CTE_IS_COMPANY'),
|
||||||
|
|
||||||
'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'),
|
|
||||||
|
|
||||||
'VERIFACTU_BASE_URL': os.getenv('VERIFACTU_BASE_URL'),
|
'VERIFACTU_BASE_URL': os.getenv('VERIFACTU_BASE_URL'),
|
||||||
'VERIFACTU_API_KEY': os.getenv('VERIFACTU_API_KEY'),
|
'VERIFACTU_API_KEY': os.getenv('VERIFACTU_API_KEY'),
|
||||||
'VERIFACTU_NIFS_API_KEY': os.getenv('VERIFACTU_NIFS_API_KEY'),
|
'VERIFACTU_NIFS_API_KEY': os.getenv('VERIFACTU_NIFS_API_KEY'),
|
||||||
|
|
||||||
|
# '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'),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
121
app/db/normalizations.py
Normal file
121
app/db/normalizations.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import logging
|
||||||
|
from config import load_config
|
||||||
|
import textwrap
|
||||||
|
from typing import Dict, Any
|
||||||
|
from decimal import Decimal, ROUND_HALF_UP
|
||||||
|
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
|
||||||
|
from striprtf.striprtf import rtf_to_text
|
||||||
|
|
||||||
|
|
||||||
|
def rtf_a_texto_plano(rtf: str) -> str:
|
||||||
|
"""
|
||||||
|
Convierte RTF a texto plano usando striprtf.
|
||||||
|
"""
|
||||||
|
return rtf_to_text(rtf).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_customer_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Normaliza campos del registro de origen 'fd' (factura).
|
||||||
|
"""
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
# >>> 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 {
|
||||||
|
"is_company": config["CTE_IS_COMPANY"],
|
||||||
|
"tin": clean_tin(str(fd.get("NIF_CIF"))),
|
||||||
|
"name": str(fd.get("NOMBRE")),
|
||||||
|
"street": str(fd.get("CALLE")),
|
||||||
|
"city": str(fd.get("POBLACION")),
|
||||||
|
"province": str(fd.get("PROVINCIA")),
|
||||||
|
"postal_code": str(fd.get("CODIGO_POSTAL")),
|
||||||
|
"country": config['CTE_COUNTRY_CODE'],
|
||||||
|
"language_code": config['CTE_LANGUAGE_CODE'],
|
||||||
|
"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 normalize_header_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Normaliza campos del registro de origen 'fd' (factura).
|
||||||
|
|
||||||
|
# campos pendiente de revisar en un futuro
|
||||||
|
# xxxxxxx = str(factura_detalle['ID_EMPRESA'])
|
||||||
|
# xxxxxxx = str(factura_detalle['OBSERVACIONES'])
|
||||||
|
"""
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"company_id": config['CTE_COMPANY_ID'],
|
||||||
|
"is_proforma": config["CTE_IS_PROFORMA"],
|
||||||
|
"status": config["CTE_STATUS_INVOICE"],
|
||||||
|
"series": config['CTE_SERIE'],
|
||||||
|
|
||||||
|
"factuges_id": int(fd['ID_FACTURA']),
|
||||||
|
"reference": str(fd['REFERENCIA']),
|
||||||
|
"invoice_date": str(fd['FECHA_FACTURA']),
|
||||||
|
"operation_date": str(fd['FECHA_FACTURA']),
|
||||||
|
"description": textwrap.shorten(
|
||||||
|
f"{str(fd['REFERENCIA'])} - {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')),
|
||||||
|
'tax_code': map_tax_code(str(fd.get("DES_TIPO_IVA"))),
|
||||||
|
'taxes_amount_value': cents((fd.get('IMPORTE_IVA') or 0) + (fd.get('IMPORTE_RE') or 0)),
|
||||||
|
'total_amount_value': cents(fd.get('IMPORTE_TOTAL')),
|
||||||
|
|
||||||
|
'base': cents(fd.get('BASE_IMPONIBLE')),
|
||||||
|
'iva': cents(fd.get('IMPORTE_IVA')),
|
||||||
|
're': cents(fd.get('IMPORTE_RE'))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_details_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Normaliza campos del registro de origen 'fd' (factura).
|
||||||
|
"""
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
tax_code = map_tax_code(str(fd.get("DES_TIPO_IVA")))
|
||||||
|
disc_pct = fd.get('DESCUENTO_DET')
|
||||||
|
|
||||||
|
# Calcular cuota de IVA de la línea
|
||||||
|
frac = tax_fraction_from_code(tax_code) # 0.21, 0.10, 0…
|
||||||
|
# Ttotal_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())
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
return {
|
||||||
|
'tax_code': tax_code,
|
||||||
|
'position': int(fd['POSICION']),
|
||||||
|
'description': rtf_a_texto_plano(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': disc_pct,
|
||||||
|
'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')),
|
||||||
|
'tax_amount': tax_amount
|
||||||
|
}
|
||||||
@ -1,36 +1,37 @@
|
|||||||
# =========================
|
# =========================
|
||||||
# MYSQL (constantes)
|
# MYSQL (constantes)
|
||||||
# =========================
|
# =========================
|
||||||
|
|
||||||
|
SELECT_INVOICES_DELETED = (
|
||||||
|
"SELECT ci.id "
|
||||||
|
"FROM customer_invoices as ci "
|
||||||
|
"WHERE "
|
||||||
|
"(ci.deleted_at is not null) "
|
||||||
|
)
|
||||||
|
|
||||||
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
INSERT_CUSTOMER = (
|
|
||||||
"INSERT INTO customers (id, name, tin, street, city, province, postal_code, country, "
|
|
||||||
"phone_primary, phone_secondary, mobile_primary, mobile_secondary, "
|
|
||||||
"email_primary, email_secondary, website, factuges_id, company_id, is_company, "
|
|
||||||
"language_code, currency_code, status, created_at, updated_at) "
|
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,1,'es','EUR','active',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
|
||||||
)
|
|
||||||
|
|
||||||
UPDATE_CUSTOMER = (
|
|
||||||
"UPDATE customers SET name=%s, tin=%s, street=%s, city=%s, province=%s, postal_code=%s, country=%s, "
|
|
||||||
"phone_primary=%s, phone_secondary=%s, mobile_primary=%s, mobile_secondary=%s, "
|
|
||||||
"email_primary=%s, email_secondary=%s, website=%s, updated_at=CURRENT_TIMESTAMP "
|
|
||||||
"WHERE (id=%s)"
|
|
||||||
)
|
|
||||||
|
|
||||||
SELECT_PAYMENT_METHOD_BY_FACTUGES = (
|
SELECT_PAYMENT_METHOD_BY_FACTUGES = (
|
||||||
"SELECT payment_methods.id FROM payment_methods WHERE payment_methods.factuges_id=%s"
|
"SELECT payment_methods.id FROM payment_methods WHERE payment_methods.factuges_id=%s"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
INSERT_CUSTOMER = (
|
||||||
|
"INSERT INTO customers (id, name, tin, street, city, province, postal_code, country, language_code, "
|
||||||
|
"phone_primary, phone_secondary, mobile_primary, mobile_secondary, "
|
||||||
|
"email_primary, email_secondary, website, factuges_id, company_id, is_company, "
|
||||||
|
"currency_code, status, created_at, updated_at) "
|
||||||
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,'EUR','active',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
||||||
|
)
|
||||||
|
|
||||||
INSERT_PAYMENT_METHOD = (
|
INSERT_PAYMENT_METHOD = (
|
||||||
"INSERT INTO payment_methods (id, description, factuges_id, created_at, updated_at) "
|
"INSERT INTO payment_methods (id, description, factuges_id, created_at, updated_at) "
|
||||||
"VALUES (%s,%s,%s,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
"VALUES (%s,%s,%s,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
||||||
)
|
)
|
||||||
|
|
||||||
INSERT_INVOICE = (
|
INSERT_INVOICE = (
|
||||||
"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, is_proforma, 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, "
|
||||||
"payment_method_id, payment_method_description, factuges_id, "
|
"payment_method_id, payment_method_description, factuges_id, "
|
||||||
@ -40,16 +41,22 @@ INSERT_INVOICE = (
|
|||||||
"%s AS id, "
|
"%s AS id, "
|
||||||
"%s AS company_id, "
|
"%s AS company_id, "
|
||||||
"COALESCE(MAX(invoice_number + 0),0)+1 AS invoice_number, "
|
"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 status, %s AS is_proforma, %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 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 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, "
|
"%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 "
|
"2,2,2,2,2,2, 'es','EUR', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP "
|
||||||
"FROM customer_invoices "
|
"FROM customer_invoices "
|
||||||
"WHERE company_id = %s "
|
"WHERE company_id = %s "
|
||||||
|
"AND is_proforma = %s "
|
||||||
"AND deleted_at is null"
|
"AND deleted_at is null"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
INSERT_VERIFACTU_RECORD = (
|
||||||
|
"INSERT INTO verifactu_records (id, invoice_id, estado, url, qr, uuid, created_at, updated_at) "
|
||||||
|
"VALUES (%s, %s, %s, '', '', '', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)"
|
||||||
|
)
|
||||||
|
|
||||||
INSERT_INVOICE_BAK = (
|
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, "
|
||||||
@ -60,7 +67,6 @@ INSERT_INVOICE_BAK = (
|
|||||||
"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, "
|
||||||
@ -81,17 +87,18 @@ 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 = (
|
UPDATE_CUSTOMER = (
|
||||||
"SELECT ci.id "
|
"UPDATE customers SET name=%s, tin=%s, street=%s, city=%s, province=%s, postal_code=%s, country=%s, language_code=%s, is_company=%s, "
|
||||||
"FROM customer_invoices as ci "
|
"phone_primary=%s, phone_secondary=%s, mobile_primary=%s, mobile_secondary=%s, "
|
||||||
"WHERE "
|
"email_primary=%s, email_secondary=%s, website=%s, updated_at=CURRENT_TIMESTAMP "
|
||||||
"(ci.deleted_at is not null) "
|
"WHERE (id=%s)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# =========================
|
# =========================
|
||||||
# FIREBIRD (constantes)
|
# FIREBIRD (constantes)
|
||||||
# =========================
|
# =========================
|
||||||
|
|
||||||
SELECT_FACTUGES_FACTURAS_CLIENTE = (
|
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"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.CALLE, fac.POBLACION, fac.PROVINCIA, fac.CODIGO_POSTAL, fac.FECHA_ALTA, "
|
||||||
@ -124,3 +131,47 @@ LIMPIAR_FACTUGES_LINK = (
|
|||||||
"VERIFACTU = 0 "
|
"VERIFACTU = 0 "
|
||||||
"WHERE (ID_VERIFACTU = ?)"
|
"WHERE (ID_VERIFACTU = ?)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# SENTENCIAS PARA VERIFACTI
|
||||||
|
# =========================
|
||||||
|
|
||||||
|
# OPCION A SACAMOS EL RESUMEN DE LA TAXES DE LA CABECERA
|
||||||
|
consulta_sql_customer_invoices_issue = (
|
||||||
|
f"SELECT ci.id, ci.series, ci.invoice_number, ci.invoice_date, ci.description, ci.customer_tin, ci.customer_name, ci.total_amount_value, ci.total_amount_scale, ci.reference, "
|
||||||
|
f"cit.taxable_amount_scale, cit.taxes_amount_scale, cit.tax_code, sum(cit.taxable_amount_value) as taxable_amount_value, sum(cit.taxes_amount_value) as taxes_amount_value, "
|
||||||
|
f"vr.id as vrId, vr.uuid, vr.estado "
|
||||||
|
f"FROM customer_invoices as ci "
|
||||||
|
f"LEFT JOIN customer_invoice_taxes cit on (ci.id = cit.invoice_id) "
|
||||||
|
f"LEFT JOIN verifactu_records vr on (ci.id = vr.invoice_id) "
|
||||||
|
f"WHERE (ci.is_proforma = 0) AND (ci.status= 'issued') "
|
||||||
|
f"AND (vr.estado <> 'Correcto') "
|
||||||
|
f"group by 1,2,3,4,5,6,7,8,9,10,11 "
|
||||||
|
f"order by reference"
|
||||||
|
)
|
||||||
|
|
||||||
|
# OPCION B SACAMOS LOS IVAS DE LOS DETALLES DE LOS ITEM
|
||||||
|
# SELECT ci.id, ci.series, ci.invoice_number, ci.invoice_date, ci.description, ci.customer_tin, ci.customer_name, ci.total_amount_value,
|
||||||
|
# ciit.tax_code, sum(ciit.taxable_amount_value), sum(ciit.taxes_amount_value)
|
||||||
|
# FROM customer_invoices as ci
|
||||||
|
# LEFT JOIN customer_invoice_items cii on (ci.id = cii.invoice_id)
|
||||||
|
# LEFT JOIN customer_invoice_item_taxes ciit on (cii.item_id = ciit.item_id)
|
||||||
|
# WHERE (ci.is_proforma = 0) AND (ci.status= 'issued')
|
||||||
|
# group by 1,2,3,4,5,6,7,8,9
|
||||||
|
|
||||||
|
update_verifactu_records_with_invoiceId = ("UPDATE verifactu_records "
|
||||||
|
"set estado = %s, "
|
||||||
|
"uuid = %s, "
|
||||||
|
"url = %s, "
|
||||||
|
"qr = %s, "
|
||||||
|
"updated_at = CURRENT_TIMESTAMP "
|
||||||
|
"WHERE invoice_id = %s"
|
||||||
|
)
|
||||||
|
|
||||||
|
update_verifactu_records_with_uuid = ("UPDATE verifactu_records "
|
||||||
|
"set estado = %s,"
|
||||||
|
"operacion = %s, "
|
||||||
|
"updated_at = CURRENT_TIMESTAMP "
|
||||||
|
"WHERE uuid = %s "
|
||||||
|
)
|
||||||
|
|||||||
@ -1,18 +1,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import textwrap
|
from typing import Dict, Any
|
||||||
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 . import sql_sentences as SQL
|
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
|
from . import normalizations as NORMALIZA
|
||||||
from striprtf.striprtf import rtf_to_text
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# constantes
|
|
||||||
# =========================
|
|
||||||
# Compañia RODAX
|
|
||||||
cte_company_id = '5e4dc5b3-96b9-4968-9490-14bd032fec5f'
|
|
||||||
|
|
||||||
|
|
||||||
def sync_invoices(conn_factuges, conn_mysql, last_execution_date):
|
def sync_invoices(conn_factuges, conn_mysql, last_execution_date):
|
||||||
@ -34,7 +25,8 @@ def sync_invoices(conn_factuges, conn_mysql, last_execution_date):
|
|||||||
if ids_verifactu_deleted:
|
if ids_verifactu_deleted:
|
||||||
sync_delete_invoices(conn_factuges, ids_verifactu_deleted, config)
|
sync_delete_invoices(conn_factuges, ids_verifactu_deleted, config)
|
||||||
else:
|
else:
|
||||||
logging.info(f"There are customer invoices deleted")
|
logging.info(
|
||||||
|
f"There are NOT customer invoices deleted since the last run")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if cursor_mysql is not None:
|
if cursor_mysql is not None:
|
||||||
@ -85,7 +77,7 @@ def sync_delete_invoices(conn_factuges, ids_verifactu_deleted, config):
|
|||||||
try:
|
try:
|
||||||
cursor_FactuGES = conn_factuges.cursor()
|
cursor_FactuGES = conn_factuges.cursor()
|
||||||
if ids_verifactu_deleted:
|
if ids_verifactu_deleted:
|
||||||
logging.info(f"Liberate factures: {ids_verifactu_deleted}")
|
logging.info(f"Liberate factuGES: {ids_verifactu_deleted}")
|
||||||
cursor_FactuGES.executemany(SQL.LIMPIAR_FACTUGES_LINK, [(
|
cursor_FactuGES.executemany(SQL.LIMPIAR_FACTUGES_LINK, [(
|
||||||
id_verifactu,) for id_verifactu in ids_verifactu_deleted])
|
id_verifactu,) for id_verifactu in ids_verifactu_deleted])
|
||||||
else:
|
else:
|
||||||
@ -118,13 +110,12 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config):
|
|||||||
for factura_detalle in filas:
|
for factura_detalle in filas:
|
||||||
|
|
||||||
# Preparamos los campos para evitar errores
|
# Preparamos los campos para evitar errores
|
||||||
customer_fields = normalize_customer_fields(factura_detalle)
|
customer_fields = NORMALIZA.normalize_customer_fields(
|
||||||
header_invoice_fields = normalize_header_invoice_fields(
|
|
||||||
factura_detalle)
|
factura_detalle)
|
||||||
details_invoice_fields = normalize_details_invoice_fields(
|
header_invoice_fields = NORMALIZA.normalize_header_invoice_fields(
|
||||||
|
factura_detalle)
|
||||||
|
details_invoice_fields = NORMALIZA.normalize_details_invoice_fields(
|
||||||
factura_detalle)
|
factura_detalle)
|
||||||
logging.info(
|
|
||||||
f"FACTURAS_CLIENTE DETALLLLLLLLLLESSSSSSS: {details_invoice_fields}")
|
|
||||||
|
|
||||||
factuges_id = int(factura_detalle['ID_FACTURA'])
|
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:
|
||||||
@ -132,7 +123,7 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config):
|
|||||||
# Comprobamos si existe el cliente del primer item de la factura
|
# Comprobamos si existe el cliente del primer item de la factura
|
||||||
customer_id = get_or_create_customer(
|
customer_id = get_or_create_customer(
|
||||||
cursorMySQL,
|
cursorMySQL,
|
||||||
cte_company_id,
|
config['CTE_COMPANY_ID'],
|
||||||
str(factura_detalle["ID_CLIENTE"]),
|
str(factura_detalle["ID_CLIENTE"]),
|
||||||
customer_fields,
|
customer_fields,
|
||||||
)
|
)
|
||||||
@ -147,15 +138,18 @@ def sync_invoices_from_FACTUGES(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
|
||||||
|
|
||||||
# ---- cabecera factura
|
# ---- cabecera factura
|
||||||
invoice_id = insert_invoice_header(
|
invoice_id = insert_invoice_header(cursorMySQL, customer_fields, header_invoice_fields, customer_id, pm_id, str(
|
||||||
cursorMySQL, cte_company_id, customer_fields, header_invoice_fields, customer_id, pm_id, str(
|
factura_detalle["DES_FORMA_PAGO"]), config
|
||||||
factura_detalle["DES_FORMA_PAGO"])
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# ---- impuestos cabecera
|
# ---- impuestos cabecera
|
||||||
insert_header_taxes_if_any(
|
insert_header_taxes_if_any(
|
||||||
cursorMySQL, invoice_id, factura_detalle['IVA'], factura_detalle['RECARGO_EQUIVALENCIA'], header_invoice_fields)
|
cursorMySQL, invoice_id, factura_detalle['IVA'], factura_detalle['RECARGO_EQUIVALENCIA'], header_invoice_fields)
|
||||||
|
|
||||||
|
# ---- registro verifactu
|
||||||
|
insert_verifactu_record(
|
||||||
|
cursorMySQL, header_invoice_fields, invoice_id, config)
|
||||||
|
|
||||||
# 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 {invoice_id} {factuges_id}")
|
f"Updating FACTURAS_CLIENTE {invoice_id} {factuges_id}")
|
||||||
@ -187,109 +181,6 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config):
|
|||||||
cursor_FactuGES.close()
|
cursor_FactuGES.close()
|
||||||
|
|
||||||
|
|
||||||
def rtf_a_texto_plano(rtf: str) -> str:
|
|
||||||
"""
|
|
||||||
Convierte RTF a texto plano usando striprtf.
|
|
||||||
"""
|
|
||||||
return rtf_to_text(rtf).strip()
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_customer_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Normaliza campos del registro de origen 'fd' (factura).
|
|
||||||
"""
|
|
||||||
# >>> 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 normalize_header_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Normaliza campos del registro de origen 'fd' (factura).
|
|
||||||
|
|
||||||
# campos pendiente de revisar en un futuro
|
|
||||||
# xxxxxxx = str(factura_detalle['ID_EMPRESA'])
|
|
||||||
# xxxxxxx = str(factura_detalle['OBSERVACIONES'])
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"factuges_id": int(fd['ID_FACTURA']),
|
|
||||||
"reference": str(fd['REFERENCIA']),
|
|
||||||
"invoice_date": str(fd['FECHA_FACTURA']),
|
|
||||||
"operation_date": str(fd['FECHA_FACTURA']),
|
|
||||||
"description": textwrap.shorten(
|
|
||||||
f"{str(fd['REFERENCIA'])} - {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')),
|
|
||||||
'tax_code': map_tax_code(str(fd.get("DES_TIPO_IVA"))),
|
|
||||||
'taxes_amount_value': cents((fd.get('IMPORTE_IVA') or 0) + (fd.get('IMPORTE_RE') or 0)),
|
|
||||||
'total_amount_value': cents(fd.get('IMPORTE_TOTAL')),
|
|
||||||
|
|
||||||
'base': cents(fd.get('BASE_IMPONIBLE')),
|
|
||||||
'iva': cents(fd.get('IMPORTE_IVA')),
|
|
||||||
're': cents(fd.get('IMPORTE_RE'))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_details_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Normaliza campos del registro de origen 'fd' (factura).
|
|
||||||
"""
|
|
||||||
tax_code = map_tax_code(str(fd.get("DES_TIPO_IVA")))
|
|
||||||
disc_pct = fd.get('DESCUENTO_DET')
|
|
||||||
|
|
||||||
# Calcular cuota de IVA de la línea
|
|
||||||
frac = tax_fraction_from_code(tax_code) # 0.21, 0.10, 0…
|
|
||||||
# Ttotal_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(
|
|
||||||
f"FACTURAS_CLIENTE IVAAAAAAAAAAAAAAA {str(frac)} DETALLE: {str((money_round(line_base * frac, 2)*100))}")
|
|
||||||
# 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
|
|
||||||
|
|
||||||
return {
|
|
||||||
'tax_code': tax_code,
|
|
||||||
'position': int(fd['POSICION']),
|
|
||||||
'description': rtf_a_texto_plano(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': disc_pct,
|
|
||||||
'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')),
|
|
||||||
'tax_amount': tax_amount
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_customer(cur, company_id: str, factuges_customer_id: str, fields: Dict[str, Any]) -> str:
|
def get_or_create_customer(cur, company_id: str, factuges_customer_id: str, fields: Dict[str, Any]) -> str:
|
||||||
"""
|
"""
|
||||||
Comprobamos si existe el cliente del primer item de la factura y si no lo creamos
|
Comprobamos si existe el cliente del primer item de la factura y si no lo creamos
|
||||||
@ -304,9 +195,9 @@ def get_or_create_customer(cur, company_id: str, factuges_customer_id: str, fiel
|
|||||||
SQL.INSERT_CUSTOMER,
|
SQL.INSERT_CUSTOMER,
|
||||||
(
|
(
|
||||||
customer_id, fields["name"], fields["tin"], fields["street"], fields["city"], fields["province"],
|
customer_id, fields["name"], fields["tin"], fields["street"], fields["city"], fields["province"],
|
||||||
fields["postal_code"], fields["country"], fields["phone_primary"], fields["phone_secondary"],
|
fields["postal_code"], fields["country"], fields["language_code"], fields["phone_primary"], fields["phone_secondary"],
|
||||||
fields["mobile_primary"], fields["mobile_secondary"], fields["email_primary"], fields["email_secondary"],
|
fields["mobile_primary"], fields["mobile_secondary"], fields["email_primary"], fields["email_secondary"],
|
||||||
fields["website"], factuges_customer_id, company_id,
|
fields["website"], factuges_customer_id, company_id, fields["is_company"]
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return customer_id
|
return customer_id
|
||||||
@ -316,7 +207,7 @@ def get_or_create_customer(cur, company_id: str, factuges_customer_id: str, fiel
|
|||||||
SQL.UPDATE_CUSTOMER,
|
SQL.UPDATE_CUSTOMER,
|
||||||
(
|
(
|
||||||
fields["name"], fields["tin"], fields["street"], fields["city"], fields["province"], fields["postal_code"],
|
fields["name"], fields["tin"], fields["street"], fields["city"], fields["province"], fields["postal_code"],
|
||||||
fields["country"], fields["phone_primary"], fields["phone_secondary"],
|
fields["country"], fields["language_code"], fields["is_company"], fields["phone_primary"], fields["phone_secondary"],
|
||||||
fields["mobile_primary"], fields["mobile_secondary"], fields["email_primary"], fields["email_secondary"],
|
fields["mobile_primary"], fields["mobile_secondary"], fields["email_primary"], fields["email_secondary"],
|
||||||
fields["website"], customer_id,
|
fields["website"], customer_id,
|
||||||
),
|
),
|
||||||
@ -342,19 +233,19 @@ def get_or_create_payment_method(cur, factuges_payment_id: str, description: str
|
|||||||
return pm_id
|
return pm_id
|
||||||
|
|
||||||
|
|
||||||
def insert_invoice_header(cur, company_id: str, cf: Dict[str, Any], hif: Dict[str, Any], customer_id: str,
|
def insert_invoice_header(cur: str, cf: Dict[str, Any], hif: Dict[str, Any], customer_id: str,
|
||||||
payment_method_id: str, payment_method_description: str) -> str:
|
payment_method_id: str, payment_method_description: str, config) -> str:
|
||||||
"""
|
"""
|
||||||
Inserta cabecera y devuelve invoice_id
|
Inserta cabecera y devuelve invoice_id
|
||||||
"""
|
"""
|
||||||
invoice_id = str(uuid7())
|
invoice_id = str(uuid7())
|
||||||
|
|
||||||
logging.info("Inserting invoice %s %s %s",
|
logging.info("Inserting invoice %s %s %s %s",
|
||||||
invoice_id, hif.get('reference'), hif.get('invoice_date'))
|
invoice_id, hif.get('reference'), hif.get('invoice_date'), config['CTE_STATUS_INVOICE'])
|
||||||
cur.execute(
|
cur.execute(
|
||||||
SQL.INSERT_INVOICE,
|
SQL.INSERT_INVOICE,
|
||||||
(
|
(
|
||||||
invoice_id, company_id, 'draft', 'F25/', hif.get('reference'), hif.get(
|
invoice_id, hif.get('company_id'), hif.get('status'), hif.get('is_proforma'), hif.get('series'), hif.get('reference'), hif.get(
|
||||||
'invoice_date'), hif.get('operation_date'), hif.get('description'),
|
'invoice_date'), hif.get('operation_date'), hif.get('description'),
|
||||||
hif.get('subtotal_amount_value'), hif.get('discount_amount_value'), hif.get(
|
hif.get('subtotal_amount_value'), hif.get('discount_amount_value'), hif.get(
|
||||||
'discount_percentage_val'), hif.get('taxable_amount_value'),
|
'discount_percentage_val'), hif.get('taxable_amount_value'),
|
||||||
@ -363,12 +254,29 @@ def insert_invoice_header(cur, company_id: str, cf: Dict[str, Any], hif: Dict[st
|
|||||||
'name'), cf.get('street'), cf.get('city'),
|
'name'), cf.get('street'), cf.get('city'),
|
||||||
cf.get('province'), cf.get('postal_code'), 'es',
|
cf.get('province'), cf.get('postal_code'), 'es',
|
||||||
payment_method_id, payment_method_description, hif.get(
|
payment_method_id, payment_method_description, hif.get(
|
||||||
'factuges_id'), company_id
|
'factuges_id'), hif.get('company_id'), hif.get('is_proforma')
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return invoice_id
|
return invoice_id
|
||||||
|
|
||||||
|
|
||||||
|
def insert_verifactu_record(cur: str, hif: Dict[str, Any], invoice_id: str, config) -> str:
|
||||||
|
"""
|
||||||
|
Inserta registro verifactu vacio y devuelve id
|
||||||
|
"""
|
||||||
|
id = str(uuid7())
|
||||||
|
|
||||||
|
logging.info("Inserting verifactu record %s %s %s",
|
||||||
|
id, hif.get('reference'), hif.get('invoice_date'))
|
||||||
|
cur.execute(
|
||||||
|
SQL.INSERT_VERIFACTU_RECORD,
|
||||||
|
(
|
||||||
|
id, invoice_id, config['CTE_STATUS_VERIFACTU']
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return id
|
||||||
|
|
||||||
|
|
||||||
def insert_header_taxes_if_any(cur, invoice_id: str, IVA: str, RECARGO: str, hif: Dict[str, Any]) -> None:
|
def insert_header_taxes_if_any(cur, invoice_id: str, IVA: str, RECARGO: str, hif: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Inserta impuestos de cabecera
|
Inserta impuestos de cabecera
|
||||||
|
|||||||
@ -4,49 +4,18 @@ from typing import Dict, Any, Tuple, Optional, List, Iterable
|
|||||||
from config import load_config
|
from config import load_config
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from utils import validar_nif, estado_factura, crear_factura, TaxCatalog, unscale_to_str
|
from utils import validar_nif, estado_factura, crear_factura, TaxCatalog, unscale_to_str
|
||||||
|
from . import sql_sentences as SQL
|
||||||
|
|
||||||
|
|
||||||
def sync_invoices_verifactu(conn_mysql, last_execution_date):
|
def sync_invoices_verifactu(conn_mysql, last_execution_date):
|
||||||
config = load_config()
|
config = load_config()
|
||||||
|
|
||||||
# OPCION A SACAMOS EL RESUMEN DE LA TAXES DE LA CABECERA
|
|
||||||
# SELECT ci.id, ci.series, ci.invoice_number, ci.invoice_date, ci.description, ci.customer_tin, ci.customer_name, ci.total_amount_value,
|
|
||||||
# cit.tax_code, sum(cit.taxable_amount_value), sum(cit.taxes_amount_value)
|
|
||||||
# FROM customer_invoices as ci
|
|
||||||
# LEFT JOIN customer_invoice_taxes cit on (ci.id = cit.invoice_id)
|
|
||||||
# WHERE (ci.is_proforma = 0) AND (ci.status= 'draft')
|
|
||||||
# group by 1,2,3,4,5,6,7,8,9
|
|
||||||
|
|
||||||
|
|
||||||
# OPCION B SACAMOS LOS IVAS DE LOS DETALLES DE LOS ITEM
|
|
||||||
# SELECT ci.id, ci.series, ci.invoice_number, ci.invoice_date, ci.description, ci.customer_tin, ci.customer_name, ci.total_amount_value,
|
|
||||||
# ciit.tax_code, sum(ciit.taxable_amount_value), sum(ciit.taxes_amount_value)
|
|
||||||
# FROM customer_invoices as ci
|
|
||||||
# LEFT JOIN customer_invoice_items cii on (ci.id = cii.invoice_id)
|
|
||||||
# LEFT JOIN customer_invoice_item_taxes ciit on (cii.item_id = ciit.item_id)
|
|
||||||
# WHERE (ci.is_proforma = 0) AND (ci.status= 'issued')
|
|
||||||
# group by 1,2,3,4,5,6,7,8,9
|
|
||||||
|
|
||||||
# Recorrer todas las facturas emitidas para madarlas o refrescar los campos
|
|
||||||
consulta_sql_customer_invoices_issue = (
|
|
||||||
f"SELECT ci.id, ci.series, ci.invoice_number, ci.invoice_date, ci.description, ci.customer_tin, ci.customer_name, ci.total_amount_value, ci.total_amount_scale, ci.reference, "
|
|
||||||
f"cit.taxable_amount_scale, cit.taxes_amount_scale, cit.tax_code, sum(cit.taxable_amount_value) as taxable_amount_value, sum(cit.taxes_amount_value) as taxes_amount_value, "
|
|
||||||
f"vr.id as vrId, vr.uuid, vr.estado "
|
|
||||||
f"FROM customer_invoices as ci "
|
|
||||||
f"LEFT JOIN customer_invoice_taxes cit on (ci.id = cit.invoice_id) "
|
|
||||||
f"LEFT JOIN verifactu_records vr on (ci.id = vr.invoice_id) "
|
|
||||||
f"WHERE (ci.is_proforma = 0) AND (ci.status= 'issued') "
|
|
||||||
f"AND ((vr.estado is null) OR (vr.estado <> 'Correcto')) "
|
|
||||||
f"group by 1,2,3,4,5,6,7,8,9,10,11 "
|
|
||||||
f"order by reference"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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
|
# Ejecutar la consulta de customer invoices a enviar
|
||||||
cursor_mysql.execute(consulta_sql_customer_invoices_issue)
|
cursor_mysql.execute(SQL.consulta_sql_customer_invoices_issue)
|
||||||
filas = cursor_mysql.fetchall()
|
filas = cursor_mysql.fetchall()
|
||||||
|
|
||||||
# Obtener los nombres de las columnas
|
# Obtener los nombres de las columnas
|
||||||
@ -64,7 +33,7 @@ def sync_invoices_verifactu(conn_mysql, last_execution_date):
|
|||||||
# Verificar si hay filas en el resultado
|
# Verificar si hay filas en el resultado
|
||||||
if tuplas_seleccionadas:
|
if tuplas_seleccionadas:
|
||||||
enviar_datos(tuplas_seleccionadas, cursor_mysql, config)
|
enviar_datos(tuplas_seleccionadas, cursor_mysql, config)
|
||||||
logging.info(f"Ha ido bien enviar_datos")
|
logging.info(f"Ok send Verifactu")
|
||||||
else:
|
else:
|
||||||
logging.info(f"There are no rows to send")
|
logging.info(f"There are no rows to send")
|
||||||
|
|
||||||
@ -84,7 +53,7 @@ def enviar_datos(invoices_to_verifactu, cursor_mysql, config):
|
|||||||
invoice_id = None
|
invoice_id = None
|
||||||
factura = None
|
factura = None
|
||||||
for fila in invoices_to_verifactu:
|
for fila in invoices_to_verifactu:
|
||||||
# Si los ids de factura anterior y actual no coinciden o empezamos factura nueva, miramos si ya existe una factura si es así la mandamos AEAT
|
# Si los ids de factura anterior y actual no coinciden, empezamos factura nueva, miramos si ya existe una factura si es así la mandamos AEAT
|
||||||
# y creamos la cabecera de la factura siguiente, si no existe factura solo la creamos
|
# y creamos la cabecera de la factura siguiente, si no existe factura solo la creamos
|
||||||
if invoice_id != str(fila['id']):
|
if invoice_id != str(fila['id']):
|
||||||
|
|
||||||
@ -132,26 +101,15 @@ def procesar_factura_verifactu(
|
|||||||
config: Dict[str, Any]
|
config: Dict[str, Any]
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
insert_verifactu_records = ("INSERT INTO verifactu_records (id, invoice_id, estado, uuid, url, qr, created_at, updated_at) "
|
|
||||||
"VALUES (%s, %s, %s, %s, %s, %s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) "
|
|
||||||
)
|
|
||||||
|
|
||||||
update_verifactu_records = ("UPDATE verifactu_records "
|
|
||||||
"set estado = %s,"
|
|
||||||
"operacion = %s, "
|
|
||||||
"updated_at = CURRENT_TIMESTAMP "
|
|
||||||
"WHERE uuid = %s "
|
|
||||||
)
|
|
||||||
|
|
||||||
if factura != None:
|
if factura != None:
|
||||||
# Creamos registro de factura en verifactu
|
# Creamos registro de factura en verifactu
|
||||||
if factura.get('uuid') is None:
|
if factura.get('uuid') == '':
|
||||||
# logging.info(f"Send to create Verifactu: {factura}")
|
# logging.info(f"Send to create Verifactu: {factura}")
|
||||||
respuesta = crear_factura(factura, config)
|
respuesta = crear_factura(factura, config)
|
||||||
if respuesta.get("status") == 200 and respuesta.get("ok"):
|
if respuesta.get("status") == 200 and respuesta.get("ok"):
|
||||||
data = respuesta.get("data")
|
data = respuesta.get("data")
|
||||||
cursor_mysql.execute(insert_verifactu_records, (str(uuid4()), factura.get(
|
cursor_mysql.execute(SQL.update_verifactu_records_with_invoiceId, (data.get("estado"), data.get(
|
||||||
"id"), data.get("estado"), data.get("uuid"), data.get("url"), data.get("qr")))
|
"uuid"), data.get("url"), data.get("qr"), factura.get("id")))
|
||||||
logging.info(
|
logging.info(
|
||||||
f">>> Factura {factura.get("reference")} registrada en Verifactu")
|
f">>> Factura {factura.get("reference")} registrada en Verifactu")
|
||||||
return True
|
return True
|
||||||
@ -165,7 +123,7 @@ def procesar_factura_verifactu(
|
|||||||
respuesta = estado_factura(factura.get('uuid'), config)
|
respuesta = estado_factura(factura.get('uuid'), config)
|
||||||
if respuesta.get("status") == 200 and respuesta.get("ok"):
|
if respuesta.get("status") == 200 and respuesta.get("ok"):
|
||||||
data = respuesta.get("data")
|
data = respuesta.get("data")
|
||||||
cursor_mysql.execute(update_verifactu_records, (data.get(
|
cursor_mysql.execute(SQL.update_verifactu_records_with_uuid, (data.get(
|
||||||
'estado'), data.get('operacion'), factura.get('uuid')))
|
'estado'), data.get('operacion'), factura.get('uuid')))
|
||||||
logging.info(
|
logging.info(
|
||||||
f">>> Factura {factura.get("reference")} actualizado registro de Verifactu")
|
f">>> Factura {factura.get("reference")} actualizado registro de Verifactu")
|
||||||
|
|||||||
@ -60,7 +60,7 @@ def main():
|
|||||||
# Sync Verifactu
|
# Sync Verifactu
|
||||||
logging.info(
|
logging.info(
|
||||||
f">>>>>>>>>> Sync facturas emitidas en FactuGES web to Verifactu")
|
f">>>>>>>>>> Sync facturas emitidas en FactuGES web to Verifactu")
|
||||||
# sync_invoices_verifactu(conn_mysql, last_execution_date_local_tz)
|
sync_invoices_verifactu(conn_mysql, last_execution_date_local_tz)
|
||||||
conn_mysql.commit()
|
conn_mysql.commit()
|
||||||
conn_mysql.close()
|
conn_mysql.close()
|
||||||
logging.info(f"FIN Sync Verifactu >>>>>>>>>>")
|
logging.info(f"FIN Sync Verifactu >>>>>>>>>>")
|
||||||
|
|||||||
@ -44,8 +44,6 @@ def tax_fraction_from_code(tax_code: str) -> Decimal:
|
|||||||
Devuelve la fracción (0.21, 0.18, ...) según el tax_code normalizado.
|
Devuelve la fracción (0.21, 0.18, ...) según el tax_code normalizado.
|
||||||
Exenta -> 0.
|
Exenta -> 0.
|
||||||
"""
|
"""
|
||||||
logging.info(f"FACTURAS_CLIENTE IVAAAAAAAAAAAAAAA {tax_code}")
|
|
||||||
|
|
||||||
# Si ya tienes TaxCatalog, puedes delegarlo allí; lo dejo inline para simplicidad.
|
# Si ya tienes TaxCatalog, puedes delegarlo allí; lo dejo inline para simplicidad.
|
||||||
mapping = {
|
mapping = {
|
||||||
'iva_21': Decimal('0.21'),
|
'iva_21': Decimal('0.21'),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user