19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:16 +01:00
parent 89c6e82fe7
commit 1b82c20a58
572 changed files with 43570 additions and 53303 deletions

View file

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import template_ec
from . import res_partner
from . import l10n_ec_sri_payment
from . import l10n_latam_document_type
@ -9,4 +8,3 @@ from . import account_tax
from . import account_tax_group
from . import res_company
from . import account_journal
from . import account_chart_template

View file

@ -1,76 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class AccountChartTemplate(models.Model):
_inherit = "account.chart.template"
def _get_account_from_template(self, companies, template):
if template:
return self.env['account.account'].search([('company_id', 'in', companies.ids), ('code', '=', template.code)])
return self.env['account.account']
def _load(self, company):
# EXTENDS account to setup taxes groups accounts configuration
res = super()._load(company)
self._l10n_ec_configure_ecuadorian_tax_groups_accounts(company)
return res
def _l10n_ec_configure_ecuadorian_tax_groups_accounts(self, companies):
'''
Set tax groups accounts for automatic closing entry in 103 and 104 reports
The structure of the variable with the list of accounts by tax group:
('<tax_group_record_id>', '<payable_account_code>', '<receivable_account_code>')
'''
_TAX_GROUPS_ACCOUNTS_LIST = [
('tax_group_vat_05', '21070102', '11050202'),
('tax_group_vat_08', '21070102', '11050202'),
('tax_group_vat_12', '21070102', '11050202'),
('tax_group_vat_13', '21070102', '11050202'),
('tax_group_vat14', '21070102', '11050202'),
('tax_group_vat_15', '21070102', '11050202'),
('tax_group_vat0', '21070102', '11050202'),
('tax_group_vat_not_charged', '21070102', '11050202'),
('tax_group_vat_exempt', '21070102', '11050202'),
('tax_group_ice', '21070104', '21070104'),
('tax_group_irbpnr', '21070105', '21070105'),
('tax_group_withhold_vat_sale', '21070102', '11050203'),
('tax_group_withhold_vat_purchase', '21070102', '11050203'),
('tax_group_withhold_income_sale', '21070103', '11050201'),
('tax_group_withhold_income_purchase', '21070103', '11050201'),
('tax_group_outflows', '21070106', '11050205'),
('tax_group_others', '21070106', '11050205'),
]
for tax_group_xml_id, payable_account_code, receivable_account_code in _TAX_GROUPS_ACCOUNTS_LIST:
for company in companies.filtered(lambda company: company.account_fiscal_country_id.code == 'EC' and
company.chart_template_id == self.env.ref('l10n_ec.l10n_ec_ifrs')):
# search accounts
AccountObject = self.env['account.account']
company_domain = [('company_id', '=', company.id)]
payable_account_id = AccountObject.search([('code', '=', payable_account_code)] + company_domain)
receivable_account_id = AccountObject.search([('code', '=', receivable_account_code)] + company_domain)
# set accounts in tax groups by company
self.env.ref(f'l10n_ec.{tax_group_xml_id}').with_company(company).property_tax_payable_account_id = payable_account_id
self.env.ref(f'l10n_ec.{tax_group_xml_id}').with_company(company).property_tax_receivable_account_id = receivable_account_id
def _prepare_all_journals(self, acc_template_ref, company, journals_dict=None):
res = super()._prepare_all_journals(acc_template_ref, company, journals_dict=journals_dict)
if company.account_fiscal_country_id.code == 'EC':
for journal_values in res:
if journal_values.get('type') == 'sale':
journal_values.update({
'name': f"001-001 {journal_values['name']}",
'l10n_ec_entity': '001',
'l10n_ec_emission': '001',
'l10n_ec_emission_address_id': company.partner_id.id,
})
sale_account = acc_template_ref.get(self.env.ref('l10n_ec.ec410101', raise_if_not_found=False))
if sale_account:
journal_values['default_account_id'] = sale_account.id
if journal_values.get('type') == 'purchase':
purchase_account = acc_template_ref.get(self.env.ref('l10n_ec.ec52022816', raise_if_not_found=False))
if purchase_account:
journal_values['default_account_id'] = purchase_account.id
return res

View file

@ -9,12 +9,22 @@ class AccountJournal(models.Model):
compute='_compute_l10n_ec_require_emission',
help='True if an entity and emission point must be set on the journal'
)
l10n_ec_entity = fields.Char(string="Emission Entity", size=3, copy=False)
l10n_ec_emission = fields.Char(string="Emission Point", size=3, copy=False)
l10n_ec_entity = fields.Char(
string="Emission Entity",
size=3,
copy=False,
help="Ecuador: Emission entity number that is given by the SRI."
)
l10n_ec_emission = fields.Char(
string="Emission Point",
size=3, copy=False,
help="Ecuador: Emission point number that is given by the SRI."
)
l10n_ec_emission_address_id = fields.Many2one(
comodel_name="res.partner",
string="Emission address",
domain="['|', ('id', '=', company_partner_id), '&', ('id', 'child_of', company_partner_id), ('type', '!=', 'contact')]",
help="Ecuador: Address for electronic invoicing.",
)
@api.depends('type', 'country_code', 'l10n_latam_use_documents')
@ -22,13 +32,3 @@ class AccountJournal(models.Model):
for journal in self:
journal.l10n_ec_require_emission = journal.type == 'sale' and journal.country_code == 'EC' and journal.l10n_latam_use_documents
# NOTE: Removed in master as it has no use
l10n_ec_emission_type = fields.Selection(
string="Emission type",
selection=[
("pre_printed", "Pre Printed"),
("auto_printer", "Auto Printer"),
("electronic", "Electronic"),
],
default="electronic",
)

View file

@ -133,12 +133,10 @@ class AccountMove(models.Model):
l10n_ec_sri_payment_id = fields.Many2one(
comodel_name="l10n_ec.sri.payment",
string="Payment Method (SRI)",
help="Ecuador: Payment Methods Defined by the SRI.",
default=lambda self: self.env['l10n_ec.sri.payment'].search([], limit=1),
)
# NOTE: For backward compatibility, removed in master
def _get_l10n_ec_identification_type(self):
return PartnerIdTypeEc.get_ats_code_for_partner(self.partner_id, self.move_type)
@api.model
def _get_l10n_ec_documents_allowed(self, identification_code):
documents_allowed = self.env['l10n_latam.document.type']
@ -151,7 +149,7 @@ class AccountMove(models.Model):
def _get_l10n_latam_documents_domain(self):
self.ensure_one()
domain = super()._get_l10n_latam_documents_domain()
if self.country_code == 'EC' and self.journal_id.l10n_latam_use_documents:
if self.country_code == 'EC' and self.l10n_latam_use_documents:
if self.debit_origin_id: # show/hide the debit note document type
domain.extend([('internal_type', '=', 'debit_note')])
elif self.move_type in ('out_invoice', 'in_invoice'):
@ -172,7 +170,7 @@ class AccountMove(models.Model):
"""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.journal_id.l10n_latam_use_documents
self.l10n_latam_use_documents
and self.company_id.country_id.code == "EC"
):
if self.l10n_latam_document_type_id:

View file

@ -1,53 +1,21 @@
# -*- coding: utf-8 -*-
# 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_ec_code_base = fields.Char(
string="Code base",
help="Tax declaration code of the base amount prior to the calculation of the tax",
)
l10n_ec_code_applied = fields.Char(
string="Code applied",
help="Tax declaration code of the resulting amount after the calculation of the tax",
)
l10n_ec_code_ats = fields.Char(
string="Code ATS",
help="Tax Identification Code for the Simplified Transactional Annex",
)
class AccountTaxTemplate(models.Model):
_inherit = "account.tax.template"
def _get_tax_vals(self, company, tax_template_to_tax):
vals = super(AccountTaxTemplate, self)._get_tax_vals(
company, tax_template_to_tax
)
vals.update(
{
"l10n_ec_code_base": self.l10n_ec_code_base,
"l10n_ec_code_applied": self.l10n_ec_code_applied,
"l10n_ec_code_ats": self.l10n_ec_code_ats,
}
)
return vals
l10n_ec_code_base = fields.Char(
string="Code base",
help="Tax declaration code of the base amount prior to the calculation of the tax",
)
l10n_ec_code_applied = fields.Char(
string="Code applied",
help="Tax declaration code of the resulting amount after the calculation of the tax",
)
l10n_ec_code_ats = fields.Char(
string="Code ATS",
help="Tax Identification Code for the Simplified Transactional Annex",
)
# -*- coding: utf-8 -*-
# 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_ec_code_base = fields.Char(
string="Code base",
help="Ecuador: Tax declaration code of the base amount prior to the calculation of the tax.",
)
l10n_ec_code_applied = fields.Char(
string="Code applied",
help="Ecuador: Tax declaration code of the resulting amount after the calculation of the tax.",
)
l10n_ec_code_ats = fields.Char(
string="Code ATS",
help="Ecuador: Indicates if the purchase invoice supports tax credit or cost or expenses, conforming table 5 of ATS.",
)

View file

@ -1,34 +1,32 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
_TYPE_EC = [
("vat05", "VAT 5%"),
("vat08", "VAT 8%"),
("vat12", "VAT 12%"),
("vat13", "VAT 13%"),
("vat14", "VAT 14%"),
("vat15", "VAT 15%"),
("zero_vat", "VAT 0%"),
("not_charged_vat", "VAT Not Charged"),
("exempt_vat", "VAT Exempt"),
("ice", "Special Consumptions Tax (ICE)"),
("irbpnr", "Plastic Bottles (IRBPNR)"),
("withhold_vat_sale", "VAT Withhold on Sales"),
("withhold_vat_purchase", "VAT Withhold on Purchases"),
("withhold_income_sale", "Profit Withhold on Sales"),
("withhold_income_purchase", "Profit Withhold on Purchases"),
("outflows_tax", "Exchange Outflows"),
("other", "Others"),
("withhold_vat", "VAT Withhold (Deprecated)"), # removed in master
("withhold_income_tax", "Profit Withhold (Deprecated)"), # removed in master
]
class AccountTaxGroup(models.Model):
_inherit = "account.tax.group"
l10n_ec_type = fields.Selection(
_TYPE_EC, string="Type Ecuadorian Tax", help="Ecuadorian taxes subtype"
)
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
_TYPE_EC = [
("vat05", "VAT 5%"),
("vat08", "VAT 8%"),
("vat12", "VAT 12%"),
("vat13", "VAT 13%"),
("vat14", "VAT 14%"),
("vat15", "VAT 15%"),
("zero_vat", "VAT 0%"),
("not_charged_vat", "VAT Not Charged"),
("exempt_vat", "VAT Exempt"),
("ice", "Special Consumptions Tax (ICE)"),
("irbpnr", "Plastic Bottles (IRBPNR)"),
("withhold_vat_sale", "VAT Withhold on Sales"),
("withhold_vat_purchase", "VAT Withhold on Purchases"),
("withhold_income_sale", "Profit Withhold on Sales"),
("withhold_income_purchase", "Profit Withhold on Purchases"),
("outflows_tax", "Exchange Outflows"),
("other", "Others"),
]
class AccountTaxGroup(models.Model):
_inherit = "account.tax.group"
l10n_ec_type = fields.Selection(
_TYPE_EC, string="Type Ecuadorian Tax", help="Ecuadorian taxes subtype"
)

View file

@ -4,10 +4,13 @@
from odoo import fields, models
class SriPayment(models.Model):
class L10n_EcSriPayment(models.Model):
_name = 'l10n_ec.sri.payment'
_name = "l10n_ec.sri.payment"
_description = "SRI Payment Method"
_order = "sequence, id"
name = fields.Char("Name")
sequence = fields.Integer("Sequence", default=10)
name = fields.Char("Name", translate=True)
code = fields.Char("Code")
active = fields.Boolean("Active", default=True)

View file

@ -5,7 +5,7 @@ from odoo.exceptions import UserError
import re
class L10nLatamDocumentType(models.Model):
class L10n_LatamDocumentType(models.Model):
_inherit = "l10n_latam.document.type"
internal_type = fields.Selection(
@ -32,9 +32,9 @@ class L10nLatamDocumentType(models.Model):
# Fill each number group with zeroes (3, 3 and 9 respectively)
document_number = "-".join([n.zfill(3 if i < 2 else 9) for i, n in enumerate(num_match.groups())])
else:
raise UserError(
_(u"Ecuadorian Document %s must be like 001-001-123456789")
% (self.display_name)
)
raise UserError(_(
"Ecuadorian Document %s must be like 001-001-123456789",
self.display_name
))
return document_number

View file

@ -1,10 +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):
self.ensure_one()
return self.account_fiscal_country_id.code == "EC" or super(ResCompany, self)._localization_use_documents()
return self.chart_template == 'ec' or self.account_fiscal_country_id.code == "EC" or super()._localization_use_documents()
def _is_latam(self):
return super()._is_latam() or self.country_code == 'EC'

View file

@ -30,7 +30,9 @@ class PartnerIdTypeEc(enum.Enum):
Returns ID code for move and partner based on subset of Table 2 of SRI's ATS specification
"""
partner_id_type = partner._l10n_ec_get_identification_type()
if move_type.startswith('in_'):
if partner.vat and verify_final_consumer(partner.vat):
return cls.FINAL_CONSUMER
elif move_type.startswith('in_'):
if partner_id_type == 'ruc': # includes final consumer
return cls.IN_RUC
elif partner_id_type == 'cedula':
@ -47,7 +49,6 @@ class PartnerIdTypeEc(enum.Enum):
class ResPartner(models.Model):
_inherit = "res.partner"
l10n_ec_vat_validation = fields.Char(
@ -56,26 +57,18 @@ class ResPartner(models.Model):
help="Error message when validating the Ecuadorian VAT",
)
@api.constrains("vat", "country_id", "l10n_latam_identification_type_id")
def check_vat(self):
it_ruc = self.env.ref("l10n_ec.ec_ruc", False)
it_dni = self.env.ref("l10n_ec.ec_dni", False)
ecuadorian_partners = self.filtered(
lambda x: x.country_id == self.env.ref("base.ec")
)
for partner in ecuadorian_partners:
if partner.vat:
if partner.l10n_latam_identification_type_id.id in (
it_ruc.id,
it_dni.id,
):
if partner.l10n_latam_identification_type_id.id == it_dni.id and len(partner.vat) != 10:
raise ValidationError(_('If your identification type is %s, it must be 10 digits')
% it_dni.display_name)
if partner.l10n_latam_identification_type_id.id == it_ruc.id and len(partner.vat) != 13:
raise ValidationError(_('If your identification type is %s, it must be 13 digits')
% it_ruc.display_name)
return super(ResPartner, self - ecuadorian_partners).check_vat()
def _run_check_identification(self, validation='error'):
""" Since we validate more documents than the vat for Argentinean partners (CUIT - VAT AR, CUIL, DNI) we
extend this method in order to process it. """
l10n_ec_partners = self.filtered(lambda p: p.vat and p.country_code == 'EC')
if l10n_ec_partners and validation == 'error':
it_dni = self.env.ref("l10n_ec.ec_dni", False)
for partner in l10n_ec_partners.filtered(lambda p: p.l10n_latam_identification_type_id == it_dni):
if len(partner.vat) != 10 or not partner.vat.isdecimal():
raise ValidationError(_('If your identification type is %s, it must be 10 digits',
it_dni.display_name))
return super(ResPartner, self - l10n_ec_partners)._run_check_identification(validation=validation)
@api.depends("vat", "country_id", "l10n_latam_identification_type_id")
def _compute_l10n_ec_vat_validation(self):
@ -90,10 +83,10 @@ class ResPartner(models.Model):
if not final_consumer:
if partner.l10n_latam_identification_type_id.id == it_dni.id and not ci.is_valid(partner.vat):
partner.l10n_ec_vat_validation = _("The VAT %s seems to be invalid as the tenth digit doesn't comply with the validation algorithm "
"(could be an old VAT number)") % partner.vat
"(could be an old VAT number)", partner.vat)
if partner.l10n_latam_identification_type_id.id == it_ruc.id and not ruc.is_valid(partner.vat):
partner.l10n_ec_vat_validation = _("The VAT %s seems to be invalid as the tenth digit doesn't comply with the validation algorithm "
"(SRI has stated that this validation is not required anymore for some VAT numbers)") % partner.vat
"(SRI has stated that this validation is not required anymore for some VAT numbers)", partner.vat)
def _l10n_ec_get_identification_type(self):
"""Maps Odoo identification types to Ecuadorian ones.

View file

@ -0,0 +1,71 @@
# 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('ec')
def _get_ec_template_data(self):
return {
'property_account_receivable_id': 'ec1102050101',
'property_account_payable_id': 'ec210301',
'journal_account_expense_categ_id': 'ec52022816',
'property_stock_valuation_account_id': 'ec110306',
'loss_stock_valuation_account': 'ec510112',
'production_stock_valuation_account': 'ec110302',
'code_digits': '4',
}
@template('ec', 'res.company')
def _get_ec_res_company(self):
return {
self.env.company.id: {
'account_fiscal_country_id': 'base.ec',
'bank_account_code_prefix': '11010201',
'cash_account_code_prefix': '1101010',
'transfer_account_code_prefix': '1101030',
'account_default_pos_receivable_account_id': 'ec1102050103',
'income_currency_exchange_account_id': 'ec430501',
'expense_currency_exchange_account_id': 'ec520304',
'account_journal_early_pay_discount_loss_account_id': 'ec_early_pay_discount_loss',
'account_journal_early_pay_discount_gain_account_id': 'ec_early_pay_discount_gain',
'default_cash_difference_income_account_id': 'ec_income_cash_difference',
'default_cash_difference_expense_account_id': 'ec_expense_cash_difference',
'account_sale_tax_id': 'tax_vat_15_411_goods',
'account_purchase_tax_id': 'tax_vat_15_510_sup_01',
'expense_account_id': 'ec110307',
'income_account_id': 'ec410101',
'tax_calculation_rounding_method': 'round_per_line',
'account_stock_journal_id': 'inventory_valuation',
'account_stock_valuation_id': 'ec110306',
},
}
@template('ec', 'account.journal')
def _get_ec_account_journal(self):
""" In case of an Ecuador, we modified the sales journal"""
return {
'sale': {
'name': "001-001 Facturas de cliente",
'l10n_ec_entity': '001',
'l10n_ec_emission': '001',
'l10n_ec_emission_address_id': self.env.company.partner_id.id,
},
}
def _post_load_data(self, template_code, company, template_data):
super()._post_load_data(template_code, company, template_data)
# Setup default Income/Expense Accounts on Sale/Purchase journals
if (purchase_journal := self.ref("purchase", raise_if_not_found=False)) and (expense_account_ref := template_data.get('journal_account_expense_categ_id')):
purchase_journal.default_account_id = self.ref(expense_account_ref, raise_if_not_found=False)
@template('ec', 'account.account')
def _get_ec_account_account(self):
return {
'ec110306': {
'account_stock_expense_id': 'ec510106',
'account_stock_variation_id': 'ec110310',
},
}