revision teniendo en cuenta calculos impuestos en detalles y dto cabecera o global
This commit is contained in:
parent
a34062d392
commit
6b63474cbc
@ -1,21 +1,24 @@
|
|||||||
import textwrap
|
import textwrap
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from decimal import Decimal
|
from decimal import ROUND_HALF_UP, Decimal
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from striprtf.striprtf import rtf_to_text
|
from striprtf.striprtf import rtf_to_text
|
||||||
|
|
||||||
from app.config import load_config
|
from app.config import load_config, logger
|
||||||
from app.utils import (
|
from app.utils import (
|
||||||
|
apply_discount_cents4,
|
||||||
cents,
|
cents,
|
||||||
cents4,
|
cents4,
|
||||||
corregir_y_validar_email,
|
corregir_y_validar_email,
|
||||||
limpiar_cadena,
|
limpiar_cadena,
|
||||||
map_tax_code,
|
map_iva_code,
|
||||||
|
map_rec_by_iva_code,
|
||||||
money_round,
|
money_round,
|
||||||
normalizar_telefono_con_plus,
|
normalizar_telefono_con_plus,
|
||||||
normalizar_url_para_insert,
|
normalizar_url_para_insert,
|
||||||
tax_fraction_from_code,
|
tax_fraction_from_code,
|
||||||
|
unscale_to_decimal,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -70,6 +73,25 @@ def normalize_header_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
"""
|
"""
|
||||||
config = load_config()
|
config = load_config()
|
||||||
|
|
||||||
|
iva_code = map_iva_code(str(fd.get("DES_TIPO_IVA")))
|
||||||
|
# Cuota de IVA de la factura
|
||||||
|
iva_frac: Optional[Decimal] = tax_fraction_from_code(iva_code) # p.ej. Decimal('0.21') o Decimal('0')
|
||||||
|
iva_percentage_value = None if (
|
||||||
|
iva_frac is None or iva_frac == 0) else cents4(iva_frac)
|
||||||
|
iva_amount = cents(fd.get('IMPORTE_IVA'))
|
||||||
|
|
||||||
|
# Cuota de RE de la factura
|
||||||
|
rec_code = None
|
||||||
|
rec_frac = None
|
||||||
|
rec_percentage_value = None
|
||||||
|
rec_amount = None
|
||||||
|
importe_re = Decimal(str(fd.get("IMPORTE_RE") or 0))
|
||||||
|
if importe_re > 0:
|
||||||
|
rec_code = map_rec_by_iva_code(iva_code)
|
||||||
|
rec_frac: Optional[Decimal] = tax_fraction_from_code(rec_code) # p.ej. Decimal('0.05') o Decimal('0.01')
|
||||||
|
rec_percentage_value = None if (rec_frac is None or rec_frac == 0) else cents4(rec_frac)
|
||||||
|
rec_amount = cents(fd.get('IMPORTE_RE'))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"company_id": config['CTE_COMPANY_ID'],
|
"company_id": config['CTE_COMPANY_ID'],
|
||||||
"is_proforma": config["CTE_IS_PROFORMA"],
|
"is_proforma": config["CTE_IS_PROFORMA"],
|
||||||
@ -90,14 +112,19 @@ def normalize_header_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
'discount_percentage_val': int((Decimal(str(fd.get('DESCUENTO') or 0)) * 100).to_integral_value())
|
'discount_percentage_val': int((Decimal(str(fd.get('DESCUENTO') or 0)) * 100).to_integral_value())
|
||||||
if fd.get('DESCUENTO') is not None else 0,
|
if fd.get('DESCUENTO') is not None else 0,
|
||||||
'taxable_amount_value': cents(fd.get('BASE_IMPONIBLE')),
|
'taxable_amount_value': cents(fd.get('BASE_IMPONIBLE')),
|
||||||
'tax_code': map_tax_code(str(fd.get("DES_TIPO_IVA"))),
|
'iva_code': iva_code,
|
||||||
|
'iva_percentage_value': iva_percentage_value,
|
||||||
|
'rec_code': rec_code,
|
||||||
|
'rec_percentage_value': rec_percentage_value,
|
||||||
|
'base': cents(fd.get('BASE_IMPONIBLE')),
|
||||||
|
'iva': iva_amount,
|
||||||
|
're': rec_amount,
|
||||||
'taxes_amount_value': cents((fd.get('IMPORTE_IVA') or 0) + (fd.get('IMPORTE_RE') or 0)),
|
'taxes_amount_value': cents((fd.get('IMPORTE_IVA') or 0) + (fd.get('IMPORTE_RE') or 0)),
|
||||||
'total_amount_value': cents(fd.get('IMPORTE_TOTAL')),
|
'total_amount_value': cents(fd.get('IMPORTE_TOTAL')),
|
||||||
|
|
||||||
'base': cents(fd.get('BASE_IMPONIBLE')),
|
'retention_code': None,
|
||||||
'iva': cents(fd.get('IMPORTE_IVA')),
|
'retention_percentage_value': None,
|
||||||
're': cents(fd.get('IMPORTE_RE'))
|
'retention_amount_value': None,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -114,19 +141,13 @@ def normalize_details_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
"""
|
"""
|
||||||
config = load_config()
|
config = load_config()
|
||||||
|
|
||||||
iva_code = map_tax_code(str(fd.get("DES_TIPO_IVA")))
|
|
||||||
# Calcular cuota de IVA de la línea
|
|
||||||
iva_frac: Optional[Decimal] = tax_fraction_from_code(iva_code) # p.ej. Decimal('0.21') o Decimal('0')
|
|
||||||
iva_percentage_value = None if (
|
|
||||||
iva_frac is None or iva_frac == 0) else cents4(iva_frac)
|
|
||||||
|
|
||||||
rec_code = None
|
|
||||||
rec_percentage_value = None
|
|
||||||
rec_amount = None
|
|
||||||
|
|
||||||
# --- Campos base del origen ---
|
# --- Campos base del origen ---
|
||||||
cantidad = fd.get("CANTIDAD")
|
cantidad = fd.get("CANTIDAD")
|
||||||
importe_unidad = fd.get("IMPORTE_UNIDAD")
|
importe_unidad = fd.get("IMPORTE_UNIDAD")
|
||||||
|
c = Decimal(str(cantidad or 0))
|
||||||
|
u = Decimal(str(importe_unidad or 0))
|
||||||
|
subtotal_amount_value = int((c * u * 10000).to_integral_value(rounding=ROUND_HALF_UP))
|
||||||
|
|
||||||
# total de línea (ya con descuento)
|
# total de línea (ya con descuento)
|
||||||
total_det = fd.get("IMPORTE_TOTAL_DET")
|
total_det = fd.get("IMPORTE_TOTAL_DET")
|
||||||
concepto_rtf = fd.get("CONCEPTO")
|
concepto_rtf = fd.get("CONCEPTO")
|
||||||
@ -137,41 +158,78 @@ def normalize_details_invoice_fields(fd: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
unit_value = None if importe_unidad is None else cents4(
|
unit_value = None if importe_unidad is None else cents4(
|
||||||
importe_unidad) # escala 4
|
importe_unidad) # escala 4
|
||||||
|
|
||||||
# --- descuento % (escala 2) ---
|
# --- descuento de linea % (escala 2) ---
|
||||||
disc_raw = fd.get("DESCUENTO_DET")
|
disc_raw = fd.get("DESCUENTO_DET")
|
||||||
disc_pct: Optional[Decimal] = None if disc_raw is None else Decimal(
|
disc_pct: Optional[Decimal] = None if disc_raw is None else Decimal(
|
||||||
str(disc_raw))
|
str(disc_raw))
|
||||||
discount_percentage_value = None if (
|
discount_percentage_value = None if (
|
||||||
disc_pct is None or disc_pct == 0) else cents(disc_pct)
|
disc_pct is None or disc_pct == 0) else cents(disc_pct)
|
||||||
|
discount_amount_value, subtotal_amount_with_dto = apply_discount_cents4(subtotal_amount_value, disc_pct)
|
||||||
|
|
||||||
|
# --- descuento global por linea % (escala 2) ---
|
||||||
|
disc_global_raw = fd.get("DESCUENTO")
|
||||||
|
disc_global_pct: Optional[Decimal] = None if disc_global_raw is None else Decimal(str(disc_global_raw))
|
||||||
|
global_percentage_value = None if (disc_global_pct is None or disc_global_pct == 0) else cents(disc_global_pct)
|
||||||
|
global_discount_amount_value, taxable_amount_value = apply_discount_cents4(
|
||||||
|
subtotal_amount_with_dto, disc_global_pct)
|
||||||
|
|
||||||
|
total_discount_amount_value = discount_amount_value + global_discount_amount_value
|
||||||
|
|
||||||
# --- total de línea (escala 4) ---
|
# --- total de línea (escala 4) ---
|
||||||
total_value = cents4(total_det)
|
total_value = cents4(total_det)
|
||||||
|
# DEBE SER LO MISMO LO CALCULADO QUE LO QUE NOS VIENE POR BD DE FACTUGES
|
||||||
|
logger.info("total con dto linea calculado: %s - total que llega: %s", subtotal_amount_with_dto, total_value)
|
||||||
|
# la base imponible sobre la que calcular impuestos es el neto menos el dto de linea y dto global
|
||||||
|
base = unscale_to_decimal(taxable_amount_value, 4)
|
||||||
|
|
||||||
# --- cuota (escala 2) ---
|
logger.info("base imponible calculada: %s - subtotal: %s - descuentodto: %s - descuentoglobal: %s",
|
||||||
# calculamos sobre el total en unidades monetarias con redondeo bancario a 2 decimales
|
base, subtotal_amount_value, discount_amount_value, global_discount_amount_value)
|
||||||
if iva_frac is None:
|
|
||||||
iva_amount = 0
|
|
||||||
else:
|
|
||||||
base = Decimal(str(total_det or 0))
|
|
||||||
iva_amount = cents(money_round(base * iva_frac, 2))
|
|
||||||
|
|
||||||
# Se calcula en el objeto de negocio de nuevo, comprobar si coincide
|
# Calcular cuota de IVA de la línea
|
||||||
# item_discount_amount = (
|
iva_code = map_iva_code(str(fd.get("DES_TIPO_IVA")))
|
||||||
# (factura_detalle['IMPORTE_UNIDAD'] or 0)*((factura_detalle['DESCUENTO'] or 0)/100))*100
|
iva_frac: Optional[Decimal] = tax_fraction_from_code(iva_code) # p.ej. Decimal('0.21') o Decimal('0')
|
||||||
|
iva_percentage_value = None if (
|
||||||
|
iva_frac is None or iva_frac == 0) else cents4(iva_frac)
|
||||||
|
iva_amount_c4 = 0
|
||||||
|
if iva_frac:
|
||||||
|
iva_amount_c4 = cents4(money_round(base * iva_frac, 2)) # escala 4
|
||||||
|
|
||||||
|
# Cuota cuota RE de la línea
|
||||||
|
rec_code = None
|
||||||
|
rec_frac: Optional[Decimal] = None
|
||||||
|
rec_percentage_value = None
|
||||||
|
rec_amount_c4 = 0
|
||||||
|
importe_re = Decimal(str(fd.get("IMPORTE_RE") or 0))
|
||||||
|
if importe_re > 0:
|
||||||
|
rec_code = map_rec_by_iva_code(iva_code)
|
||||||
|
rec_frac = tax_fraction_from_code(rec_code) # p.ej. Decimal('0.05') o Decimal('0.01')
|
||||||
|
if rec_frac and rec_frac != 0:
|
||||||
|
rec_percentage_value = cents4(rec_frac) # escala 4 del % (0.052 -> 520)
|
||||||
|
rec_amount_c4 = cents4(base * rec_frac) # escala 4
|
||||||
|
|
||||||
|
taxes_amount_value = iva_amount_c4 + rec_amount_c4
|
||||||
|
|
||||||
|
total_value = taxable_amount_value + taxes_amount_value
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'position': int(fd.get("POSICION") or 0),
|
'position': int(fd.get("POSICION") or 0),
|
||||||
'description': rtf_a_texto_plano(str(concepto_rtf or "")),
|
'description': rtf_a_texto_plano(str(concepto_rtf or "")),
|
||||||
'quantity_value': quantity_value,
|
'quantity_value': quantity_value,
|
||||||
'unit_value': unit_value,
|
'unit_value': unit_value,
|
||||||
|
'subtotal_amount_value': subtotal_amount_value,
|
||||||
'disc_pct': disc_pct,
|
'disc_pct': disc_pct,
|
||||||
'discount_percentage_value': discount_percentage_value,
|
'discount_percentage_value': discount_percentage_value,
|
||||||
|
'discount_amount_value': discount_amount_value,
|
||||||
|
'global_percentage_value': global_percentage_value,
|
||||||
|
'global_discount_amount_value': global_discount_amount_value,
|
||||||
|
'total_discount_amount_value': total_discount_amount_value,
|
||||||
|
'taxable_amount_value': taxable_amount_value,
|
||||||
'total_value': total_value,
|
'total_value': total_value,
|
||||||
'iva_code': iva_code,
|
'iva_code': iva_code,
|
||||||
'iva_percentage_value': iva_percentage_value,
|
'iva_percentage_value': iva_percentage_value,
|
||||||
'iva_amount': iva_amount,
|
'iva_amount_value': iva_amount_c4,
|
||||||
'rec_code': rec_code,
|
'rec_code': rec_code,
|
||||||
'rec_percentage_value': rec_percentage_value,
|
'rec_percentage_value': rec_percentage_value,
|
||||||
'rec_amount': rec_amount,
|
'rec_amount_value': rec_amount_c4,
|
||||||
|
'taxes_amount_value': taxes_amount_value,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,23 +69,27 @@ INSERT_INVOICE_BAK = (
|
|||||||
|
|
||||||
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, subtotal_amount_value, "
|
||||||
"discount_percentage_value, discount_amount_value, total_amount_value, "
|
"discount_percentage_value, discount_amount_value, global_percentage_value, global_discount_amount_value, total_discount_amount_value, "
|
||||||
|
" taxable_amount_value, total_amount_value, "
|
||||||
|
|
||||||
"iva_code, iva_percentage_value, iva_amount_value, "
|
"iva_code, iva_percentage_value, iva_amount_value, "
|
||||||
"rec_code, rec_percentage_value, rec_amount_value, "
|
"rec_code, rec_percentage_value, rec_amount_value, taxes_amount_value, "
|
||||||
|
|
||||||
"quantity_scale, unit_amount_scale, discount_amount_scale, total_amount_scale, discount_percentage_scale,"
|
"quantity_scale, unit_amount_scale, subtotal_amount_scale, "
|
||||||
"iva_percentage_scale, iva_amount_scale, rec_percentage_scale, rec_amount_scale, retention_percentage_scale, retention_amount_scale,"
|
"discount_percentage_scale, discount_amount_scale, global_percentage_scale, global_discount_amount_scale, total_discount_amount_scale, taxable_amount_scale, total_amount_scale, "
|
||||||
|
"iva_percentage_scale, iva_amount_scale, rec_percentage_scale, rec_amount_scale, taxes_amount_scale, retention_percentage_scale, retention_amount_scale, "
|
||||||
"created_at, updated_at) "
|
"created_at, updated_at) "
|
||||||
|
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,2,4,2,4,2,2,4,2,4,2,4,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,2,4,4,2,4,2,4,4,4,4,2,4,2,4,4,2,4,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
||||||
)
|
)
|
||||||
|
|
||||||
INSERT_INVOICE_TAX = (
|
INSERT_INVOICE_TAX = (
|
||||||
"INSERT INTO customer_invoice_taxes (tax_id, invoice_id, tax_code, taxable_amount_value, taxes_amount_value, "
|
"INSERT INTO customer_invoice_taxes (tax_id, invoice_id, taxable_amount_value, iva_code, iva_percentage_value, iva_amount_value, "
|
||||||
"taxable_amount_scale, taxes_amount_scale, created_at, updated_at) "
|
"rec_code, rec_percentage_value, rec_amount_value, retention_code, retention_percentage_value, retention_amount_value, taxes_amount_value, "
|
||||||
"VALUES (%s,%s,%s,%s,%s,2,2,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
"taxable_amount_scale, iva_percentage_scale, iva_amount_scale, rec_percentage_scale, rec_amount_scale, retention_percentage_scale, retention_amount_scale, taxes_amount_scale, "
|
||||||
|
"created_at, updated_at) "
|
||||||
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,2,2,2,2,2,2,2,2,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)"
|
||||||
)
|
)
|
||||||
|
|
||||||
# INSERT_INVOICE_ITEM_TAX = (
|
# INSERT_INVOICE_ITEM_TAX = (
|
||||||
|
|||||||
@ -166,8 +166,6 @@ def sync_invoices_from_FACTUGES(conn_mysql, filas, conn_factuges, config):
|
|||||||
insert_header_taxes_if_any(
|
insert_header_taxes_if_any(
|
||||||
cursorMySQL,
|
cursorMySQL,
|
||||||
invoice_id,
|
invoice_id,
|
||||||
factura_detalle["IVA"],
|
|
||||||
factura_detalle["RECARGO_EQUIVALENCIA"],
|
|
||||||
header_invoice_fields,
|
header_invoice_fields,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -392,46 +390,25 @@ def insert_verifactu_record(
|
|||||||
def insert_header_taxes_if_any(
|
def insert_header_taxes_if_any(
|
||||||
cur,
|
cur,
|
||||||
invoice_id: str,
|
invoice_id: str,
|
||||||
IVA: str,
|
|
||||||
RECARGO: str,
|
|
||||||
hif: Dict[str, Any],
|
hif: Dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Inserta impuestos de cabecera"""
|
"""Inserta impuestos de cabecera"""
|
||||||
|
|
||||||
# Conversión segura a número
|
cur.execute(SQL.INSERT_INVOICE_TAX,
|
||||||
try:
|
|
||||||
iva_val = float(IVA)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
iva_val = 0.0
|
|
||||||
|
|
||||||
try:
|
|
||||||
rec_val = float(RECARGO)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
rec_val = 0.0
|
|
||||||
|
|
||||||
# IVA (>= 0 acepta 0%)
|
|
||||||
if iva_val >= 0:
|
|
||||||
cur.execute(
|
|
||||||
SQL.INSERT_INVOICE_TAX,
|
|
||||||
(
|
(
|
||||||
str(uuid7()),
|
str(uuid7()),
|
||||||
invoice_id,
|
invoice_id,
|
||||||
hif.get("tax_code"),
|
|
||||||
hif.get("base"),
|
hif.get("base"),
|
||||||
|
hif.get("iva_code"),
|
||||||
|
hif.get("iva_percentage_value"),
|
||||||
hif.get("iva"),
|
hif.get("iva"),
|
||||||
),
|
hif.get("rec_code"),
|
||||||
)
|
hif.get("rec_percentage_value"),
|
||||||
|
|
||||||
# Recargo equivalencia (> 0)
|
|
||||||
if rec_val > 0:
|
|
||||||
cur.execute(
|
|
||||||
SQL.INSERT_INVOICE_TAX,
|
|
||||||
(
|
|
||||||
str(uuid7()),
|
|
||||||
invoice_id,
|
|
||||||
"rec_5_2",
|
|
||||||
hif.get("base"),
|
|
||||||
hif.get("re"),
|
hif.get("re"),
|
||||||
|
hif.get("retention_code"),
|
||||||
|
hif.get("retention_percentage_value"),
|
||||||
|
hif.get("retention_amount_value"),
|
||||||
|
hif.get("taxes_amount_value"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -452,15 +429,21 @@ def insert_item_and_taxes(cur, invoice_id: str, fields: Dict[str, Any]) -> None:
|
|||||||
fields.get("description"),
|
fields.get("description"),
|
||||||
fields.get("quantity_value"),
|
fields.get("quantity_value"),
|
||||||
fields.get("unit_value"),
|
fields.get("unit_value"),
|
||||||
|
fields.get("subtotal_amount_value"),
|
||||||
fields.get("discount_percentage_value"),
|
fields.get("discount_percentage_value"),
|
||||||
None,
|
fields.get("discount_amount_value"),
|
||||||
|
fields.get("global_percentage_value"),
|
||||||
|
fields.get("global_discount_amount_value"),
|
||||||
|
fields.get("total_discount_amount_value"),
|
||||||
|
fields.get("taxable_amount_value"),
|
||||||
fields.get("total_value"),
|
fields.get("total_value"),
|
||||||
fields.get("iva_code"),
|
fields.get("iva_code"),
|
||||||
fields.get("iva_percentage_value"),
|
fields.get("iva_percentage_value"),
|
||||||
fields.get("tax_amount"),
|
fields.get("iva_amount_value"),
|
||||||
fields.get("rec_code"),
|
fields.get("rec_code"),
|
||||||
fields.get("rec_percentage_value"),
|
fields.get("rec_percentage_value"),
|
||||||
fields.get("rec_amount"),
|
fields.get("rec_amount_value"),
|
||||||
|
fields.get("taxes_amount_value"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,27 @@
|
|||||||
from .last_execution_helper import actualizar_fecha_ultima_ejecucion, obtener_fecha_ultima_ejecucion
|
from .importes_helper import (
|
||||||
|
apply_discount_cents4,
|
||||||
|
cents,
|
||||||
|
cents4,
|
||||||
|
money_round,
|
||||||
|
unscale_to_decimal,
|
||||||
|
unscale_to_str,
|
||||||
|
)
|
||||||
|
from .last_execution_helper import (
|
||||||
|
actualizar_fecha_ultima_ejecucion,
|
||||||
|
obtener_fecha_ultima_ejecucion,
|
||||||
|
)
|
||||||
|
from .mails_helper import corregir_y_validar_email
|
||||||
from .password import hashPassword
|
from .password import hashPassword
|
||||||
from .send_orders_mail import send_orders_mail
|
from .send_orders_mail import send_orders_mail
|
||||||
from .text_converter import text_converter, limpiar_cadena
|
from .send_rest_api import crear_factura, estado_factura, validar_nif
|
||||||
from .send_rest_api import validar_nif, estado_factura, crear_factura
|
from .tax_catalog_helper import (
|
||||||
from .tax_catalog_helper import TaxCatalog, get_default_tax_catalog, map_tax_code, calc_item_tax_amount, tax_fraction_from_code
|
TaxCatalog,
|
||||||
from .importes_helper import unscale_to_str, unscale_to_decimal, cents, cents4, money_round
|
calc_item_tax_amount,
|
||||||
|
get_default_tax_catalog,
|
||||||
|
map_iva_code,
|
||||||
|
map_rec_by_iva_code,
|
||||||
|
tax_fraction_from_code,
|
||||||
|
)
|
||||||
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 .text_converter import limpiar_cadena, text_converter
|
||||||
from .websites_helper import normalizar_url_para_insert
|
from .websites_helper import normalizar_url_para_insert
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from decimal import Decimal, ROUND_HALF_UP
|
from decimal import ROUND_HALF_UP, Decimal
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
def cents4(value: Optional[Decimal | float | int]) -> int:
|
def cents4(value: Optional[Decimal | float | int]) -> int:
|
||||||
@ -53,3 +53,29 @@ def unscale_to_str(
|
|||||||
if strip_trailing_zeros and "." in s:
|
if strip_trailing_zeros and "." in s:
|
||||||
s = s.rstrip("0").rstrip(".")
|
s = s.rstrip("0").rstrip(".")
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def calc_discount_cents4(subtotal_cents4: int, disc_pct: Optional[Decimal | float | int]) -> int:
|
||||||
|
"""
|
||||||
|
Calcula el importe de descuento en escala 4 (×10000) como ENTERO.
|
||||||
|
- subtotal_cents4: subtotal ya en escala 4
|
||||||
|
- disc_pct: porcentaje de descuento (p.ej. 10 -> 10%)
|
||||||
|
|
||||||
|
Devuelve un entero NEGATIVO (como en Delphi):
|
||||||
|
ImporteDto := (-1) * ((Subtotal * Descuento) / 100);
|
||||||
|
"""
|
||||||
|
pct = Decimal(str(disc_pct or 0))
|
||||||
|
# descuento = round(subtotal * pct / 100) en la MISMA escala (×10000)
|
||||||
|
disc = (Decimal(subtotal_cents4) * pct / Decimal(100)).to_integral_value(rounding=ROUND_HALF_UP)
|
||||||
|
return disc
|
||||||
|
|
||||||
|
|
||||||
|
def apply_discount_cents4(subtotal_cents4: int, disc_pct: Optional[Decimal | float | int]) -> Tuple[int, int]:
|
||||||
|
"""
|
||||||
|
Devuelve (discount_cents4, total_cents4) ambos en escala 4.
|
||||||
|
- discount_cents4 NEGATIVO
|
||||||
|
- total_cents4 = subtotal_cents4 + discount_cents4
|
||||||
|
"""
|
||||||
|
discount = calc_discount_cents4(subtotal_cents4, disc_pct)
|
||||||
|
total = subtotal_cents4 - discount
|
||||||
|
return discount, total
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from pathlib import Path
|
|||||||
from typing import Any, Dict, Iterable, Optional, Tuple
|
from typing import Any, Dict, Iterable, Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
def map_tax_code(raw: str) -> str:
|
def map_iva_code(raw: str) -> str:
|
||||||
t = (raw or "").strip().lower().replace(" ", "")
|
t = (raw or "").strip().lower().replace(" ", "")
|
||||||
mapping = {
|
mapping = {
|
||||||
"iva21": "iva_21",
|
"iva21": "iva_21",
|
||||||
@ -18,6 +18,18 @@ def map_tax_code(raw: str) -> str:
|
|||||||
return mapping.get(t, "")
|
return mapping.get(t, "")
|
||||||
|
|
||||||
|
|
||||||
|
def map_rec_by_iva_code(raw: str) -> str:
|
||||||
|
t = (raw or "").strip().lower().replace(" ", "")
|
||||||
|
mapping = {
|
||||||
|
"iva_21": "rec_5_2",
|
||||||
|
"iva_10": "rec_1_75",
|
||||||
|
"iva_5": "rec_0_62",
|
||||||
|
"iva_4": "rec_0_5",
|
||||||
|
"iva_7_5": "rec_1",
|
||||||
|
}
|
||||||
|
return mapping.get(t, "")
|
||||||
|
|
||||||
|
|
||||||
def calc_item_tax_amount(tax_code: str, importe_total_det: Optional[Decimal | float | int]) -> int:
|
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.
|
Devuelve el impuesto de línea escalado a céntimos (x100) como entero.
|
||||||
@ -30,6 +42,11 @@ def calc_item_tax_amount(tax_code: str, importe_total_det: Optional[Decimal | fl
|
|||||||
"iva_10": Decimal("0.10"),
|
"iva_10": Decimal("0.10"),
|
||||||
"iva_4": Decimal("0.04"),
|
"iva_4": Decimal("0.04"),
|
||||||
"iva_exenta": Decimal("0.00"),
|
"iva_exenta": Decimal("0.00"),
|
||||||
|
"rec_5_2": Decimal('0.052'),
|
||||||
|
"rec_1_75": Decimal('0.0175'),
|
||||||
|
"rec_0_62": Decimal('0.0062'),
|
||||||
|
"rec_0_5": Decimal('0.005'),
|
||||||
|
"rec_1": Decimal('0.01')
|
||||||
}
|
}
|
||||||
rate = rates.get(tax_code)
|
rate = rates.get(tax_code)
|
||||||
if rate is None:
|
if rate is None:
|
||||||
@ -55,8 +72,13 @@ def tax_fraction_from_code(tax_code: str) -> Decimal:
|
|||||||
'iva_2': Decimal('0.02'),
|
'iva_2': Decimal('0.02'),
|
||||||
'iva_0': Decimal('0.00'),
|
'iva_0': Decimal('0.00'),
|
||||||
'iva_exenta': Decimal('0.00'),
|
'iva_exenta': Decimal('0.00'),
|
||||||
|
'rec_5_2': Decimal('0.052'),
|
||||||
|
'rec_1_75': Decimal('0.0175'),
|
||||||
|
'rec_0_62': Decimal('0.0062'),
|
||||||
|
'rec_0_5': Decimal('0.005'),
|
||||||
|
'rec_1': Decimal('0.01')
|
||||||
}
|
}
|
||||||
return mapping.get(tax_code or '', Decimal('0.13'))
|
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]:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user