mirror of
https://github.com/bringout/oca-ocb-l10n_americas.git
synced 2026-04-26 17:52:03 +02:00
19.0 vanilla
This commit is contained in:
parent
89c6e82fe7
commit
1b82c20a58
572 changed files with 43570 additions and 53303 deletions
|
|
@ -1,11 +1,12 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import template_ar_ex
|
||||
from . import template_ar_ri
|
||||
from . import template_ar_base
|
||||
from . import l10n_latam_identification_type
|
||||
from . import l10n_ar_afip_responsibility_type
|
||||
from . import account_journal
|
||||
from . import account_tax_group
|
||||
from . import account_fiscal_position
|
||||
from . import account_fiscal_position_template
|
||||
from . import l10n_latam_document_type
|
||||
from . import res_partner
|
||||
from . import res_country
|
||||
|
|
|
|||
|
|
@ -1,73 +1,67 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, api, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.http import request
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.addons.account.models.chart_template import template
|
||||
|
||||
|
||||
class AccountChartTemplate(models.Model):
|
||||
|
||||
class AccountChartTemplate(models.AbstractModel):
|
||||
_inherit = 'account.chart.template'
|
||||
|
||||
def _get_fp_vals(self, company, position):
|
||||
res = super()._get_fp_vals(company, position)
|
||||
if company.country_id.code == "AR":
|
||||
res['l10n_ar_afip_responsibility_type_ids'] = [
|
||||
(6, False, position.l10n_ar_afip_responsibility_type_ids.ids)]
|
||||
return res
|
||||
|
||||
def _prepare_all_journals(self, acc_template_ref, company, journals_dict=None):
|
||||
""" In case of an Argentinean CoA, we modify the default values of the sales journal to be a preprinted journal"""
|
||||
res = super()._prepare_all_journals(acc_template_ref, company, journals_dict=journals_dict)
|
||||
if company.country_id.code == "AR":
|
||||
for vals in res:
|
||||
if vals['type'] == 'sale':
|
||||
vals.update({
|
||||
"name": _("Ventas Preimpreso"),
|
||||
"code": "0001",
|
||||
"l10n_ar_afip_pos_number": 1,
|
||||
"l10n_ar_afip_pos_partner_id": company.partner_id.id,
|
||||
"l10n_ar_afip_pos_system": 'II_IM',
|
||||
"refund_sequence": False
|
||||
})
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _get_ar_responsibility_match(self, chart_template_id):
|
||||
""" return responsibility type that match with the given chart_template_id
|
||||
def _get_ar_responsibility_match(self, chart_template):
|
||||
""" return responsibility type that match with the given chart_template code
|
||||
"""
|
||||
match = {
|
||||
self.env.ref('l10n_ar.l10nar_base_chart_template').id: self.env.ref('l10n_ar.res_RM'),
|
||||
self.env.ref('l10n_ar.l10nar_ex_chart_template').id: self.env.ref('l10n_ar.res_IVAE'),
|
||||
self.env.ref('l10n_ar.l10nar_ri_chart_template').id: self.env.ref('l10n_ar.res_IVARI'),
|
||||
'ar_base': self.env.ref('l10n_ar.res_RM'),
|
||||
'ar_ex': self.env.ref('l10n_ar.res_IVAE'),
|
||||
'ar_ri': self.env.ref('l10n_ar.res_IVARI'),
|
||||
}
|
||||
return match.get(chart_template_id)
|
||||
return match.get(chart_template)
|
||||
|
||||
def _load(self, company):
|
||||
""" Set companies AFIP Responsibility and Country if AR CoA is installed, also set tax calculation rounding
|
||||
method required in order to properly validate match AFIP invoices.
|
||||
def _load(self, template_code, company, install_demo,force_create=True):
|
||||
""" Set companies ARCA Responsibility and Country if AR CoA is installed, also set tax calculation rounding
|
||||
method required in order to properly validate match ARCA invoices.
|
||||
|
||||
Also, raise a warning if the user is trying to install a CoA that does not match with the defined AFIP
|
||||
Also, raise a warning if the user is trying to install a CoA that does not match with the defined ARCA
|
||||
Responsibility defined in the company
|
||||
"""
|
||||
self.ensure_one()
|
||||
coa_responsibility = self._get_ar_responsibility_match(self.id)
|
||||
coa_responsibility = self._get_ar_responsibility_match(template_code)
|
||||
if coa_responsibility:
|
||||
company_responsibility = company.l10n_ar_afip_responsibility_type_id
|
||||
company.write({
|
||||
'l10n_ar_afip_responsibility_type_id': coa_responsibility.id,
|
||||
'country_id': self.env['res.country'].search([('code', '=', 'AR')]).id,
|
||||
'tax_calculation_rounding_method': 'round_globally',
|
||||
})
|
||||
# set CUIT identification type (which is the argentinean vat) in the created company partner instead of
|
||||
# the default VAT type.
|
||||
company.partner_id.l10n_latam_identification_type_id = self.env.ref('l10n_ar.it_cuit')
|
||||
|
||||
res = super()._load(company)
|
||||
current_identification_type = company.partner_id.l10n_latam_identification_type_id
|
||||
try:
|
||||
# set CUIT identification type (which is the argentinean vat) in the created company partner instead of
|
||||
# the default VAT type.
|
||||
company.partner_id.l10n_latam_identification_type_id = self.env.ref('l10n_ar.it_cuit')
|
||||
except ValidationError:
|
||||
# put back previous value if we could not validate the CUIT
|
||||
company.partner_id.l10n_latam_identification_type_id = current_identification_type
|
||||
|
||||
res = super()._load(template_code, company, install_demo,force_create)
|
||||
|
||||
# If Responsable Monotributista remove the default purchase tax
|
||||
if self == self.env.ref('l10n_ar.l10nar_base_chart_template') or \
|
||||
self == self.env.ref('l10n_ar.l10nar_ex_chart_template'):
|
||||
if template_code in ('ar_base', 'ar_ex'):
|
||||
company.account_purchase_tax_id = self.env['account.tax']
|
||||
|
||||
return res
|
||||
|
||||
def try_loading(self, template_code, company, install_demo=False, force_create=True):
|
||||
# During company creation load template code corresponding to the ARCA Responsibility
|
||||
if not company:
|
||||
return
|
||||
if isinstance(company, int):
|
||||
company = self.env['res.company'].browse([company])
|
||||
if company.country_code == 'AR' and not company.chart_template:
|
||||
match = {
|
||||
self.env.ref('l10n_ar.res_RM'): 'ar_base',
|
||||
self.env.ref('l10n_ar.res_IVAE'): 'ar_ex',
|
||||
self.env.ref('l10n_ar.res_IVARI'): 'ar_ri',
|
||||
}
|
||||
template_code = match.get(company.l10n_ar_afip_responsibility_type_id, template_code)
|
||||
return super().try_loading(template_code, company, install_demo, force_create)
|
||||
|
|
|
|||
|
|
@ -3,24 +3,17 @@ from odoo import fields, models, api, _
|
|||
|
||||
|
||||
class AccountFiscalPosition(models.Model):
|
||||
|
||||
_inherit = 'account.fiscal.position'
|
||||
|
||||
l10n_ar_afip_responsibility_type_ids = fields.Many2many(
|
||||
'l10n_ar.afip.responsibility.type', 'l10n_ar_afip_reponsibility_type_fiscal_pos_rel',
|
||||
string='AFIP Responsibility Types', help='List of AFIP responsibilities where this fiscal position '
|
||||
string='ARCA Responsibility Types', help='List of ARCA responsibilities where this fiscal position '
|
||||
'should be auto-detected')
|
||||
|
||||
@api.model
|
||||
def _get_fiscal_position(self, partner, delivery=None):
|
||||
def _get_fpos_validation_functions(self, partner):
|
||||
functions = super()._get_fpos_validation_functions(partner)
|
||||
if self.env.company.country_id.code != "AR":
|
||||
return super()._get_fiscal_position(partner, delivery=delivery)
|
||||
return super(AccountFiscalPosition, self.with_context(l10n_ar_afip_responsibility_type_id=partner.l10n_ar_afip_responsibility_type_id.id))._get_fiscal_position(partner, delivery=delivery)
|
||||
|
||||
def _prepare_fpos_base_domain(self, vat_required):
|
||||
domain = super()._prepare_fpos_base_domain(vat_required)
|
||||
if 'l10n_ar_afip_responsibility_type_id' in self._context:
|
||||
domain += ['|',
|
||||
('l10n_ar_afip_responsibility_type_ids', '=', False),
|
||||
('l10n_ar_afip_responsibility_type_ids', '=', self._context.get('l10n_ar_afip_responsibility_type_id'))]
|
||||
return domain
|
||||
return functions
|
||||
return [
|
||||
lambda fpos: partner.l10n_ar_afip_responsibility_type_id in fpos.l10n_ar_afip_responsibility_type_ids,
|
||||
] + functions
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountFiscalPositionTemplate(models.Model):
|
||||
|
||||
_inherit = 'account.fiscal.position.template'
|
||||
|
||||
l10n_ar_afip_responsibility_type_ids = fields.Many2many(
|
||||
'l10n_ar.afip.responsibility.type', 'l10n_ar_afip_reponsibility_type_fiscal_pos_temp_rel',
|
||||
string='AFIP Responsibility Types')
|
||||
|
|
@ -1,22 +1,39 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, api, _
|
||||
from odoo.exceptions import ValidationError, RedirectWarning
|
||||
from odoo.exceptions import UserError, ValidationError, RedirectWarning
|
||||
|
||||
|
||||
class AccountJournal(models.Model):
|
||||
|
||||
_inherit = "account.journal"
|
||||
|
||||
l10n_ar_afip_pos_system = fields.Selection(
|
||||
selection='_get_l10n_ar_afip_pos_types_selection', string='AFIP POS System')
|
||||
selection='_get_l10n_ar_afip_pos_types_selection', string='ARCA POS System',
|
||||
compute='_compute_l10n_ar_afip_pos_system', store=True, readonly=False,
|
||||
help="Argentina: Specify which type of system will be used to create the electronic invoice. This will depend on the type of invoice to be created.",
|
||||
)
|
||||
l10n_ar_afip_pos_number = fields.Integer(
|
||||
'AFIP POS Number', help='This is the point of sale number assigned by AFIP in order to generate invoices')
|
||||
'ARCA POS Number', help='This is the point of sale number assigned by ARCA in order to generate invoices')
|
||||
company_partner = fields.Many2one('res.partner', related='company_id.partner_id')
|
||||
l10n_ar_afip_pos_partner_id = fields.Many2one(
|
||||
'res.partner', 'AFIP POS Address', help='This is the address used for invoice reports of this POS',
|
||||
'res.partner', 'ARCA POS Address', help='This is the address used for invoice reports of this POS',
|
||||
domain="['|', ('id', '=', company_partner), '&', ('id', 'child_of', company_partner), ('type', '!=', 'contact')]"
|
||||
)
|
||||
l10n_ar_is_pos = fields.Boolean(
|
||||
compute="_compute_l10n_ar_is_pos", store=True, readonly=False,
|
||||
string="Is ARCA POS?",
|
||||
help="Argentina: Specify if this Journal will be used to send electronic invoices to ARCA.",
|
||||
)
|
||||
|
||||
@api.depends('country_code', 'type', 'l10n_latam_use_documents')
|
||||
def _compute_l10n_ar_is_pos(self):
|
||||
for journal in self:
|
||||
journal.l10n_ar_is_pos = journal.country_code == 'AR' and journal.type == 'sale' and journal.l10n_latam_use_documents
|
||||
|
||||
@api.depends('l10n_ar_is_pos')
|
||||
def _compute_l10n_ar_afip_pos_system(self):
|
||||
for journal in self:
|
||||
journal.l10n_ar_afip_pos_system = journal.l10n_ar_is_pos and journal.l10n_ar_afip_pos_system
|
||||
|
||||
def _get_l10n_ar_afip_pos_types_selection(self):
|
||||
""" Return the list of values of the selection field. """
|
||||
|
|
@ -27,10 +44,11 @@ class AccountJournal(models.Model):
|
|||
('FEERCELP', _('Export Voucher - Billing Plus')),
|
||||
('FEERCEL', _('Export Voucher - Online Invoice')),
|
||||
('CPERCEL', _('Product Coding - Online Voucher')),
|
||||
('CF', _('External Fiscal Controller')),
|
||||
]
|
||||
|
||||
def _get_journal_letter(self, counterpart_partner=False):
|
||||
""" Regarding the AFIP responsibility of the company and the type of journal (sale/purchase), get the allowed
|
||||
""" Regarding the ARCA responsibility of the company and the type of journal (sale/purchase), get the allowed
|
||||
letters. Optionally, receive the counterpart partner (customer/supplier) and get the allowed letters to work
|
||||
with him. This method is used to populate document types on journals and also to filter document types on
|
||||
specific invoices to/from customer/supplier
|
||||
|
|
@ -43,7 +61,7 @@ class AccountJournal(models.Model):
|
|||
'5': [],
|
||||
'6': ['C', 'E'],
|
||||
'7': ['B', 'C', 'I'],
|
||||
'8': ['B', 'C', 'I'],
|
||||
'8': ['I'],
|
||||
'9': ['I'],
|
||||
'10': [],
|
||||
'13': ['C', 'E'],
|
||||
|
|
@ -66,21 +84,19 @@ class AccountJournal(models.Model):
|
|||
}
|
||||
if not self.company_id.l10n_ar_afip_responsibility_type_id:
|
||||
action = self.env.ref('base.action_res_company_form')
|
||||
msg = _('Can not create chart of account until you configure your company AFIP Responsibility and VAT.')
|
||||
msg = _('Can not create chart of account until you configure your company ARCA Responsibility and VAT.')
|
||||
raise RedirectWarning(msg, action.id, _('Go to Companies'))
|
||||
|
||||
letters = letters_data['issued' if self.type == 'sale' else 'received'][
|
||||
letters = letters_data['issued' if self.l10n_ar_is_pos else 'received'][
|
||||
self.company_id.l10n_ar_afip_responsibility_type_id.code]
|
||||
if counterpart_partner:
|
||||
counterpart_letters = letters_data['issued' if self.type == 'purchase' else 'received'].get(
|
||||
counterpart_letters = letters_data['issued' if not self.l10n_ar_is_pos else 'received'].get(
|
||||
counterpart_partner.l10n_ar_afip_responsibility_type_id.code, [])
|
||||
letters = list(set(letters) & set(counterpart_letters))
|
||||
return letters
|
||||
|
||||
def _get_journal_codes(self):
|
||||
def _get_journal_codes_domain(self):
|
||||
self.ensure_one()
|
||||
if self.type != 'sale':
|
||||
return []
|
||||
return self._get_codes_per_journal_type(self.l10n_ar_afip_pos_system)
|
||||
|
||||
@api.model
|
||||
|
|
@ -91,51 +107,85 @@ class AccountJournal(models.Model):
|
|||
receipt_m_code = ['54']
|
||||
receipt_codes = ['4', '9', '15']
|
||||
expo_codes = ['19', '20', '21']
|
||||
zeta_codes = ['80', '83']
|
||||
if afip_pos_system == 'II_IM':
|
||||
tique_codes = ['81', '82', '83', '110', '112', '113', '115', '116', '118', '119', '120']
|
||||
lsg_codes = ['331']
|
||||
no_pos_docs = [
|
||||
'23', '24', '25', '26', '27', '28', '33', '43', '45', '46', '48', '58', '60', '61', '150', '151', '157',
|
||||
'158', '161', '162', '164', '166', '167', '171', '172', '180', '182', '186', '188', '332']
|
||||
codes = []
|
||||
if (self.type == 'sale' and not self.l10n_ar_is_pos) or (self.type == 'purchase' and afip_pos_system in ['II_IM', 'RLI_RLM']):
|
||||
codes = no_pos_docs + lsg_codes
|
||||
elif self.type == 'purchase' and afip_pos_system == 'RAW_MAW':
|
||||
# electronic invoices (wsfev1) (intersection between available docs on ws and no_pos_docs)
|
||||
codes = ['60', '61']
|
||||
elif self.type == 'purchase':
|
||||
return [('code', 'not in', no_pos_docs)]
|
||||
elif afip_pos_system == 'II_IM':
|
||||
# pre-printed invoice
|
||||
return usual_codes + receipt_codes + expo_codes + invoice_m_code + receipt_m_code
|
||||
elif afip_pos_system == 'RAW_MAW':
|
||||
codes = usual_codes + receipt_codes + expo_codes + invoice_m_code + receipt_m_code
|
||||
elif afip_pos_system in ['RAW_MAW', 'RLI_RLM']:
|
||||
# electronic/online invoice
|
||||
return usual_codes + receipt_codes + invoice_m_code + receipt_m_code + mipyme_codes
|
||||
elif afip_pos_system == 'RLI_RLM':
|
||||
return usual_codes + receipt_codes + invoice_m_code + receipt_m_code + mipyme_codes + zeta_codes
|
||||
codes = usual_codes + receipt_codes + invoice_m_code + receipt_m_code + mipyme_codes
|
||||
elif afip_pos_system in ['CPERCEL', 'CPEWS']:
|
||||
# invoice with detail
|
||||
return usual_codes + invoice_m_code
|
||||
codes = usual_codes + invoice_m_code
|
||||
elif afip_pos_system in ['BFERCEL', 'BFEWS']:
|
||||
# Bonds invoice
|
||||
return usual_codes + mipyme_codes
|
||||
codes = usual_codes + mipyme_codes
|
||||
elif afip_pos_system in ['FEERCEL', 'FEEWS', 'FEERCELP']:
|
||||
return expo_codes
|
||||
codes = expo_codes
|
||||
elif afip_pos_system == 'CF':
|
||||
codes = tique_codes
|
||||
return [('code', 'in', codes)]
|
||||
|
||||
@api.constrains('type', 'l10n_ar_afip_pos_system', 'l10n_ar_afip_pos_number', 'l10n_latam_use_documents')
|
||||
def _check_afip_configurations(self):
|
||||
""" Do not let the user update the journal if it already contains confirmed invoices """
|
||||
journals = self.filtered(lambda x: x.company_id.account_fiscal_country_id.code == "AR" and x.type in ['sale', 'purchase'])
|
||||
invoices = self.env['account.move'].search([('journal_id', 'in', journals.ids), ('posted_before', '=', True)], limit=1)
|
||||
if invoices:
|
||||
raise ValidationError(
|
||||
_("You can not change the journal's configuration if it already has validated invoices") + ' ('
|
||||
+ ', '.join(invoices.mapped('journal_id').mapped('name')) + ')')
|
||||
@api.constrains('l10n_ar_afip_pos_system')
|
||||
def _check_afip_pos_system(self):
|
||||
journals = self.filtered(
|
||||
lambda j: j.l10n_ar_is_pos and j.type == 'purchase' and
|
||||
j.l10n_ar_afip_pos_system not in ['II_IM', 'RLI_RLM', 'RAW_MAW'])
|
||||
if journals:
|
||||
raise ValidationError("\n".join(
|
||||
_("The pos system %(system)s can not be used on a purchase journal (id %(id)s)", system=x.l10n_ar_afip_pos_system, id=x.id)
|
||||
for x in journals
|
||||
))
|
||||
|
||||
@api.constrains('l10n_ar_afip_pos_number')
|
||||
def _check_afip_pos_number(self):
|
||||
to_review = self.filtered(
|
||||
lambda x: x.type == 'sale' and x.l10n_latam_use_documents and
|
||||
x.company_id.account_fiscal_country_id.code == "AR")
|
||||
if self.filtered(lambda j: j.l10n_ar_is_pos and j.l10n_ar_afip_pos_number == 0):
|
||||
raise ValidationError(_('Please define an ARCA POS number'))
|
||||
|
||||
if to_review.filtered(lambda x: x.l10n_ar_afip_pos_number == 0):
|
||||
raise ValidationError(_('Please define an AFIP POS number'))
|
||||
|
||||
if to_review.filtered(lambda x: x.l10n_ar_afip_pos_number > 99999):
|
||||
raise ValidationError(_('Please define a valid AFIP POS number (5 digits max)'))
|
||||
if self.filtered(lambda j: j.l10n_ar_is_pos and j.l10n_ar_afip_pos_number > 99999):
|
||||
raise ValidationError(_('Please define a valid ARCA POS number (5 digits max)'))
|
||||
|
||||
@api.onchange('l10n_ar_afip_pos_number', 'type')
|
||||
def _onchange_set_short_name(self):
|
||||
""" Will define the AFIP POS Address field domain taking into account the company configured in the journal
|
||||
""" Will define the ARCA POS Address field domain taking into account the company configured in the journal
|
||||
The short code of the journal only admit 5 characters, so depending on the size of the pos_number (also max 5)
|
||||
we add or not a prefix to identify sales journal.
|
||||
"""
|
||||
if self.type == 'sale' and self.l10n_ar_afip_pos_number:
|
||||
self.code = "%05i" % self.l10n_ar_afip_pos_number
|
||||
|
||||
def write(self, vals):
|
||||
protected_fields = ('type', 'l10n_ar_afip_pos_system', 'l10n_ar_afip_pos_number', 'l10n_latam_use_documents')
|
||||
fields_to_check = [field for field in protected_fields if field in vals]
|
||||
|
||||
if fields_to_check:
|
||||
self.env.cr.execute("SELECT DISTINCT(journal_id) FROM account_move WHERE posted_before = True")
|
||||
res = self.env.cr.fetchall()
|
||||
journal_with_entry_ids = [journal_id for journal_id, in res]
|
||||
|
||||
for journal in self:
|
||||
if (
|
||||
journal.company_id.account_fiscal_country_id.code != "AR"
|
||||
or journal.type not in ['sale', 'purchase']
|
||||
or journal.id not in journal_with_entry_ids
|
||||
):
|
||||
continue
|
||||
|
||||
for field in fields_to_check:
|
||||
# Wouldn't work if there was a relational field, as we would compare an id with a recordset.
|
||||
if vals[field] != journal[field]:
|
||||
raise UserError(_("You can not change %s journal's configuration if it already has validated invoices", journal.name))
|
||||
|
||||
return super().write(vals)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError, RedirectWarning, ValidationError
|
||||
from odoo.fields import Domain
|
||||
from odoo.tools.misc import formatLang
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import logging
|
||||
|
|
@ -8,7 +9,6 @@ _logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
|
||||
_inherit = 'account.move'
|
||||
|
||||
@api.model
|
||||
|
|
@ -21,19 +21,33 @@ class AccountMove(models.Model):
|
|||
return {'invoice_number': int(invoice_number), 'point_of_sale': int(pos)}
|
||||
|
||||
l10n_ar_afip_responsibility_type_id = fields.Many2one(
|
||||
'l10n_ar.afip.responsibility.type', string='AFIP Responsibility Type', help='Defined by AFIP to'
|
||||
'l10n_ar.afip.responsibility.type', string='ARCA Responsibility Type', help='Defined by ARCA to'
|
||||
' identify the type of responsibilities that a person or a legal entity could have and that impacts in the'
|
||||
' type of operations and requirements they need.')
|
||||
|
||||
l10n_ar_currency_rate = fields.Float(copy=False, digits=(16, 6), readonly=True, string="Currency Rate")
|
||||
|
||||
# Mostly used on reports
|
||||
l10n_ar_afip_concept = fields.Selection(
|
||||
compute='_compute_l10n_ar_afip_concept', selection='_get_afip_invoice_concepts', string="AFIP Concept",
|
||||
help="A concept is suggested regarding the type of the products on the invoice but it is allowed to force a"
|
||||
" different type if required.")
|
||||
l10n_ar_afip_service_start = fields.Date(string='AFIP Service Start Date', readonly=True, states={'draft': [('readonly', False)]})
|
||||
l10n_ar_afip_service_end = fields.Date(string='AFIP Service End Date', readonly=True, states={'draft': [('readonly', False)]})
|
||||
compute='_compute_l10n_ar_afip_concept', selection='_get_afip_invoice_concepts', string="ARCA Concept",
|
||||
help="A concept is suggested regarding the type of the products on the invoice.")
|
||||
l10n_ar_afip_service_start = fields.Date(string='ARCA Service Start Date')
|
||||
l10n_ar_afip_service_end = fields.Date(string='ARCA Service End Date')
|
||||
|
||||
def _is_manual_document_number(self):
|
||||
""" Document number should be manual input by user when the journal use documents and
|
||||
|
||||
* if sales journal and not a ARCA pos (liquido producto case)
|
||||
* if purchase journal and not a ARCA pos (regular case of vendor bills)
|
||||
|
||||
All the other cases the number should be automatic set, wiht only one exception, for pre-printed/online ARCA
|
||||
POS type, the first numeber will be always set manually by the user and then will be computed automatically
|
||||
from there """
|
||||
if self.country_code != 'AR':
|
||||
return super()._is_manual_document_number()
|
||||
|
||||
# NOTE: There is a corner case where 2 sales documents can have the same number for the same DOC from a
|
||||
# different vendor, in that case, the user can create a new Sales Liquido Producto Journal
|
||||
return self.l10n_latam_use_documents and self.journal_id.type in ['purchase', 'sale'] and \
|
||||
not self.journal_id.l10n_ar_is_pos
|
||||
|
||||
@api.constrains('move_type', 'journal_id')
|
||||
def _check_moves_use_documents(self):
|
||||
|
|
@ -72,15 +86,15 @@ class AccountMove(models.Model):
|
|||
def _get_concept(self):
|
||||
""" Method to get the concept of the invoice considering the type of the products on the invoice """
|
||||
self.ensure_one()
|
||||
invoice_lines = self.invoice_line_ids.filtered(lambda x: x.display_type not in ('line_note', 'line_section'))
|
||||
invoice_lines = self.invoice_line_ids.filtered(lambda x: x.display_type not in ('line_section', 'line_subsection', 'line_note'))
|
||||
product_types = set([x.product_id.type for x in invoice_lines if x.product_id])
|
||||
consumable = set(['consu', 'product'])
|
||||
consumable = {'consu'}
|
||||
service = set(['service'])
|
||||
# on expo invoice you can mix services and products
|
||||
expo_invoice = self.l10n_latam_document_type_id.code in ['19', '20', '21']
|
||||
|
||||
# WSFEX 1668 - If Expo invoice and we have a "IVA Liberado – Ley Nº 19.640" (Zona Franca) partner
|
||||
# then AFIP concept to use should be type "Others (4)"
|
||||
# then ARCA concept to use should be type "Others (4)"
|
||||
is_zona_franca = self.partner_id.l10n_ar_afip_responsibility_type_id == self.env.ref("l10n_ar.res_IVA_LIB")
|
||||
# Default value "product"
|
||||
afip_concept = '1'
|
||||
|
|
@ -104,12 +118,11 @@ class AccountMove(models.Model):
|
|||
domain = super()._get_l10n_latam_documents_domain()
|
||||
if self.journal_id.company_id.account_fiscal_country_id.code == "AR":
|
||||
letters = self.journal_id._get_journal_letter(counterpart_partner=self.partner_id.commercial_partner_id)
|
||||
domain += ['|', ('l10n_ar_letter', '=', False), ('l10n_ar_letter', 'in', letters)]
|
||||
codes = self.journal_id._get_journal_codes()
|
||||
if codes:
|
||||
domain.append(('code', 'in', codes))
|
||||
if self.move_type == 'in_refund':
|
||||
domain = ['|', ('code', 'in', self._get_l10n_ar_codes_used_for_inv_and_ref())] + domain
|
||||
domain = Domain(domain)
|
||||
domain &= Domain('l10n_ar_letter', '=', False) | Domain('l10n_ar_letter', 'in', letters)
|
||||
domain &= Domain(self.journal_id._get_journal_codes_domain())
|
||||
if self.move_type in ['out_refund', 'in_refund']:
|
||||
domain = Domain('code', 'in', self._get_l10n_ar_codes_used_for_inv_and_ref()) | domain
|
||||
return domain
|
||||
|
||||
def _check_argentinean_invoice_taxes(self):
|
||||
|
|
@ -120,15 +133,15 @@ class AccountMove(models.Model):
|
|||
# we require a single vat on each invoice line except from some purchase documents
|
||||
if inv.move_type in ['in_invoice', 'in_refund'] and inv.l10n_latam_document_type_id.purchase_aliquots == 'zero':
|
||||
purchase_aliquots = 'zero'
|
||||
for line in inv.mapped('invoice_line_ids').filtered(lambda x: x.display_type not in ('line_section', 'line_note')):
|
||||
for line in inv.mapped('invoice_line_ids').filtered(lambda x: x.display_type not in ('line_section', 'line_subsection', 'line_note')):
|
||||
vat_taxes = line.tax_ids.filtered(lambda x: x.tax_group_id.l10n_ar_vat_afip_code)
|
||||
if len(vat_taxes) != 1:
|
||||
raise UserError(_('There should be a single tax from the "VAT" tax group per line, add it to "%s". If you already have it, please check the tax configuration, in advanced options, in the corresponding field "Tax Group".') % line.name)
|
||||
raise UserError(_("There should be a single tax from the “VAT“ tax group per line, but this is not the case for line “%s”. Please add a tax to this line or check the tax configuration's advanced options for the corresponding field “Tax Group”.", line.name))
|
||||
|
||||
elif purchase_aliquots == 'zero' and vat_taxes.tax_group_id.l10n_ar_vat_afip_code != '0':
|
||||
raise UserError(_('On invoice id "%s" you must use VAT Not Applicable on every line.') % inv.id)
|
||||
raise UserError(_('On invoice id “%s” you must use VAT Not Applicable on every line.', inv.id))
|
||||
elif purchase_aliquots == 'not_zero' and vat_taxes.tax_group_id.l10n_ar_vat_afip_code == '0':
|
||||
raise UserError(_('On invoice id "%s" you must use VAT taxes different than VAT Not Applicable.') % inv.id)
|
||||
raise UserError(_('On invoice id “%s” you must use a VAT tax that is not VAT Not Applicable', inv.id))
|
||||
|
||||
def _set_afip_service_dates(self):
|
||||
for rec in self.filtered(lambda m: m.invoice_date and m.l10n_ar_afip_concept in ['2', '3', '4']):
|
||||
|
|
@ -143,27 +156,13 @@ class AccountMove(models.Model):
|
|||
for rec in self:
|
||||
rec.l10n_ar_afip_responsibility_type_id = rec.commercial_partner_id.l10n_ar_afip_responsibility_type_id.id
|
||||
|
||||
def _set_afip_rate(self):
|
||||
""" We set the l10n_ar_currency_rate value with the accounting date. This should be done
|
||||
after invoice has been posted in order to have the proper accounting date"""
|
||||
for rec in self:
|
||||
if rec.company_id.currency_id == rec.currency_id:
|
||||
rec.l10n_ar_currency_rate = 1.0
|
||||
elif not rec.l10n_ar_currency_rate:
|
||||
rec.l10n_ar_currency_rate = self.env['res.currency']._get_conversion_rate(
|
||||
from_currency=rec.currency_id,
|
||||
to_currency=rec.company_id.currency_id,
|
||||
company=rec.company_id,
|
||||
date=rec.invoice_date,
|
||||
)
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def _onchange_afip_responsibility(self):
|
||||
if self.company_id.account_fiscal_country_id.code == 'AR' and self.l10n_latam_use_documents and self.partner_id \
|
||||
and not self.partner_id.l10n_ar_afip_responsibility_type_id:
|
||||
return {'warning': {
|
||||
'title': _('Missing Partner Configuration'),
|
||||
'message': _('Please configure the AFIP Responsibility for "%s" in order to continue') % (
|
||||
'message': _('Please configure the ARCA Responsibility for "%s" in order to continue',
|
||||
self.partner_id.name)}}
|
||||
|
||||
@api.onchange('partner_id')
|
||||
|
|
@ -173,15 +172,19 @@ class AccountMove(models.Model):
|
|||
for rec in self.filtered(lambda x: x.company_id.account_fiscal_country_id.code == "AR" and x.journal_id.type == 'sale'
|
||||
and x.l10n_latam_use_documents and x.partner_id.l10n_ar_afip_responsibility_type_id):
|
||||
res_code = rec.partner_id.l10n_ar_afip_responsibility_type_id.code
|
||||
domain = [('company_id', '=', rec.company_id.id), ('l10n_latam_use_documents', '=', True), ('type', '=', 'sale')]
|
||||
domain = [
|
||||
*self.env['account.journal']._check_company_domain(rec.company_id),
|
||||
('l10n_latam_use_documents', '=', True),
|
||||
('type', '=', 'sale'),
|
||||
]
|
||||
journal = self.env['account.journal']
|
||||
msg = False
|
||||
if res_code in ['9', '10'] and rec.journal_id.l10n_ar_afip_pos_system not in expo_journals:
|
||||
# if partner is foregin and journal is not of expo, we try to change to expo journal
|
||||
if res_code in ['8', '9', '10'] and rec.journal_id.l10n_ar_afip_pos_system not in expo_journals:
|
||||
# if it is a foreign partner and journal is not for expo, we try to change it to an expo journal
|
||||
journal = journal.search(domain + [('l10n_ar_afip_pos_system', 'in', expo_journals)], limit=1)
|
||||
msg = _('You are trying to create an invoice for foreign partner but you don\'t have an exportation journal')
|
||||
elif res_code not in ['9', '10'] and rec.journal_id.l10n_ar_afip_pos_system in expo_journals:
|
||||
# if partner is NOT foregin and journal is for expo, we try to change to local journal
|
||||
elif res_code not in ['8', '9', '10'] and rec.journal_id.l10n_ar_afip_pos_system in expo_journals:
|
||||
# if it is NOT a foreign partner and journal is for expo, we try to change it to a local journal
|
||||
journal = journal.search(domain + [('l10n_ar_afip_pos_system', 'not in', expo_journals)], limit=1)
|
||||
msg = _('You are trying to create an invoice for domestic partner but you don\'t have a domestic market journal')
|
||||
if journal:
|
||||
|
|
@ -191,6 +194,21 @@ class AccountMove(models.Model):
|
|||
action = self.env.ref('account.action_account_journal_form')
|
||||
raise RedirectWarning(msg, action.id, _('Go to Journals'))
|
||||
|
||||
def _compute_l10n_latam_document_type(self):
|
||||
"""We correct the default document type in vendor bills in case the partner is foreign (code 8)
|
||||
so that it is always 'Foreign invoices and receipts'.
|
||||
"""
|
||||
super()._compute_l10n_latam_document_type()
|
||||
foreign_vendor_bills = self.filtered(lambda x: (
|
||||
x.company_id.account_fiscal_country_id.code == "AR"
|
||||
and x.state == 'draft'
|
||||
and x.move_type in ['in_invoice', 'in_refund']
|
||||
and x.l10n_latam_document_type_id
|
||||
and x.partner_id.l10n_ar_afip_responsibility_type_id.code == '8'))
|
||||
doctype_fa_exterior = self.env.ref('l10n_ar.fa_exterior', raise_if_not_found=False)
|
||||
if doctype_fa_exterior:
|
||||
foreign_vendor_bills.l10n_latam_document_type_id = doctype_fa_exterior
|
||||
|
||||
def _post(self, soft=True):
|
||||
ar_invoices = self.filtered(lambda x: x.company_id.account_fiscal_country_id.code == "AR" and x.l10n_latam_use_documents)
|
||||
# We make validations here and not with a constraint because we want validation before sending electronic
|
||||
|
|
@ -200,7 +218,6 @@ class AccountMove(models.Model):
|
|||
|
||||
posted_ar_invoices = posted & ar_invoices
|
||||
posted_ar_invoices._set_afip_responsibility()
|
||||
posted_ar_invoices._set_afip_rate()
|
||||
posted_ar_invoices._set_afip_service_dates()
|
||||
return posted
|
||||
|
||||
|
|
@ -219,7 +236,7 @@ class AccountMove(models.Model):
|
|||
super()._inverse_l10n_latam_document_number()
|
||||
|
||||
to_review = self.filtered(lambda x: (
|
||||
x.journal_id.type == 'sale'
|
||||
x.journal_id.l10n_ar_is_pos
|
||||
and x.l10n_latam_document_type_id
|
||||
and x.l10n_latam_document_number
|
||||
and (x.l10n_latam_manual_document_number or not x.highest_name)
|
||||
|
|
@ -246,7 +263,7 @@ class AccountMove(models.Model):
|
|||
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.journal_id.l10n_latam_use_documents and self.company_id.account_fiscal_country_id.code == "AR":
|
||||
if self.l10n_latam_use_documents and self.company_id.account_fiscal_country_id.code == "AR":
|
||||
if self.l10n_latam_document_type_id:
|
||||
return self._get_formatted_sequence()
|
||||
return super()._get_starting_sequence()
|
||||
|
|
@ -278,7 +295,22 @@ class AccountMove(models.Model):
|
|||
if any(tax.tax_group_id.l10n_ar_vat_afip_code and tax.tax_group_id.l10n_ar_vat_afip_code not in ['0', '1', '2'] for tax in line.tax_ids):
|
||||
vat_taxable |= line
|
||||
|
||||
profits_tax_group = self.env.ref('l10n_ar.tax_group_percepcion_ganancias')
|
||||
profits_tax_group = self.env['account.chart.template'].with_company(self.company_id).ref(
|
||||
'tax_group_percepcion_ganancias',
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
if not profits_tax_group:
|
||||
raise RedirectWarning(
|
||||
message=_(
|
||||
"A required tax group could not be found (XML ID: %s).\n"
|
||||
"Please reload your chart template in order to reinstall the required tax group.\n\n"
|
||||
"Note: You might have to relink your existing taxes to this new tax group.",
|
||||
'tax_group_percepcion_ganancias',
|
||||
),
|
||||
action=self.env.ref('account.action_account_config').id,
|
||||
button_text=_("Accounting Settings"),
|
||||
)
|
||||
|
||||
return {'vat_amount': sign * sum(vat_taxes.mapped(amount_field)),
|
||||
# For invoices of letter C should not pass VAT
|
||||
'vat_taxable_amount': sign * sum(vat_taxable.mapped(amount_field)) if self.l10n_latam_document_type_id.l10n_ar_letter != 'C' else self.amount_untaxed,
|
||||
|
|
@ -318,7 +350,7 @@ class AccountMove(models.Model):
|
|||
# Report vat 0%
|
||||
vat_base_0 = sum(self.invoice_line_ids.filtered(lambda x: x.tax_ids.filtered(lambda y: y.tax_group_id.l10n_ar_vat_afip_code == '3')).mapped('price_subtotal'))
|
||||
if vat_base_0:
|
||||
res += [{'Id': '3', 'BaseImp': vat_base_0, 'Importe': 0.0}]
|
||||
res += [{'Id': '3', 'BaseImp': sign * vat_base_0, 'Importe': 0.0}]
|
||||
|
||||
return res if res else []
|
||||
|
||||
|
|
@ -331,81 +363,74 @@ class AccountMove(models.Model):
|
|||
def _l10n_ar_get_invoice_totals_for_report(self):
|
||||
"""If the invoice document type indicates that vat should not be detailed in the printed report (result of _l10n_ar_include_vat()) then we overwrite tax_totals field so that includes taxes in the total amount, otherwise it would be showing amount_untaxed in the amount_total"""
|
||||
self.ensure_one()
|
||||
tax_totals = self.tax_totals
|
||||
include_vat = self._l10n_ar_include_vat()
|
||||
base_lines = self.line_ids.filtered(lambda x: x.display_type == 'product')
|
||||
tax_lines = self.line_ids.filtered(lambda x: x.display_type == 'tax')
|
||||
if not include_vat:
|
||||
return tax_totals
|
||||
|
||||
involved_tax_group_ids = []
|
||||
for subtotals in self.tax_totals['groups_by_subtotal'].values():
|
||||
for subtotal in subtotals:
|
||||
involved_tax_group_ids.append(subtotal['tax_group_id'])
|
||||
involved_tax_groups = self.env['account.tax.group'].browse(involved_tax_group_ids)
|
||||
nat_int_tax_groups = involved_tax_groups.filtered(lambda tax_group: tax_group.l10n_ar_tribute_afip_code in ('01', '04'))
|
||||
vat_tax_groups = involved_tax_groups.filtered('l10n_ar_vat_afip_code')
|
||||
both_tax_group_ids = nat_int_tax_groups.ids + vat_tax_groups.ids
|
||||
tax_group_ids = {
|
||||
tax_group['id']
|
||||
for subtotal in tax_totals['subtotals']
|
||||
for tax_group in subtotal['tax_groups']
|
||||
}
|
||||
tax_group_ids_to_exclude = self.env['account.tax.group']\
|
||||
.browse(tax_group_ids)\
|
||||
.filtered(lambda tax_group: (
|
||||
self._l10n_ar_is_tax_group_other_national_ind_tax(tax_group)
|
||||
or self._l10n_ar_is_tax_group_vat(tax_group)
|
||||
)).ids
|
||||
if tax_group_ids_to_exclude:
|
||||
tax_totals = self.env['account.tax']._exclude_tax_groups_from_tax_totals_summary(tax_totals, tax_group_ids_to_exclude)
|
||||
return tax_totals
|
||||
|
||||
# Base lines.
|
||||
base_line_vals_list = [x._convert_to_tax_base_line_dict() for x in base_lines]
|
||||
if include_vat:
|
||||
for vals in base_line_vals_list:
|
||||
vals['taxes'] = vals['taxes']\
|
||||
.flatten_taxes_hierarchy()\
|
||||
.filtered(lambda tax: tax.tax_group_id.id not in both_tax_group_ids)
|
||||
def _l10n_ar_get_invoice_custom_tax_summary_for_report(self):
|
||||
""" Get a new tax details for RG 5614/2024 to show ARCA VAT and Other National Internal Taxes. """
|
||||
if self.l10n_latam_document_type_id.code not in ('6', '7', '8'):
|
||||
return []
|
||||
|
||||
# Tax lines.
|
||||
tax_line_vals_list = [x._convert_to_tax_line_dict() for x in tax_lines]
|
||||
if include_vat:
|
||||
tax_line_vals_list = [
|
||||
x
|
||||
for x in tax_line_vals_list
|
||||
if x['tax_repartition_line'].tax_id.tax_group_id.id not in both_tax_group_ids
|
||||
]
|
||||
base_lines, _tax_lines = self._get_rounded_base_and_tax_lines()
|
||||
|
||||
tax_totals = self.env['account.tax']._prepare_tax_totals(
|
||||
base_line_vals_list,
|
||||
self.currency_id,
|
||||
tax_lines=tax_line_vals_list,
|
||||
)
|
||||
|
||||
temp = self.tax_totals
|
||||
if include_vat:
|
||||
tax_totals['amount_total'] = temp['amount_total']
|
||||
tax_totals['formatted_amount_total'] = temp['formatted_amount_total']
|
||||
|
||||
# RG 5614/2024: Show ARCA VAT and Other National Internal Taxes
|
||||
if self.l10n_latam_document_type_id.code in ['6', '7', '8']:
|
||||
|
||||
# Prepare the subtotals to show in the report
|
||||
currency_symbol = self.currency_id.symbol
|
||||
detail_info = {
|
||||
'vat_taxes': {'name': _("VAT Content %s", currency_symbol), 'tax_amount': 0.0, 'group': 'vat'},
|
||||
'other_taxes': {'name': _("Other National Ind. Taxes %s", currency_symbol), 'tax_amount': 0.0,
|
||||
'group': 'other'},
|
||||
def grouping_function(base_line, tax_data):
|
||||
if not tax_data:
|
||||
return None
|
||||
tax_group = tax_data['tax'].tax_group_id
|
||||
skip = False
|
||||
name = None
|
||||
if self._l10n_ar_is_tax_group_other_national_ind_tax(tax_group):
|
||||
name = _("Other National Ind. Taxes %s", base_line['currency_id'].symbol)
|
||||
elif self._l10n_ar_is_tax_group_vat(tax_group):
|
||||
name = _("VAT Content %s", base_line['currency_id'].symbol)
|
||||
else:
|
||||
skip = True
|
||||
return {
|
||||
'name': name,
|
||||
'skip': skip,
|
||||
}
|
||||
|
||||
for subtotals in temp['groups_by_subtotal'].values():
|
||||
for subtotal in subtotals:
|
||||
tax_group_id = subtotal['tax_group_id']
|
||||
if tax_group_id in nat_int_tax_groups.ids:
|
||||
key = 'other_taxes'
|
||||
elif tax_group_id in vat_tax_groups.ids:
|
||||
key = 'vat_taxes'
|
||||
else:
|
||||
continue # If not belongs to the needed groups we ignore them
|
||||
|
||||
detail_info[key]["tax_amount"] += subtotal['tax_group_amount']
|
||||
|
||||
if detail_info['other_taxes']["tax_amount"] == 0.0:
|
||||
detail_info.pop('other_taxes')
|
||||
|
||||
# Format the amounts to show in the report
|
||||
for _item, values in detail_info.items():
|
||||
values["formatted_amount_tax"] = formatLang(self.env, values["tax_amount"])
|
||||
|
||||
tax_totals["detail_ar_tax"] = list(detail_info.values())
|
||||
|
||||
return tax_totals
|
||||
AccountTax = self.env['account.tax']
|
||||
base_lines_aggregated_values = AccountTax._aggregate_base_lines_tax_details(base_lines, grouping_function)
|
||||
values_per_grouping_key = AccountTax._aggregate_base_lines_aggregated_values(base_lines_aggregated_values)
|
||||
results = []
|
||||
for grouping_key, values in values_per_grouping_key.items():
|
||||
if (
|
||||
grouping_key
|
||||
and not grouping_key['skip']
|
||||
):
|
||||
results.append({
|
||||
'name': grouping_key['name'],
|
||||
'tax_amount_currency': values['tax_amount_currency'],
|
||||
'formatted_tax_amount_currency': formatLang(self.env, values['tax_amount_currency']),
|
||||
})
|
||||
return results
|
||||
|
||||
def _l10n_ar_include_vat(self):
|
||||
self.ensure_one()
|
||||
return self.l10n_latam_document_type_id.l10n_ar_letter in ['B', 'C', 'X', 'R']
|
||||
|
||||
@api.model
|
||||
def _l10n_ar_is_tax_group_other_national_ind_tax(self, tax_group):
|
||||
return tax_group.l10n_ar_tribute_afip_code in ('01', '04')
|
||||
|
||||
@api.model
|
||||
def _l10n_ar_is_tax_group_vat(self, tax_group):
|
||||
return bool(tax_group.l10n_ar_vat_afip_code)
|
||||
|
|
|
|||
|
|
@ -3,29 +3,46 @@ from odoo import models
|
|||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
def _l10n_ar_prices_and_taxes(self):
|
||||
self.ensure_one()
|
||||
invoice = self.move_id
|
||||
included_taxes = self.tax_ids.filtered('tax_group_id.l10n_ar_vat_afip_code') if self.move_id._l10n_ar_include_vat() else False
|
||||
price_digits = 10**self.env['decimal.precision'].precision_get('Product Price')
|
||||
if not included_taxes:
|
||||
price_unit = self.tax_ids.compute_all(
|
||||
self.price_unit * price_digits, invoice.currency_id, 1.0, self.product_id, invoice.partner_id)
|
||||
price_unit = price_unit['total_excluded'] / price_digits
|
||||
price_subtotal = self.price_subtotal
|
||||
include_vat = invoice._l10n_ar_include_vat()
|
||||
|
||||
AccountTax = self.env['account.tax']
|
||||
base_line = invoice._prepare_product_base_line_for_taxes_computation(self)
|
||||
if include_vat:
|
||||
base_line['tax_ids'] = self.tax_ids.filtered('tax_group_id.l10n_ar_vat_afip_code')
|
||||
AccountTax._add_tax_details_in_base_line(base_line, self.company_id, rounding_method='round_globally')
|
||||
|
||||
tax_details = base_line['tax_details']
|
||||
discount = base_line['discount']
|
||||
price_unit = base_line['price_unit']
|
||||
quantity = base_line['quantity']
|
||||
if include_vat:
|
||||
raw_total = tax_details['raw_total_included_currency']
|
||||
else:
|
||||
price_unit = included_taxes.compute_all(
|
||||
self.price_unit * price_digits, invoice.currency_id, 1.0, self.product_id, invoice.partner_id)['total_included'] / price_digits
|
||||
price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
|
||||
price_subtotal = included_taxes.compute_all(
|
||||
price, invoice.currency_id, self.quantity, self.product_id, invoice.partner_id)['total_included']
|
||||
price_net = price_unit * (1 - (self.discount or 0.0) / 100.0)
|
||||
raw_total = tax_details['raw_total_excluded_currency']
|
||||
|
||||
if discount == 100.0:
|
||||
price_subtotal_before_discount = price_unit * quantity
|
||||
else:
|
||||
price_subtotal_before_discount = raw_total / (1 - discount / 100.0)
|
||||
|
||||
if quantity:
|
||||
price_unit = price_subtotal_before_discount / quantity
|
||||
price_net = raw_total / quantity
|
||||
else:
|
||||
price_unit = 0.0
|
||||
price_net = 0.0
|
||||
|
||||
return {
|
||||
'price_unit': price_unit,
|
||||
'price_subtotal': price_subtotal,
|
||||
'price_subtotal': invoice.currency_id.round(raw_total),
|
||||
'price_net': price_net,
|
||||
}
|
||||
|
||||
# TODO: deprecated, remove in master
|
||||
def get_column_to_exclude_for_colspan_calculation(self, taxes=None):
|
||||
return super().get_column_to_exclude_for_colspan_calculation(taxes)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models, api
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountTaxGroup(models.Model):
|
||||
|
||||
_inherit = 'account.tax.group'
|
||||
|
||||
# values from http://www.afip.gob.ar/fe/documentos/otros_Tributos.xlsx
|
||||
|
|
@ -17,7 +17,7 @@ class AccountTaxGroup(models.Model):
|
|||
('08', '08 - Municipal Taxes Perceptions'),
|
||||
('09', '09 - Other Perceptions'),
|
||||
('99', '99 - Others'),
|
||||
], string='Tribute AFIP Code', index=True, readonly=True)
|
||||
], string='Tribute ARCA Code', index=True, readonly=True)
|
||||
# values from http://www.afip.gob.ar/fe/documentos/OperacionCondicionIVA.xls
|
||||
l10n_ar_vat_afip_code = fields.Selection([
|
||||
('0', 'Not Applicable'),
|
||||
|
|
@ -29,4 +29,22 @@ class AccountTaxGroup(models.Model):
|
|||
('6', '27%'),
|
||||
('8', '5%'),
|
||||
('9', '2,5%'),
|
||||
], string='VAT AFIP Code', index=True, readonly=True)
|
||||
], string='VAT ARCA Code', index=True, readonly=True)
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def check_uninstall_required(self):
|
||||
"""
|
||||
Make sure we don't uninstall a required tax group
|
||||
"""
|
||||
ar_companies = self.filtered(lambda g: g.company_id.chart_template.startswith('ar_')).mapped('company_id')
|
||||
profits_tax_group_ids = self.env['ir.model.data'].search([
|
||||
('name', 'in', [f'{company.id}_tax_group_percepcion_ganancias' for company in ar_companies]),
|
||||
('module', '=', 'account'),
|
||||
]).mapped('res_id')
|
||||
if profit_tax_groups_to_be_deleted := self.filtered(lambda g: g.id in profits_tax_group_ids):
|
||||
raise UserError(
|
||||
_(
|
||||
"The tax group '%s' can't be removed, since it is required in the Argentinian localization.",
|
||||
profit_tax_groups_to_be_deleted[0].name,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
from odoo import models, fields
|
||||
|
||||
|
||||
class L10nArAfipResponsibilityType(models.Model):
|
||||
|
||||
class L10n_ArAfipResponsibilityType(models.Model):
|
||||
_name = 'l10n_ar.afip.responsibility.type'
|
||||
_description = 'AFIP Responsibility Type'
|
||||
|
||||
_description = 'ARCA Responsibility Type'
|
||||
_order = 'sequence'
|
||||
|
||||
name = fields.Char(required=True, index='trigram')
|
||||
|
|
@ -14,5 +14,5 @@ class L10nArAfipResponsibilityType(models.Model):
|
|||
code = fields.Char(required=True, index=True)
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
_sql_constraints = [('name', 'unique(name)', 'Name must be unique!'),
|
||||
('code', 'unique(code)', 'Code must be unique!')]
|
||||
_name_uniq = models.Constraint('unique(name)', 'Name must be unique!')
|
||||
_code_uniq = models.Constraint('unique(code)', 'Code must be unique!')
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@ from odoo import models, api, fields, _
|
|||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class L10nLatamDocumentType(models.Model):
|
||||
|
||||
class L10n_LatamDocumentType(models.Model):
|
||||
_inherit = 'l10n_latam.document.type'
|
||||
|
||||
l10n_ar_letter = fields.Selection(
|
||||
selection='_get_l10n_ar_letters',
|
||||
string='Letters',
|
||||
help='Letters defined by the AFIP that can be used to identify the'
|
||||
help='Letters defined by the ARCA that can be used to identify the'
|
||||
' documents presented to the government and that depends on the'
|
||||
' operation type, the responsibility of both the issuer and the'
|
||||
' receptor of the document')
|
||||
|
|
@ -44,15 +43,19 @@ class L10nLatamDocumentType(models.Model):
|
|||
if not document_number:
|
||||
return False
|
||||
|
||||
msg = "'%s' " + _("is not a valid value for") + " '%s'.<br/>%s"
|
||||
|
||||
if not self.code:
|
||||
return document_number
|
||||
|
||||
# Import Dispatch Number Validator
|
||||
if self.code in ['66', '67']:
|
||||
if len(document_number) != 16:
|
||||
raise UserError(msg % (document_number, self.name, _('The number of import Dispatch must be 16 characters')))
|
||||
raise UserError(
|
||||
_(
|
||||
"%(value)s is not a valid value for %(field)s.\nThe number of import Dispatch must be 16 characters.",
|
||||
value=document_number,
|
||||
field=self.name,
|
||||
),
|
||||
)
|
||||
return document_number
|
||||
|
||||
# Invoice Number Validator (For Eg: 123-123)
|
||||
|
|
@ -68,9 +71,12 @@ class L10nLatamDocumentType(models.Model):
|
|||
failed = True
|
||||
document_number = '{:>05s}-{:>08s}'.format(pos, number)
|
||||
if failed:
|
||||
raise UserError(msg % (document_number, self.name, _(
|
||||
'The document number must be entered with a dash (-) and a maximum of 5 characters for the first part'
|
||||
'and 8 for the second. The following are examples of valid numbers:\n* 1-1\n* 0001-00000001'
|
||||
'\n* 00001-00000001')))
|
||||
raise UserError(
|
||||
_(
|
||||
"%(value)s is not a valid value for %(field)s.\nThe document number must be entered with a dash (-) and a maximum of 5 characters for the first part and 8 for the second. The following are examples of valid numbers:\n* 1-1\n* 0001-00000001\n* 00001-00000001",
|
||||
value=document_number,
|
||||
field=self.name,
|
||||
),
|
||||
)
|
||||
|
||||
return document_number
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
from odoo import models, fields
|
||||
|
||||
|
||||
class L10nLatamIdentificationType(models.Model):
|
||||
|
||||
class L10n_LatamIdentificationType(models.Model):
|
||||
_inherit = "l10n_latam.identification.type"
|
||||
|
||||
l10n_ar_afip_code = fields.Char("AFIP Code")
|
||||
l10n_ar_afip_code = fields.Char("ARCA Code")
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
|
||||
_inherit = "res.company"
|
||||
|
||||
l10n_ar_gross_income_number = fields.Char(
|
||||
|
|
@ -33,12 +33,15 @@ class ResCompany(models.Model):
|
|||
def _localization_use_documents(self):
|
||||
""" Argentinean localization use documents """
|
||||
self.ensure_one()
|
||||
return self.account_fiscal_country_id.code == "AR" or super()._localization_use_documents()
|
||||
return self.chart_template in {'ar_base', 'ar_ex', 'ar_ri'} or self.account_fiscal_country_id.code == "AR" or super()._localization_use_documents()
|
||||
|
||||
@api.constrains('l10n_ar_afip_responsibility_type_id')
|
||||
def _check_accounting_info(self):
|
||||
""" Do not let to change the AFIP Responsibility of the company if there is already installed a chart of
|
||||
account and if there has accounting entries """
|
||||
if self.env['account.chart.template'].existing_accounting(self):
|
||||
raise ValidationError(_(
|
||||
'Could not change the AFIP Responsibility of this company because there are already accounting entries.'))
|
||||
def write(self, vals):
|
||||
if 'l10n_ar_afip_responsibility_type_id' in vals:
|
||||
for company in self:
|
||||
if vals['l10n_ar_afip_responsibility_type_id'] != company.l10n_ar_afip_responsibility_type_id.id and company.sudo()._existing_accounting():
|
||||
raise UserError(_('Could not change the ARCA Responsibility of this company because there are already accounting entries.'))
|
||||
|
||||
return super().write(vals)
|
||||
|
||||
def _is_latam(self):
|
||||
return super()._is_latam() or self.country_code == 'AR'
|
||||
|
|
|
|||
|
|
@ -4,16 +4,15 @@ from odoo import fields, models
|
|||
|
||||
|
||||
class ResCountry(models.Model):
|
||||
|
||||
_inherit = 'res.country'
|
||||
|
||||
l10n_ar_afip_code = fields.Char('AFIP Code', size=3, help='This code will be used on electronic invoice')
|
||||
l10n_ar_afip_code = fields.Char('ARCA Code', size=3, help='This code will be used on electronic invoice')
|
||||
l10n_ar_natural_vat = fields.Char(
|
||||
'Natural Person VAT', size=11, help="Generic VAT number defined by AFIP in order to recognize partners from"
|
||||
'Natural Person VAT', size=11, help="Generic VAT number defined by ARCA in order to recognize partners from"
|
||||
" this country that are natural persons")
|
||||
l10n_ar_legal_entity_vat = fields.Char(
|
||||
'Legal Entity VAT', size=11, help="Generic VAT number defined by AFIP in order to recognize partners from this"
|
||||
'Legal Entity VAT', size=11, help="Generic VAT number defined by ARCA in order to recognize partners from this"
|
||||
" country that are legal entity")
|
||||
l10n_ar_other_vat = fields.Char(
|
||||
'Other VAT', size=11, help="Generic VAT number defined by AFIP in order to recognize partners from this"
|
||||
'Other VAT', size=11, help="Generic VAT number defined by ARCA in order to recognize partners from this"
|
||||
" country that are not natural persons or legal entities")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ from odoo import fields, models
|
|||
|
||||
|
||||
class ResCurrency(models.Model):
|
||||
|
||||
_inherit = "res.currency"
|
||||
|
||||
l10n_ar_afip_code = fields.Char('AFIP Code', size=4, help='This code will be used on electronic invoice')
|
||||
l10n_ar_afip_code = fields.Char('ARCA Code', size=4, help='This code will be used on electronic invoice')
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ _logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
|
||||
_inherit = 'res.partner'
|
||||
|
||||
l10n_ar_vat = fields.Char(
|
||||
|
|
@ -22,15 +21,11 @@ class ResPartner(models.Model):
|
|||
l10n_ar_gross_income_number = fields.Char('Gross Income Number')
|
||||
l10n_ar_gross_income_type = fields.Selection(
|
||||
[('multilateral', 'Multilateral'), ('local', 'Local'), ('exempt', 'Exempt')],
|
||||
'Gross Income Type', help='Type of gross income: exempt, local, multilateral')
|
||||
'Gross Income Type', help='Argentina: Type of gross income: exempt, local, multilateral.')
|
||||
l10n_ar_afip_responsibility_type_id = fields.Many2one(
|
||||
'l10n_ar.afip.responsibility.type', string='AFIP Responsibility Type', index='btree_not_null', help='Defined by AFIP to'
|
||||
'l10n_ar.afip.responsibility.type', string='ARCA Responsibility Type', index='btree_not_null', help='Defined by ARCA to'
|
||||
' identify the type of responsibilities that a person or a legal entity could have and that impacts in the'
|
||||
' type of operations and requirements they need.')
|
||||
l10n_ar_special_purchase_document_type_ids = fields.Many2many(
|
||||
'l10n_latam.document.type', 'res_partner_document_type_rel', 'partner_id', 'document_type_id',
|
||||
string='Other Purchase Documents', help='Set here if this partner can issue other documents further than'
|
||||
' invoices, credit notes and debit notes')
|
||||
|
||||
@api.depends('l10n_ar_vat')
|
||||
def _compute_l10n_ar_formatted_vat(self):
|
||||
|
|
@ -57,16 +52,20 @@ class ResPartner(models.Model):
|
|||
remaining = self - recs_ar_vat
|
||||
remaining.l10n_ar_vat = False
|
||||
|
||||
@api.constrains('vat', 'l10n_latam_identification_type_id')
|
||||
def check_vat(self):
|
||||
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. """
|
||||
# NOTE by the moment we include the CUIT (VAT AR) validation also here because we extend the messages
|
||||
# errors to be more friendly to the user. In a future when Odoo improve the base_vat message errors
|
||||
# we can change this method and use the base_vat.check_vat_ar method.s
|
||||
l10n_ar_partners = self.filtered(lambda p: p.l10n_latam_identification_type_id.l10n_ar_afip_code or p.country_code == 'AR')
|
||||
l10n_ar_partners.l10n_ar_identification_validation()
|
||||
return super(ResPartner, self - l10n_ar_partners).check_vat()
|
||||
l10n_ar_partners = self.filtered(lambda p: p.vat and (
|
||||
p.l10n_latam_identification_type_id.l10n_ar_afip_code
|
||||
or p.country_code == 'AR'
|
||||
))
|
||||
for partner in l10n_ar_partners:
|
||||
if id_number := partner._get_id_number_sanitize():
|
||||
partner.vat = str(id_number)
|
||||
if validation == 'error':
|
||||
partner._l10n_ar_identification_validation()
|
||||
|
||||
return super(ResPartner, self - l10n_ar_partners)._run_check_identification(validation=validation)
|
||||
|
||||
@api.model
|
||||
def _commercial_fields(self):
|
||||
|
|
@ -81,9 +80,15 @@ class ResPartner(models.Model):
|
|||
This method can be used to validate is the VAT is proper defined in the partner """
|
||||
self.ensure_one()
|
||||
if not self.l10n_ar_vat:
|
||||
raise UserError(_('No VAT configured for partner [%i] %s') % (self.id, self.name))
|
||||
raise UserError(_('No VAT configured for partner [%i] %s', self.id, self.name))
|
||||
return self.l10n_ar_vat
|
||||
|
||||
def _get_frontend_writable_fields(self):
|
||||
frontend_writable_fields = super()._get_frontend_writable_fields()
|
||||
frontend_writable_fields.add('l10n_ar_afip_responsibility_type_id')
|
||||
|
||||
return frontend_writable_fields
|
||||
|
||||
def _get_validation_module(self):
|
||||
self.ensure_one()
|
||||
if self.l10n_latam_identification_type_id.l10n_ar_afip_code in ['80', '86']:
|
||||
|
|
@ -91,7 +96,7 @@ class ResPartner(models.Model):
|
|||
elif self.l10n_latam_identification_type_id.l10n_ar_afip_code == '96':
|
||||
return stdnum.ar.dni
|
||||
|
||||
def l10n_ar_identification_validation(self):
|
||||
def _l10n_ar_identification_validation(self):
|
||||
for rec in self.filtered('vat'):
|
||||
try:
|
||||
module = rec._get_validation_module()
|
||||
|
|
@ -110,6 +115,9 @@ class ResPartner(models.Model):
|
|||
raise ValidationError(_('Invalid length for "%s"', rec.l10n_latam_identification_type_id.name))
|
||||
except module.InvalidFormat:
|
||||
raise ValidationError(_('Only numbers allowed for "%s"', rec.l10n_latam_identification_type_id.name))
|
||||
except module.InvalidComponent:
|
||||
valid_cuit = ('20', '23', '24', '27', '30', '33', '34', '50', '51', '55')
|
||||
raise ValidationError(_('CUIT number must be prefixed with one of the following: %s', ', '.join(valid_cuit)))
|
||||
except Exception as error:
|
||||
raise ValidationError(repr(error))
|
||||
|
||||
|
|
@ -124,5 +132,5 @@ class ResPartner(models.Model):
|
|||
res = int(stdnum.ar.cuit.compact(self.vat))
|
||||
else:
|
||||
id_number = re.sub('[^0-9]', '', self.vat)
|
||||
res = int(id_number)
|
||||
res = id_number and int(id_number)
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -1,36 +1,14 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
import logging
|
||||
|
||||
from stdnum.ar.cbu import validate
|
||||
|
||||
from odoo import models, api, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
try:
|
||||
from stdnum.ar.cbu import validate as validate_cbu
|
||||
except ImportError:
|
||||
import stdnum
|
||||
_logger.warning("stdnum.ar.cbu is avalaible from stdnum >= 1.6. The one installed is %s" % stdnum.__version__)
|
||||
|
||||
def validate_cbu(number):
|
||||
def _check_digit(number):
|
||||
"""Calculate the check digit."""
|
||||
weights = (3, 1, 7, 9)
|
||||
check = sum(int(n) * weights[i % 4] for i, n in enumerate(reversed(number)))
|
||||
return str((10 - check) % 10)
|
||||
number = stdnum.util.clean(number, ' -').strip()
|
||||
if len(number) != 22:
|
||||
raise ValidationError(_('Invalid Length'))
|
||||
if not number.isdigit():
|
||||
raise ValidationError(_('Invalid Format'))
|
||||
if _check_digit(number[:7]) != number[7]:
|
||||
raise ValidationError(_('Invalid Checksum'))
|
||||
if _check_digit(number[8:-1]) != number[-1]:
|
||||
raise ValidationError(_('Invalid Checksum'))
|
||||
return number
|
||||
|
||||
|
||||
class ResPartnerBank(models.Model):
|
||||
|
||||
_inherit = 'res.partner.bank'
|
||||
|
||||
@api.model
|
||||
|
|
@ -43,7 +21,7 @@ class ResPartnerBank(models.Model):
|
|||
@api.model
|
||||
def retrieve_acc_type(self, acc_number):
|
||||
try:
|
||||
validate_cbu(acc_number)
|
||||
validate(acc_number)
|
||||
except Exception:
|
||||
return super().retrieve_acc_type(acc_number)
|
||||
return 'cbu'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
# 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('ar_base')
|
||||
def _get_ar_base_template_data(self):
|
||||
return {
|
||||
'property_account_receivable_id': 'base_deudores_por_ventas',
|
||||
'property_account_payable_id': 'base_proveedores',
|
||||
'name': _('Generic Chart of Accounts Argentina Single Taxpayer / Basis'),
|
||||
'code_digits': '12',
|
||||
'sequence': 1,
|
||||
}
|
||||
|
||||
@template('ar_base', 'res.company')
|
||||
def _get_ar_base_res_company(self):
|
||||
return {
|
||||
self.env.company.id: {
|
||||
'account_fiscal_country_id': 'base.ar',
|
||||
'bank_account_code_prefix': '1.1.1.02.',
|
||||
'cash_account_code_prefix': '1.1.1.01.',
|
||||
'transfer_account_code_prefix': '6.0.00.00.',
|
||||
'account_default_pos_receivable_account_id': 'base_deudores_por_ventas_pos',
|
||||
'income_currency_exchange_account_id': 'base_diferencias_de_cambio',
|
||||
'expense_currency_exchange_account_id': 'base_diferencias_de_cambio',
|
||||
'expense_account_id': 'base_compra_mercaderia',
|
||||
'income_account_id': 'base_venta_de_mercaderia',
|
||||
'account_stock_journal_id': 'inventory_valuation',
|
||||
'account_stock_valuation_id': 'base_mercaderia_reventa',
|
||||
},
|
||||
}
|
||||
|
||||
@template('ar_base', 'account.journal')
|
||||
def _get_ar_account_journal(self):
|
||||
""" In case of an Argentinean CoA, we modify the default values of the sales journal to be a preprinted journal"""
|
||||
return {
|
||||
'sale': {
|
||||
"name": self.env._("Ventas Preimpreso"),
|
||||
"code": "0001",
|
||||
"l10n_ar_afip_pos_number": 1,
|
||||
"l10n_ar_afip_pos_partner_id": self.env.company.partner_id.id,
|
||||
"l10n_ar_afip_pos_system": 'II_IM',
|
||||
"refund_sequence": False,
|
||||
},
|
||||
}
|
||||
|
||||
@template('ar_base', 'account.account')
|
||||
def _get_ar_base_account_account(self):
|
||||
return {
|
||||
'base_mercaderia_reventa': {
|
||||
'account_stock_expense_id': 'base_compra_mercaderia',
|
||||
'account_stock_variation_id': 'base_variacion_mercaderia_reventa',
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# 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('ar_ex')
|
||||
def _get_ar_ex_template_data(self):
|
||||
return {
|
||||
'name': _('Argentine Generic Chart of Accounts for Exempt Individuals'),
|
||||
'parent': 'ar_base',
|
||||
'code_digits': '12',
|
||||
'sequence': 2,
|
||||
}
|
||||
|
||||
@template('ar_ex', 'res.company')
|
||||
def _get_ar_ex_res_company(self):
|
||||
return {
|
||||
self.env.company.id: {
|
||||
'account_fiscal_country_id': 'base.ar',
|
||||
'bank_account_code_prefix': '1.1.1.02.',
|
||||
'cash_account_code_prefix': '1.1.1.01.',
|
||||
'transfer_account_code_prefix': '6.0.00.00.',
|
||||
'account_default_pos_receivable_account_id': 'base_deudores_por_ventas_pos',
|
||||
'income_currency_exchange_account_id': 'base_diferencias_de_cambio',
|
||||
'expense_currency_exchange_account_id': 'base_diferencias_de_cambio',
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# 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('ar_ri')
|
||||
def _get_ar_ri_template_data(self):
|
||||
return {
|
||||
'name': _('Argentine Generic Chart of Accounts for Registered Accountants'),
|
||||
'parent': 'ar_ex',
|
||||
'code_digits': '12',
|
||||
'sequence': 0,
|
||||
}
|
||||
|
||||
@template('ar_ri', 'res.company')
|
||||
def _get_ar_ri_res_company(self):
|
||||
return {
|
||||
self.env.company.id: {
|
||||
'account_fiscal_country_id': 'base.ar',
|
||||
'bank_account_code_prefix': '1.1.1.02.',
|
||||
'cash_account_code_prefix': '1.1.1.01.',
|
||||
'transfer_account_code_prefix': '6.0.00.00.',
|
||||
'account_default_pos_receivable_account_id': 'base_deudores_por_ventas_pos',
|
||||
'income_currency_exchange_account_id': 'base_diferencias_de_cambio',
|
||||
'expense_currency_exchange_account_id': 'base_diferencias_de_cambio',
|
||||
'account_sale_tax_id': 'ri_tax_vat_21_ventas',
|
||||
'account_purchase_tax_id': 'ri_tax_vat_21_compras',
|
||||
},
|
||||
}
|
||||
|
|
@ -2,8 +2,7 @@
|
|||
from odoo import fields, models
|
||||
|
||||
|
||||
class Uom(models.Model):
|
||||
|
||||
class UomUom(models.Model):
|
||||
_inherit = 'uom.uom'
|
||||
|
||||
l10n_ar_afip_code = fields.Char('AFIP Code', help='This code will be used on electronic invoice')
|
||||
l10n_ar_afip_code = fields.Char('Code', help='Argentina: This code will be used on electronic invoice.')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue