mirror of
https://github.com/bringout/oca-ocb-l10n_americas.git
synced 2026-04-27 08:32:07 +02:00
19.0 vanilla
This commit is contained in:
parent
89c6e82fe7
commit
1b82c20a58
572 changed files with 43570 additions and 53303 deletions
8
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/__init__.py
Normal file
8
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from . import account_move
|
||||
from . import account_tax
|
||||
from . import l10n_latam_identification_type
|
||||
from . import res_company
|
||||
from . import res_partner
|
||||
from . import template_uy
|
||||
from . import l10n_latam_document_type
|
||||
49
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/account_move.py
Normal file
49
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/account_move.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models
|
||||
|
||||
# Let us match the document types to properly suggest the DN and CN documents
|
||||
# NOTE: this can be avoided if we have an extra subclassification of UY documents
|
||||
UY_DOC_SUBTYPES = [
|
||||
["0"], # not electronic
|
||||
["101", "102", "103", "201", "202", "203"], # e-ticket
|
||||
["111", "112", "113", "211", "212", "213"], # e-invoice
|
||||
["121", "122", "123", "221", "222", "223"], # e-inv-expo
|
||||
["151", "152", "153", "251", "252", "253"], # e-boleta (not implemented yet)
|
||||
]
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
def _get_starting_sequence(self):
|
||||
""" If use documents then will create a new starting sequence using the document type code prefix and the
|
||||
journal document number with a 8 padding number """
|
||||
if self.l10n_latam_use_documents and self.company_id.account_fiscal_country_id.code == "UY" and self.l10n_latam_document_type_id:
|
||||
return self._l10n_uy_get_formatted_sequence()
|
||||
return super()._get_starting_sequence()
|
||||
|
||||
def _l10n_uy_get_formatted_sequence(self, number=0):
|
||||
return "%s A%07d" % (self.l10n_latam_document_type_id.doc_code_prefix, number)
|
||||
|
||||
def _get_last_sequence_domain(self, relaxed=False):
|
||||
where_string, param = super(AccountMove, self)._get_last_sequence_domain(relaxed)
|
||||
if self.company_id.account_fiscal_country_id.code == "UY" and self.l10n_latam_use_documents:
|
||||
where_string += " AND l10n_latam_document_type_id = %(l10n_latam_document_type_id)s"
|
||||
param['l10n_latam_document_type_id'] = self.l10n_latam_document_type_id.id or 0
|
||||
return where_string, param
|
||||
|
||||
def _get_l10n_latam_documents_domain(self):
|
||||
""" If this is a reversal or debit, suggest only related subtypes """
|
||||
self.ensure_one()
|
||||
domain = super()._get_l10n_latam_documents_domain()
|
||||
if self.country_code == "UY" and (original_move := self.reversed_entry_id or self.debit_origin_id):
|
||||
matching_subtype_codes = [
|
||||
subtype for subtype in UY_DOC_SUBTYPES
|
||||
if original_move.l10n_latam_document_type_id.code in subtype
|
||||
]
|
||||
if matching_subtype_codes:
|
||||
# restrict to the codes from the subtype matching the one of the original_move (e.g. 'e-ticket')
|
||||
codes = self.env["l10n_latam.document.type"].search(domain).mapped('code')
|
||||
allowed_codes = set(codes).intersection(set(matching_subtype_codes[0]))
|
||||
domain += [("code", "in", tuple(allowed_codes))]
|
||||
return domain
|
||||
10
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/account_tax.py
Normal file
10
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/account_tax.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountTax(models.Model):
|
||||
_inherit = "account.tax"
|
||||
|
||||
l10n_uy_tax_category = fields.Selection([
|
||||
('vat', 'VAT'),
|
||||
], string="Tax Category", help="UY: Use to group the transactions in the Financial Reports required by DGI")
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import _, models
|
||||
from odoo.exceptions import UserError
|
||||
import re
|
||||
|
||||
|
||||
class L10n_LatamDocumentType(models.Model):
|
||||
_inherit = 'l10n_latam.document.type'
|
||||
|
||||
def _format_document_number(self, document_number):
|
||||
""" format and validate the document_number"""
|
||||
self.ensure_one()
|
||||
if self.country_id.code != "UY":
|
||||
return super()._format_document_number(document_number)
|
||||
|
||||
if not document_number:
|
||||
return False
|
||||
|
||||
if self.code == "0":
|
||||
return document_number
|
||||
|
||||
document_number = document_number.strip()
|
||||
number_part = re.findall(r'[\d]+', document_number)
|
||||
serie_part = re.findall(r'^[A-Za-z]+', document_number)
|
||||
if not serie_part or len(serie_part) > 1 or len(serie_part[0]) > 2 \
|
||||
or not number_part or len(number_part) > 1 or len(number_part[0]) > 7:
|
||||
raise UserError(_(
|
||||
"%(document_number)s is not a valid value for %(document_type)s.\n"
|
||||
"The document number must be entered with a maximum of 2 letters for the first part "
|
||||
"and 7 numbers for the second. The following are examples of valid document numbers:\n"
|
||||
"- XX0000001\n - YY0000123\n - A0000001",
|
||||
document_number=document_number,
|
||||
document_type=self.name,
|
||||
))
|
||||
return serie_part[0].upper() + number_part[0].zfill(7)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class L10n_LatamIdentificationType(models.Model):
|
||||
_inherit = "l10n_latam.identification.type"
|
||||
|
||||
l10n_uy_dgi_code = fields.Char('DGI Code')
|
||||
14
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/res_company.py
Normal file
14
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/res_company.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
def _localization_use_documents(self):
|
||||
""" Uruguayan localization use documents """
|
||||
self.ensure_one()
|
||||
return self.chart_template == 'uy' or self.account_fiscal_country_id.code == "UY" or super()._localization_use_documents()
|
||||
|
||||
def _is_latam(self):
|
||||
return super()._is_latam() or self.country_code == 'UY'
|
||||
101
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/res_partner.py
Normal file
101
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/res_partner.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import logging
|
||||
import re
|
||||
|
||||
from odoo import api, models, _
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
def _run_check_identification(self, validation='error'):
|
||||
"""Add validation of UY document types CI and NIE """
|
||||
if validation == 'error':
|
||||
ci_nie_types = self.filtered(lambda p:
|
||||
p.l10n_latam_identification_type_id.l10n_uy_dgi_code in ("1", "3")
|
||||
and p.l10n_latam_identification_type_id.country_id.code == "UY"
|
||||
and p.vat
|
||||
)
|
||||
for partner in ci_nie_types:
|
||||
if not partner._l10n_uy_ci_nie_is_valid():
|
||||
raise ValidationError(self._l10n_uy_build_vat_error_message(partner))
|
||||
return super()._run_check_identification(validation=validation)
|
||||
|
||||
@api.model
|
||||
def _l10n_uy_build_vat_error_message(self, partner):
|
||||
""" Similar to _build_vat_error_message but using latam doc type name instead of vat_label
|
||||
NOTE: maybe can be implemented in master to l10n_latam_base for the use of different doc types """
|
||||
vat_label = _("CI/NIE")
|
||||
expected_format = _("3:402.010-2 or 93:402.010-1 (CI or NIE)")
|
||||
|
||||
# Catch use case where the record label is about the public user (name: False)
|
||||
if partner.name:
|
||||
msg = "\n" + _(
|
||||
"The %(vat_label)s number [%(wrong_vat)s] for %(partner_label)s does not seem to be valid."
|
||||
"\nNote: the expected format is %(expected_format)s",
|
||||
vat_label=vat_label,
|
||||
wrong_vat=partner.vat,
|
||||
partner_label=_("partner [%s]", partner.name),
|
||||
expected_format=expected_format,
|
||||
)
|
||||
else:
|
||||
msg = "\n" + _(
|
||||
"The %(vat_label)s number [%(wrong_vat)s] does not seem to be valid."
|
||||
"\nNote: the expected format is %(expected_format)s",
|
||||
vat_label=vat_label,
|
||||
wrong_vat=partner.vat,
|
||||
expected_format=expected_format,
|
||||
)
|
||||
return msg
|
||||
|
||||
def _l10n_uy_ci_nie_is_valid(self):
|
||||
""" Check if the partner's CI or NIE number is a valid one.
|
||||
|
||||
CI:
|
||||
1) The ID number is taken up to the second to last position, that is, the first 6 or 7 digits.
|
||||
2) Each digit is multiplied by a different factor starting from right to left, the factors are:
|
||||
2, 9, 8, 7, 6, 3, 4.
|
||||
3) The products obtained are added:
|
||||
4) The base module 10 is calculated on this result to obtain the check digit, expressed in another way,
|
||||
the next number ending in zero is taken that follows the result of the addition (for the example
|
||||
would be 60) subtracting the sum itself: 60 - 59 = 1. The verification digit of the example ID is 1.
|
||||
|
||||
NOTE: If the ID has fewer digits, it is preceded with zeros and the mechanism described above is applied
|
||||
|
||||
NIE:
|
||||
The calculation for the NIE is the same as that used for the CI. The only difference is that we skip the
|
||||
first number
|
||||
|
||||
Both algorithms where extracted from Uruware's Technical Manual (section 9.2 and 9.3)
|
||||
|
||||
Return: False is not valid, True is valid
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
# The VAT must consist only numbers (format could have these characters ":., " we can skip them later)
|
||||
invalid_chars = re.findall(r"[^0-9:., \-]", self.vat)
|
||||
if invalid_chars:
|
||||
return False
|
||||
|
||||
ci_nie_number = re.sub("[^0-9]", "", self.vat)
|
||||
|
||||
# we get the validation digit, if NIE doc type we skip the first digit
|
||||
is_nie = self.l10n_latam_identification_type_id.l10n_uy_dgi_code == "1"
|
||||
verif_digit = int(ci_nie_number[-1])
|
||||
ci_nie_number = ci_nie_number[1:-1] if is_nie else ci_nie_number[0:-1]
|
||||
|
||||
# If number is < 7 digits we add 0 to the left
|
||||
ci_nie_number = "%07d" % int(ci_nie_number)
|
||||
|
||||
# If NIE > 7 digits is not valid
|
||||
if len(ci_nie_number) > 7:
|
||||
return False
|
||||
|
||||
verification_vector = (2, 9, 8, 7, 6, 3, 4)
|
||||
num_sum = sum(int(ci_nie_number[i]) * verification_vector[i] for i in range(7))
|
||||
|
||||
res = -num_sum % 10
|
||||
return res == verif_digit
|
||||
73
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/template_uy.py
Normal file
73
odoo-bringout-oca-ocb-l10n_uy/l10n_uy/models/template_uy.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models, _
|
||||
from odoo.addons.account.models.chart_template import template
|
||||
|
||||
|
||||
class AccountChartTemplate(models.AbstractModel):
|
||||
_inherit = 'account.chart.template'
|
||||
|
||||
@template('uy')
|
||||
def _get_uy_template_data(self):
|
||||
return {
|
||||
'property_account_receivable_id': 'uy_code_11300',
|
||||
'property_account_payable_id': 'uy_code_21100',
|
||||
'code_digits': '6',
|
||||
'name': _('Uruguayan Generic Chart of Accounts'),
|
||||
}
|
||||
|
||||
@template('uy', 'res.company')
|
||||
def _get_uy_res_company(self):
|
||||
return {
|
||||
self.env.company.id: {
|
||||
'account_fiscal_country_id': 'base.uy',
|
||||
'bank_account_code_prefix': '1111',
|
||||
'cash_account_code_prefix': '1112',
|
||||
'transfer_account_code_prefix': '11120',
|
||||
'account_default_pos_receivable_account_id': 'uy_code_11307',
|
||||
'income_currency_exchange_account_id': 'uy_code_4302',
|
||||
'expense_currency_exchange_account_id': 'uy_code_5302',
|
||||
'account_journal_early_pay_discount_loss_account_id': 'uy_code_5303',
|
||||
'account_journal_early_pay_discount_gain_account_id': 'uy_code_4303',
|
||||
'account_sale_tax_id': 'vat1',
|
||||
'account_purchase_tax_id': 'vat4',
|
||||
'deferred_expense_account_id': 'uy_code_11407',
|
||||
'deferred_revenue_account_id': 'uy_code_21321',
|
||||
'income_account_id': 'uy_code_4102',
|
||||
'expense_account_id': 'uy_code_5100',
|
||||
'account_stock_journal_id': 'inventory_valuation',
|
||||
'account_stock_valuation_id': 'uy_code_11704',
|
||||
},
|
||||
}
|
||||
|
||||
@template('uy', 'account.journal')
|
||||
def _get_uy_account_journal(self):
|
||||
return {
|
||||
'sale': {
|
||||
"name": _("Sales"),
|
||||
"code": "0001",
|
||||
"l10n_latam_use_documents": True,
|
||||
"refund_sequence": False,
|
||||
},
|
||||
'purchase': {
|
||||
"name": _("Purchases"),
|
||||
"code": "0002",
|
||||
"l10n_latam_use_documents": True,
|
||||
"refund_sequence": False,
|
||||
},
|
||||
}
|
||||
|
||||
def _load(self, template_code, company, install_demo, force_create=True):
|
||||
""" Set companies rut as the company identification type after install the chart of account,
|
||||
this one is the uruguayan vat """
|
||||
res = super()._load(template_code, company, install_demo, force_create)
|
||||
if template_code == 'uy':
|
||||
company.partner_id.l10n_latam_identification_type_id = self.env.ref('l10n_uy.it_rut')
|
||||
return res
|
||||
|
||||
@template('uy', 'account.account')
|
||||
def _get_uy_account_account(self):
|
||||
return {
|
||||
'uy_code_11704': {
|
||||
'account_stock_variation_id': 'uy_code_5401',
|
||||
},
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue