Skip to content

Commit

Permalink
[REF] ticketbai: update and fix fundamental functionality
Browse files Browse the repository at this point in the history
This consolidates the following PRs:

  - OCA#1761
  - OCA#1790
  - OCA#1794
  - OCA#1796
  - OCA#1811
  - OCA#1823
  - OCA#1826
  - OCA#1828
  - OCA#1850

[IMP] Allow credit notes for invoices created before deploying ticketbai

[IMP] Allow the user to specify that the line detail is confidential

Gipuzkoa requires that invoices include line details but at the same
time requires that this details should be obfuscated in case the are
to be considered confidential for any reason (medical data for
example).

[FIX] Update URLs for Gipuzkoa

[FIX] Require country to be set for partners, don't default on "Spanish"

[REF] Ensure that no messages are sent to IRS from cloned databases

[UPD] Update Gipuzkoa Tax Agency Rest Api response codes

Update to the latest official documentation:
https://www.gipuzkoa.eus/documents/2456431/13761128/Anexo+IV.pdf/79d4cafc-eeaf-0b73-d4a9-3eb70c68034a

[UPD] Save warning messages from IRS also when invoices get accepted

[FIX] Update signature policy so that also Bizkaia accepts it.

[IMP] Use custom signature policy for each tax agency.

[FIX] Add support for "IVA Exento Repercutido Sujeto"

[IMP] Add Batuz support.
  • Loading branch information
blaskurain committed Nov 12, 2021
1 parent 97d78ce commit 0cf8f5f
Show file tree
Hide file tree
Showing 123 changed files with 10,751 additions and 106 deletions.
2 changes: 1 addition & 1 deletion l10n_es_ticketbai/__manifest__.py
Expand Up @@ -4,7 +4,7 @@
"name": "TicketBAI - "
"declaración de todas las operaciones de venta realizadas por las personas "
"y entidades que desarrollan actividades económicas",
"version": "11.0.0.2.0",
"version": "11.0.0.3.1",
"category": "Accounting & Finance",
"website": "http://www.binovo.es",
"author": "Binovo,"
Expand Down
7 changes: 7 additions & 0 deletions l10n_es_ticketbai/data/account_fiscal_position_template.xml
Expand Up @@ -74,6 +74,13 @@
<field name="tbai_vat_regime_key" ref="tbai_vat_regime_01"/>
</record>

<!-- Tax Templates: Régimen Nacional -->
<record id="tbai_fp_nacional_s_iva0" model="account.fp.tbai.tax_template">
<field name="position_id" ref="l10n_es.fp_nacional"/>
<field name="tax_id" ref="l10n_es.account_tax_template_s_iva0"/>
<field name="tbai_vat_exemption_key" ref="tbai_vat_exemption_E1"/>
</record>

<!-- Tax Templates: Intracommunity -->
<record id="tbai_fp_intra_s_iva0_ic" model="account.fp.tbai.tax_template">
<field name="position_id" ref="l10n_es.fp_intra"/>
Expand Down
4 changes: 2 additions & 2 deletions l10n_es_ticketbai/hooks.py
Expand Up @@ -28,8 +28,8 @@ def post_init_hook(cr, registry):
if 0 < len(fiscal_position_template.tbai_vat_exemption_ids):
tbai_vat_exemptions = []
for exemption in fiscal_position_template.tbai_vat_exemption_ids:
tax = env['l10n.es.aeat.report'].\
get_taxes_from_templates(exemption.tax_id)
tax = env['l10n.es.aeat.report']\
.get_taxes_from_templates(exemption.tax_id)
if 1 == len(tax):
tbai_vat_exemptions.append((0, 0, {
'tax_id': tax.id,
Expand Down
130 changes: 129 additions & 1 deletion l10n_es_ticketbai/i18n/es.po
Expand Up @@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-08-26 10:55+0000\n"
"PO-Revision-Date: 2021-08-26 10:55+0000\n"
"PO-Revision-Date: 2021-10-13 18:45+0200\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"Language: \n"
Expand Down Expand Up @@ -647,3 +647,131 @@ msgstr "Asistente obtención llaves de certificado AEAT"
#: model:ir.model,name:l10n_es_ticketbai.model_tbai_info
msgid "tbai.info"
msgstr "TicketBAI Información general"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,field_description:l10n_es_ticketbai.field_tbai_invoice_refund_origin_account_refund_invoice_id
msgid "Account Refund Invoice"
msgstr "Factura rectificativa"

#. module: l10n_es_ticketbai
#: model:ir.model,name:l10n_es_ticketbai.model_tbai_invoice_refund_origin
msgid "TicketBAI Refunded Invoices Origin Invoice Data"
msgstr "Datos de facturas origen para la factura rectificativa TicketBAI"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,field_description:l10n_es_ticketbai.field_account_invoice_tbai_refund_origin_ids
msgid "TicketBAI Refund Origin References"
msgstr "TicketBAI rectificativas referencias de origen"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,help:l10n_es_ticketbai.field_tbai_invoice_refund_origin_expedition_date
msgid "Expected string date format: %dd-%mm-%yyyy"
msgstr "Formato de fecha esperado: %dd-%mm-%yyyy"

#. module: l10n_es_ticketbai
#: code:addons/l10n_es_ticketbai/models/ticketbai_invoice.py:110
#, python-format
msgid "Invoice: number %s prefix %s invoice_date %s exists. Create a credit note from this invoice."
msgstr "La factura con número %s , prefijo %s y fecha %s existe. Crea una rectificativa partiendo de esa factura."

#. module: l10n_es_ticketbai
#: model:ir.ui.view,arch_db:l10n_es_ticketbai.invoice_form_inherit
msgid "Key/Type"
msgstr "Clave/Tipo"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,field_description:l10n_es_ticketbai.field_tbai_invoice_refund_origin_number
msgid "Number"
msgstr "Número"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,field_description:l10n_es_ticketbai.field_tbai_invoice_refund_origin_number_prefix
msgid "Number Prefix"
msgstr "Serie"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,help:l10n_es_ticketbai.field_tbai_invoice_refund_origin_number_prefix
msgid "Number prefix of this invoice name e.g. if the invoice name is INV/2021/00001 then the prefix is INV/2021/, ending back slash included"
msgstr "Prefijo de factura hasta el número p.e. si la factura es FV/2021/00001 entonces el prefijo es FV/2021/, incluyendo el caracter separador final"

#. module: l10n_es_ticketbai
#: code:addons/l10n_es_ticketbai/models/ticketbai_invoice.py:89
#, python-format
msgid "Refunded Invoice %s Expedition Date"
msgstr "Factura rectificativa %s fecha"

#. module: l10n_es_ticketbai
#: code:addons/l10n_es_ticketbai/models/ticketbai_invoice.py:80
#, python-format
msgid "Refunded Invoice %s Number Prefix %s longer than expected. Should be 20 characters max.!"
msgstr "Factura rectificativa %s. Prefijo %s más largo de lo esperado. Debería de contener 20 caracteres como máximo."

#. module: l10n_es_ticketbai
#: code:addons/l10n_es_ticketbai/models/ticketbai_invoice.py:70
#, python-format
msgid "Refunded Invoice Number %s longer than expected. Should be 20 characters max.!"
msgstr "Factura rectificativa número %s más largo de lo esperado. Debería de contener 20 caracteres como máximo."

#. module: l10n_es_ticketbai
#: code:addons/l10n_es_ticketbai/models/account_invoice.py:331
#, python-format
msgid "Some of the original invoices have related tbai cancelled invoices"
msgstr "Algunas de las facturas origen tienen facturas TicketBAI de anulación relacionadas"

#. module: l10n_es_ticketbai
#: code:addons/l10n_es_ticketbai/models/account_invoice.py:327
#, python-format
msgid "Some of the original invoices have related tbai invoices in inconsistent state please fix them beforehand."
msgstr "Algunas de las facturas origen tienen facturas TicketBAI relacionadas en estado incosistente, por favor corrija primero esos errores."

#. module: l10n_es_ticketbai
#: model:ir.model.fields,help:l10n_es_ticketbai.field_tbai_invoice_refund_origin_number
msgid "The number of the invoice name e.g. if the invoice name is INV/2021/00001 then the number is 00001"
msgstr "El número de la factura p.e. si la actura es FV/2021/00001 entonces el número es 00001"

#. module: l10n_es_ticketbai
#: model:ir.ui.view,arch_db:l10n_es_ticketbai.invoice_form_inherit
msgid "Origin Invoices Data"
msgstr "Datos facturas origen"

#. module: l10n_es_ticketbai
#: model:ir.ui.view,arch_db:l10n_es_ticketbai.invoice_form_inherit
msgid "Invoice Serial"
msgstr "Número de serie"

#. module: l10n_es_ticketbai
#: model:ir.ui.view,arch_db:l10n_es_ticketbai.invoice_form_inherit
msgid "Invoice Number"
msgstr "Número de factura"

#. module: l10n_es_ticketbai
#: model:ir.ui.view,arch_db:l10n_es_ticketbai.invoice_form_inherit
msgid "Invoice Date"
msgstr "Fecha de la factura"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,field_description:l10n_es_ticketbai.field_tbai_invoice_refund_origin_expedition_date
msgid "Expedition Date"
msgstr "Fecha de la factura"

#. module: l10n_es_ticketbai
#: code:addons/l10n_es_ticketbai/models/account_invoice.py:326
#, python-format
msgid "Please, specify data for the original invoices that are going to be refunded"
msgstr "Por favor, introduzca la información de las facturas originales a rectificar."

#. module: l10n_es_ticketbai
#: code:addons/l10n_es_ticketbai/models/res_company.py:15
#, python-format
msgid "Information protected by Article 9 Regulation 679/2016"
msgstr "Información protegida por el artículo 9 Reglamento 679/2016"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,field_description:l10n_es_ticketbai.field_res_company_tbai_protected_data
msgid "Protected Data"
msgstr "Datos protegidos"

#. module: l10n_es_ticketbai
#: model:ir.model.fields,field_description:l10n_es_ticketbai.field_res_company_tbai_protected_data_txt
msgid "Substitution Text"
msgstr "Texto de substitución"
28 changes: 28 additions & 0 deletions l10n_es_ticketbai/migrations/11.0.0.3.1/post-migrate.py
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# © 2021 Binovo IT Human Project SL
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import odoo


def migrate(cr, version):
if not version:
return
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
fp_nacional = env.ref('l10n_es.1_fp_nacional')
fp_nacional_tmpl = env.ref('l10n_es.fp_nacional')
if fp_nacional_tmpl.tbai_vat_exemption_ids:
vals = {}
tbai_vat_exemptions = []
for fp_tmpl_exemption in fp_nacional_tmpl.tbai_vat_exemption_ids:
fp_tmpl_tax = env['l10n.es.aeat.report'].get_taxes_from_templates(fp_tmpl_exemption.tax_id)
if 1 == len(fp_tmpl_tax):
exemption_found = fp_nacional.tbai_vat_exemption_ids.filtered(lambda ex: ex.tax_id.id == fp_tmpl_tax.id)
if not exemption_found:
tbai_vat_exemptions.append((0, 0,
{'tax_id': fp_tmpl_tax.id,
'tbai_vat_exemption_key': fp_tmpl_exemption.tbai_vat_exemption_key.id})
)
if tbai_vat_exemptions:
vals['tbai_vat_exemption_ids'] = tbai_vat_exemptions
fp_nacional.write(vals)
114 changes: 84 additions & 30 deletions l10n_es_ticketbai/models/account_invoice.py
Expand Up @@ -53,6 +53,10 @@ class AccountInvoice(models.Model):
comodel_name='tbai.vat.regime.key', string='VAT Regime 2nd Key', copy=True)
tbai_vat_regime_key3 = fields.Many2one(
comodel_name='tbai.vat.regime.key', string='VAT Regime 3rd Key', copy=True)
tbai_refund_origin_ids = fields.One2many(
comodel_name='tbai.invoice.refund.origin',
inverse_name='account_refund_invoice_id',
string='TicketBAI Refund Origin References')

@api.multi
@api.constrains('state')
Expand All @@ -77,13 +81,15 @@ def create(self, vals):
if vals and vals.get('company_id', False):
company = self.env['res.company'].browse(vals['company_id'])
if company.tbai_enabled:
filter_refund = self._context.get('filter_refund', False)
invoice_type = vals.get('type', False)
filter_refund = self._context.get('filter_refund', False) \
or self._context.get('type', False) == 'out_refund'
invoice_type = vals.get('type', False) \
or self._context.get('type', False)
if filter_refund and invoice_type:
if 'out_refund' == vals['type']:
if 'tbai_refund_type' not in vals:
if 'out_refund' == invoice_type:
if not vals.get('tbai_refund_type', False):
vals['tbai_refund_type'] = RefundType.differences.value
if 'tbai_refund_key' not in vals:
if not vals.get('tbai_refund_key', False):
vals['tbai_refund_key'] = RefundCode.R1.value
if vals.get('fiscal_position_id', False):
fiscal_position = self.env['account.fiscal.position'].browse(
Expand Down Expand Up @@ -146,6 +152,38 @@ def onchange_tbai_refund_type(self):
}

def tbai_prepare_invoice_values(self):

def tbai_prepare_refund_values():
refunded_inv = self.refund_invoice_id
if refunded_inv:
vals.update({
'is_invoice_refund': True,
'refund_code': self.tbai_refund_key,
'refund_type': self.tbai_refund_type,
'tbai_invoice_refund_ids': [(0, 0, {
'number_prefix': refunded_inv.tbai_get_value_serie_factura(),
'number': refunded_inv.tbai_get_value_num_factura(),
'expedition_date':
refunded_inv.tbai_get_value_fecha_expedicion_factura()
})]
})
else:
if self.tbai_refund_origin_ids:
refund_id_dicts = []
for refund_origin_id in self.tbai_refund_origin_ids:
refund_id_dicts.append(
(0, 0, {
'number_prefix': refund_origin_id.number_prefix,
'number': refund_origin_id.number,
'expedition_date': refund_origin_id.expedition_date
}))
vals.update({
'is_invoice_refund': True,
'refund_code': self.tbai_refund_key,
'refund_type': self.tbai_refund_type,
'tbai_invoice_refund_ids': refund_id_dicts
})

self.ensure_one()
partner = self.partner_id
vals = {
Expand Down Expand Up @@ -180,19 +218,7 @@ def tbai_prepare_invoice_values(self):
vals['tax_retention_amount_total'] = retencion_soportada
if self.tbai_is_invoice_refund() and \
RefundType.differences.value == self.tbai_refund_type:
# You may inherit this method to support substitution credit notes (refunds)
refunded_invoice = self.refund_invoice_id
vals.update({
'is_invoice_refund': True,
'refund_code': self.tbai_refund_key,
'refund_type': self.tbai_refund_type,
'tbai_invoice_refund_ids': [(0, 0, {
'number_prefix': refunded_invoice.tbai_get_value_serie_factura(),
'number': refunded_invoice.tbai_get_value_num_factura(),
'expedition_date':
refunded_invoice.tbai_get_value_fecha_expedicion_factura()
})]
})
tbai_prepare_refund_values()
operation_date = self.tbai_get_value_fecha_operacion()
if operation_date:
vals['operation_date'] = operation_date
Expand All @@ -202,8 +228,12 @@ def tbai_prepare_invoice_values(self):
if tax_agency == gipuzkoa_tax_agency:
lines = []
for line in self.invoice_line_ids:
description_line = line.name[:250]
if self.company_id.tbai_protected_data \
and self.company_id.tbai_protected_data_txt:
description_line = self.company_id.tbai_protected_data_txt[:250]
lines.append((0, 0, {
'description': line.name[:250],
'description': description_line,
'quantity': line.tbai_get_value_cantidad(),
'price_unit': "%.8f" % line.price_unit,
'discount_amount': line.tbai_get_value_descuento(),
Expand Down Expand Up @@ -299,6 +329,32 @@ def action_cancel(self):

@api.multi
def invoice_validate(self):

def validate_refund_invoices():
for invoice in refund_invoices:
valid_refund = True
error_refund_msg = None
if not invoice.refund_invoice_id and not invoice.tbai_refund_origin_ids:
valid_refund = False
error_refund_msg = _("Please, specify data for the original"
" invoices that are going to be refunded")
if invoice.refund_invoice_id.tbai_invoice_id:
valid_refund = invoice.refund_invoice_id.tbai_invoice_id.state in \
[
TicketBaiInvoiceState.pending.value,
TicketBaiInvoiceState.sent.value
]
if not valid_refund:
error_refund_msg = _(
"Some of the original invoices have related tbai invoices "
"in inconsistent state please fix them beforehand.")
if valid_refund and invoice.refund_invoice_id.tbai_cancellation_id:
valid_refund = False
error_refund_msg = _("Some of the original invoices "
"have related tbai cancelled invoices")
if not valid_refund:
raise exceptions.ValidationError(error_refund_msg)

res = super().invoice_validate()
# Credit Notes:
# A. refund: creates a draft credit note, not validated from wizard.
Expand All @@ -307,17 +363,15 @@ def invoice_validate(self):
# * The draft invoice won't be a credit note 'by substitution',
# but a normal customer invoice.
# There is no 'by substitution' credit note, only 'by differences'.
tbai_invoices = self.sudo().filtered(
lambda x: x.tbai_enabled and (
'out_invoice' == x.type or
(
x.refund_invoice_id and 'out_refund' == x.type and
x.tbai_refund_type == RefundType.differences.value and
x.refund_invoice_id.tbai_invoice_id and not
x.refund_invoice_id.tbai_cancellation_id
)
)
)
tbai_invoices = self.sudo().env['account.invoice']
tbai_invoices |= self.sudo().filtered(
lambda x: x.tbai_enabled and 'out_invoice' == x.type)
refund_invoices = self.sudo().filtered(
lambda x: x.tbai_enabled and 'out_refund' == x.type and
x.tbai_refund_type == RefundType.differences.value)

validate_refund_invoices()
tbai_invoices |= refund_invoices
tbai_invoices._tbai_build_invoice()
return res

Expand Down
4 changes: 2 additions & 2 deletions l10n_es_ticketbai/models/chart_template.py
Expand Up @@ -57,8 +57,8 @@ def create_record_with_xmlid(self, company, template, model, vals):
fiscal_position = self.env['account.fiscal.position'].browse(res_id)
tbai_vat_exemptions = []
for exemption in template.tbai_vat_exemption_ids:
tax = self.env['l10n.es.aeat.report'].\
get_taxes_from_templates(exemption.tax_id)
Report = self.env['l10n.es.aeat.report']
tax = Report.get_taxes_from_templates(exemption.tax_id)
if 1 == len(tax):
tbai_vat_exemptions.append((0, 0, {
'tax_id': tax.id,
Expand Down
5 changes: 5 additions & 0 deletions l10n_es_ticketbai/models/res_company.py
Expand Up @@ -10,6 +10,11 @@ class ResCompany(models.Model):
tbai_aeat_certificate_id = fields.Many2one(
comodel_name='l10n.es.aeat.certificate', string='AEAT Certificate',
domain="[('state', '=', 'active'), ('company_id', '=', id)]", copy=False)
tbai_protected_data = fields.Boolean('Protected Data', default=False)
tbai_protected_data_txt = fields.Text(
"Substitution Text",
translate=True,
default='Information protected by Article 9 Regulation 679/2016')

@api.onchange('tbai_enabled')
def onchange_tbai_enabled_unset_tbai_aeat_certificate_id(self):
Expand Down

0 comments on commit 0cf8f5f

Please sign in to comment.