...
 
Commits (12)
<a name="8.0.3.6.6"></a>
## 8.0.3.6.6 (2024-02-12)
#### Bug Fixes
* **account.invoice:** corrige la diferencia en montos cuando el cliente no es sujeto de IEPS ([608fd005](608fd005))
<a name="8.0.3.6.5"></a>
## 8.0.3.6.5 (2024-01-17)
#### Features
* **account_voucher.txt:** ([526d75a9](526d75a9))
<a name="8.0.3.6.4"></a>
## 8.0.3.6.4 (2023-10-27)
#### Bug Fixes
* **account.invoice:** se corrige la función action_consult_cancellation_status ([59776618](59776618))
<a name="8.0.3.6.3"></a>
## 8.0.3.6.3 (2023-10-11)
#### Bug Fixes
* **account.invoice:** se corrige migración para soporta el cambio del campo cfdi_fiscal_regime_id ([78393b60](78393b60))
<a name="8.0.3.6.2"></a>
## 8.0.3.6.2 (2023-09-12)
......
......@@ -7,48 +7,50 @@ from openerp import SUPERUSER_ID
@tools.migrate(use_env=True, uid=SUPERUSER_ID)
def migrate(env, installed_version):
tools.logged_query(
env.cr,
"""
WITH account_regimen_code as (
SELECT
ai.id AS invoice_id,
fr.id AS fiscal_regime_id
FROM account_invoice as ai
LEFT JOIN account_fiscal_position as afp ON afp.id = ai.fiscal_position
LEFT JOIN cfdi_fiscal_regime AS fr ON afp.code = fr.code
WHERE afp.code in (
'601','603','605','606','607','608','609','610','611','612','614',
'615','616','620','621','622','623','624','628','629','630'
legacy_name = tools.get_legacy_name("cfdi_fiscal_regime_id")
if tools.column_exists(env.cr, "account_invoice", legacy_name):
tools.logged_query(
env.cr,
"""
WITH account_regimen_code as (
SELECT
ai.id AS invoice_id,
fr.id AS fiscal_regime_id
FROM account_invoice as ai
LEFT JOIN account_fiscal_position as afp ON afp.id = ai.fiscal_position
LEFT JOIN cfdi_fiscal_regime AS fr ON afp.code = fr.code
WHERE afp.code in (
'601','603','605','606','607','608','609','610','611','612','614',
'615','616','620','621','622','623','624','628','629','630'
)
)
)
UPDATE account_invoice AS ai
SET cfdi_fiscal_regime_id=fr.fiscal_regime_id
FROM account_regimen_code AS fr
WHERE ai.id=fr.invoice_id
""",
)
UPDATE account_invoice AS ai
SET cfdi_fiscal_regime_id=fr.fiscal_regime_id
FROM account_regimen_code AS fr
WHERE ai.id=fr.invoice_id
""",
)
tools.logged_query(
env.cr,
"""
WITH account_regimen_code as (
SELECT
ai.id AS invoice_id,
fr.id AS fiscal_regime_id
FROM account_invoice as ai
LEFT JOIN account_fiscal_position as afp ON afp.id = ai.fiscal_position
LEFT JOIN cfdi_fiscal_regime AS fr ON afp.code = fr.code
WHERE afp.code in (
'601','603','605','606','607','608','609','610','611','612','614',
'615','616','620','621','622','623','624','628','629','630'
tools.logged_query(
env.cr,
"""
WITH account_regimen_code as (
SELECT
ai.id AS invoice_id,
fr.id AS fiscal_regime_id
FROM account_invoice as ai
LEFT JOIN account_fiscal_position as afp ON afp.id = ai.fiscal_position
LEFT JOIN cfdi_fiscal_regime AS fr ON afp.code = fr.code
WHERE afp.code in (
'601','603','605','606','607','608','609','610','611','612','614',
'615','616','620','621','622','623','624','628','629','630'
)
)
)
UPDATE account_invoice AS ai
SET fiscal_position=Null
FROM account_regimen_code AS fr
WHERE ai.id=fr.invoice_id
""",
)
UPDATE account_invoice AS ai
SET fiscal_position=Null
FROM account_regimen_code AS fr
WHERE ai.id=fr.invoice_id
""",
)
......@@ -274,8 +274,6 @@ class AccountInvoice(models.Model):
for invoice in self.filtered(lambda i: i.type in ("out_invoice", "out_refund")):
partner = invoice.partner_id
if partner:
if partner.cfdi_fiscal_regime_id.id:
invoice.cfdi_fiscal_regime_id = partner.cfdi_fiscal_regime_id.id
if partner.cfdi_use.id:
invoice.cfdi_use = partner.cfdi_use.id
invoice.payment_method_id = partner.payment_method_id.id
......@@ -365,7 +363,7 @@ class AccountInvoice(models.Model):
for invoice in self:
try:
with self.env.cr.savepoint():
status_cancelacion = invoice.consult_cfdi_cancellation_status()
status_cancelacion = invoice.consult_cfdi_cancellation_status()[0]
if status_cancelacion is None:
invoice.message_post(
body=_("No status update found on SAT")
......@@ -402,7 +400,6 @@ class AccountInvoice(models.Model):
[
"cfdi_use",
"partner_id",
"cfdi_fiscal_regime_id",
"payment_type_ids",
"address_issued_id",
"journal_id",
......@@ -582,9 +579,6 @@ class AccountInvoiceLine(models.Model):
"""
tax_record = self.env["account.tax"].browse(tax["id"])
tax_group = tax_record.tax_category_id
# IEPS tax only must be included when partner is IEPS subjected
if tax_group.name == "IEPS" and not partner.ieps_subjected:
return
# TODO: Delete on version 3.0.0
if "base" not in tax:
tax["base"] = currency.cfdi_round(tax["price_unit"] * self.quantity)
......@@ -600,16 +594,7 @@ class AccountInvoiceLine(models.Model):
total_discount *= 1 - self.invoice_id.global_discount / 100
price = float_round(self.price_unit * total_discount, precision)
partner = self.invoice_id.partner_id
# Check if IEPS is on taxes, this will be used later to know if need price
# to be recalculated because IEPS must be price included as partner is not
# IEPS subjected and product include IEPS taxes
ieps_group = self.env.ref("l10n_mx.tax_category_ieps")
is_ieps_tax_subjected = any(
tax.tax_category_id == ieps_group for tax in self.invoice_line_tax_id
)
is_price_included = any(
tax.price_include for tax in self.invoice_line_tax_id
)
# Compute taxes using original compute_all function from
# account.invoice.tax to get same result for CFDI display
res = self.invoice_line_tax_id.compute_all(
......@@ -619,36 +604,19 @@ class AccountInvoiceLine(models.Model):
partner=partner,
currency=self.invoice_id.currency_id,
)
# pylint: disable=C1801
if len(res["taxes"]) == 0:
if not len(res["taxes"]):
raise ValidationError(
_("Product {p} must have at least one tax selected.").format(
p=self.product_id.name
)
)
taxes = []
taxes_list = iter(res["taxes"])
tax = next(taxes_list)
# Iterate taxes and append to the new tax list as needed
while True:
tax = process_tax(tax)
if tax:
taxes.append(tax)
try:
tax = next(taxes_list)
except StopIteration:
if tax is None:
raise ValidationError(
_(
"Incorrect tax sequence configuration, check "
"this data in Account >> Tax >> Sequence"
)
)
break
is_price_included = any(
tax.price_include for tax in self.invoice_line_tax_id
)
res["price_unit"] = self.price_unit
# Recompute price_unit is needed when any tax is setup to price included or
# when product is IEPS subjected but not partner
if is_price_included or (not partner.ieps_subjected and is_ieps_tax_subjected):
# Recompute price_unit is needed when any tax is setup to price included
if is_price_included:
# Send round=False in context to avoid rounding to wrong value when working
# with high Product Price precision (6 digits)
res["price_unit"] = self.invoice_line_tax_id.with_context(
......@@ -664,6 +632,25 @@ class AccountInvoiceLine(models.Model):
self.env["decimal.precision"].precision_get("Product Price"),
)
taxes = []
taxes_list = iter(res["taxes"])
tax = next(taxes_list)
# Iterate taxes and append to the new tax list as needed
while True:
tax = process_tax(tax)
# IEPS tax only must be included when partner is IEPS subjected
if tax["group"] == "IEPS" and not partner.ieps_subjected:
res["price_unit"] = float_round(
res["price_unit"] + (tax["amount"] / self.quantity),
self.env["decimal.precision"].precision_get("Product Price"),
)
else:
taxes.append(tax)
try:
tax = next(taxes_list)
except StopIteration:
break
res["importe"] = currency.round(res["price_unit"] * self.quantity)
res["descuento"] = currency.round(res["importe"] * (1 - total_discount))
# Overrides original taxes with the list computed by us
......
......@@ -156,7 +156,7 @@
IdDocumento="${ invoice.cfdi_folio_fiscal }"
MonedaDR="${ invoice.currency_id.name }"
{% if invoice.currency_id == o.currency_id %}
EquivalenciaDR="1"
EquivalenciaDR="1.0000000000"
{% end %}
{% if invoice.currency_id != o.currency_id %}
EquivalenciaDR="${ o.tipocambiodr(invoice) }"
......
......@@ -31,3 +31,15 @@ Característica: Cancelar facturas con CFDIS no timbrados.
Cuando se cancele la Factura
Entonces la factura cambia a Cancelada
Y el CFDI cambia a Cancelado
Característica: Validación de XML y PDF en el total de factura, importe y precio unitario del producto
Esquema del escenario:
Dado una factura con la siguiente información
El cliente no está sujeto a IEPS
| Concepto | Precio Unitario | Cantidad | Impuesto |
| ProductoA | 308.3554| 5.0 | IEPS 30% |
| | | | IVA 16% |
Cuando: Valido la factura.
Entonces: la factura cambia a Abierta
Y el total de la factura, el importe y precio unitario del producto es correcto en el XML