account_retencion_ar/invoice.py

162 lines
5.4 KiB
Python

# This file is part of the account_retencion_ar module for Tryton.
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from decimal import Decimal
from trytond.model import fields
from trytond.pool import PoolMeta
from trytond.pyson import Eval
from trytond.exceptions import UserError
from trytond.i18n import gettext
class Invoice(metaclass=PoolMeta):
__name__ = 'account.invoice'
@fields.depends('type')
def _get_taxes(self):
taxes = super()._get_taxes()
if self.type == 'out':
taxes.update(self._get_perceptions())
return taxes
def _get_perceptions(self):
taxes = {}
if self.company.iibb_agente_percepcion:
taxes.update(self._get_perception_iibb())
return taxes
def _get_perception_iibb(self):
taxes = {}
quantize = Decimal(10) ** -Decimal(2)
factor = 1
untaxed_amount = Decimal(0)
if self.lines:
for line in self.lines:
untaxed_amount += getattr(line, 'amount', None) or 0
if not untaxed_amount:
return taxes
if untaxed_amount < 0:
untaxed_amount = abs(untaxed_amount)
factor = -1
perception_data = self._get_perception_data_iibb()
for data in perception_data.values():
tax = data['tax']
minimum_non_taxable_amount = (
tax.minimum_non_taxable_amount or Decimal(0))
if untaxed_amount < minimum_non_taxable_amount:
continue
taxable_amount = untaxed_amount - minimum_non_taxable_amount
rate = data['rate']
if not rate:
continue
computed_amount = taxable_amount * rate / Decimal(100)
computed_amount = computed_amount.quantize(quantize)
minimum_perceivable_amount = (
tax.minimum_perceivable_amount or Decimal(0))
if computed_amount < minimum_perceivable_amount:
continue
base = untaxed_amount * factor
amount = computed_amount * factor
taxline = self._compute_tax_line(amount, base, tax)
if taxline not in taxes:
taxes[taxline] = taxline
else:
taxes[taxline]['base'] += taxline['base']
taxes[taxline]['amount'] += taxline['amount']
return taxes
def _get_perception_data_iibb(self):
# Verify conditions
if not self.party.iibb_condition:
raise UserError(gettext(
'account_retencion_ar.msg_party_iibb_condition'))
if self.party.iibb_condition in ['ex', 'rs', 'na', 'cs']:
return {}
if self.party.iva_condition not in ['responsable_inscripto', 'exento']:
return {}
company_address = self.company.party.address_get('invoice')
if not company_address or not company_address.subdivision:
raise UserError(gettext(
'account_retencion_ar.msg_company_subdivision'))
company_subdivision = company_address.subdivision
res = {}
for tax in self.company.iibb_regimenes_percepcion:
ok = False
if tax.subdivision == company_subdivision:
ok = True
elif self.party.iibb_condition == 'cm':
for x in self.party.iibb_regimenes:
if x.regimen_percepcion == tax:
ok = True
if not ok:
continue
res[tax.id] = {
'tax': tax,
}
# Verify exemptions
taxes = [x for x in res.keys()]
for exemption in self.party.exemptions:
for tax_id in taxes:
reference = 'account.tax,%s' % str(tax_id)
if (str(exemption.tax) == reference and
exemption.end_date >= self.tax_date):
del res[tax_id]
# Rate and extra data
for tax_id, tax in res.items():
tax.update(self._get_perception_extra_data_iibb(tax))
return res
def _get_perception_extra_data_iibb(self, tax_data):
res = {
'rate': Decimal(0),
}
regimen = tax_data['tax']
for x in self.party.iibb_regimenes:
if x.regimen_percepcion == regimen and x.rate_percepcion:
res['rate'] = x.rate_percepcion
return res
if self.party.iibb_condition in ['in', 'cm']:
res['rate'] = regimen.rate_registered
else:
res['rate'] = regimen.rate_non_registered
return res
class InvoiceLine(metaclass=PoolMeta):
__name__ = 'account.invoice.line'
ganancias_regimen = fields.Many2One('account.retencion',
'Régimen Ganancias',
domain=[('type', '=', 'efectuada'), ('tax', '=', 'gana')],
states={'invisible': Eval('invoice_type') != 'in'},
context={'company': Eval('company', -1)}, depends={'company'})
@fields.depends('product', 'invoice', '_parent_invoice.type',
'invoice_type')
def on_change_product(self):
super().on_change_product()
if not self.product:
return
if self.invoice and self.invoice.type:
type_ = self.invoice.type
else:
type_ = self.invoice_type
if type_ != 'in':
return
self.ganancias_regimen = self.product.ganancias_regimen_used