mirror of
https://github.com/bringout/oca-ocb-l10n_americas.git
synced 2026-04-26 14:12:06 +02:00
Initial commit: L10N_Americas packages
This commit is contained in:
commit
12b27ce151
714 changed files with 79328 additions and 0 deletions
13
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/__init__.py
Normal file
13
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/__init__.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from . import account_chart_template
|
||||
from . import account_move
|
||||
from . import account_move_line
|
||||
from . import account_tax
|
||||
from . import l10n_latam_document_type
|
||||
from . import res_company
|
||||
from . import res_country
|
||||
from . import res_currency
|
||||
from . import res_partner
|
||||
from . import res_partner_bank
|
||||
from . import uom_uom
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class AccountChartTemplate(models.Model):
|
||||
_inherit = 'account.chart.template'
|
||||
|
||||
def _load(self, company):
|
||||
""" Set tax calculation rounding method required in Chilean localization"""
|
||||
res = super()._load(company)
|
||||
if company.account_fiscal_country_id.code == 'CL':
|
||||
company.write({'tax_calculation_rounding_method': 'round_globally'})
|
||||
return res
|
||||
280
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/account_move.py
Normal file
280
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/account_move.py
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools.misc import formatLang
|
||||
from odoo.tools.float_utils import float_repr, float_round
|
||||
|
||||
SII_VAT = '60805000-0'
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
partner_id_vat = fields.Char(related='partner_id.vat', string='VAT No')
|
||||
l10n_latam_internal_type = fields.Selection(
|
||||
related='l10n_latam_document_type_id.internal_type', string='L10n Latam Internal Type')
|
||||
|
||||
def _get_l10n_latam_documents_domain(self):
|
||||
self.ensure_one()
|
||||
if self.journal_id.company_id.account_fiscal_country_id != self.env.ref('base.cl') or not \
|
||||
self.journal_id.l10n_latam_use_documents:
|
||||
return super()._get_l10n_latam_documents_domain()
|
||||
if self.journal_id.type == 'sale':
|
||||
domain = [('country_id.code', '=', 'CL')]
|
||||
if self.move_type in ['in_invoice', 'out_invoice']:
|
||||
domain += [('internal_type', 'in', ['invoice', 'debit_note', 'invoice_in'])]
|
||||
elif self.move_type in ['in_refund', 'out_refund']:
|
||||
domain += [('internal_type', '=', 'credit_note')]
|
||||
if self.company_id.partner_id.l10n_cl_sii_taxpayer_type == '1':
|
||||
domain += [('code', '!=', '71')] # Companies with VAT Affected doesn't have "Boleta de honorarios Electrónica"
|
||||
return domain
|
||||
if self.move_type == 'in_refund':
|
||||
internal_types_domain = ('internal_type', '=', 'credit_note')
|
||||
else:
|
||||
internal_types_domain = ('internal_type', 'in', ['invoice', 'debit_note', 'invoice_in'])
|
||||
domain = [
|
||||
('country_id.code', '=', 'CL'),
|
||||
internal_types_domain,
|
||||
]
|
||||
if self.partner_id.l10n_cl_sii_taxpayer_type == '1' and self.partner_id_vat != '60805000-0':
|
||||
domain += [('code', 'not in', ['39', '70', '71', '914', '911'])]
|
||||
elif self.partner_id.l10n_cl_sii_taxpayer_type == '1' and self.partner_id_vat == '60805000-0':
|
||||
domain += [('code', 'not in', ['39', '70', '71'])]
|
||||
elif self.partner_id.l10n_cl_sii_taxpayer_type == '2':
|
||||
domain += [('code', '=', '71')]
|
||||
elif self.partner_id.l10n_cl_sii_taxpayer_type == '3':
|
||||
domain += [('code', 'in', ['35', '38', '39', '41', '56', '61'])]
|
||||
elif self.partner_id.country_id.code != 'CL' or self.partner_id.l10n_cl_sii_taxpayer_type == '4':
|
||||
domain += [('code', '=', '46')]
|
||||
else:
|
||||
domain += [('code', 'in', [])]
|
||||
return domain
|
||||
|
||||
def _check_document_types_post(self):
|
||||
for rec in self.filtered(
|
||||
lambda r: r.company_id.account_fiscal_country_id.code == "CL" and
|
||||
r.journal_id.type in ['sale', 'purchase']):
|
||||
tax_payer_type = rec.partner_id.l10n_cl_sii_taxpayer_type
|
||||
vat = rec.partner_id.vat
|
||||
country_id = rec.partner_id.country_id
|
||||
latam_document_type_code = rec.l10n_latam_document_type_id.code
|
||||
if (rec.journal_id.type == 'purchase' and tax_payer_type == '4' and country_id.code != 'CL' and
|
||||
latam_document_type_code == '61' and
|
||||
'46' in rec.l10n_cl_reference_ids.mapped('l10n_cl_reference_doc_type_selection')):
|
||||
continue
|
||||
if (not tax_payer_type or not vat) and (country_id.code == "CL" and latam_document_type_code
|
||||
and latam_document_type_code not in ['35', '38', '39', '41']):
|
||||
raise ValidationError(_('Tax payer type and vat number are mandatory for this type of '
|
||||
'document. Please set the current tax payer type of this customer'))
|
||||
if rec.journal_id.type == 'sale' and rec.journal_id.l10n_latam_use_documents:
|
||||
if country_id.code != "CL":
|
||||
if not ((tax_payer_type == '4' and latam_document_type_code in ['110', '111', '112']) or (
|
||||
tax_payer_type == '3' and latam_document_type_code in ['39', '41', '61', '56'])):
|
||||
raise ValidationError(_(
|
||||
'Document types for foreign customers must be export type (codes 110, 111 or 112) or you should define the customer as an end consumer and use receipts (codes 39 or 41)'))
|
||||
if rec.journal_id.type == 'purchase' and rec.journal_id.l10n_latam_use_documents:
|
||||
if vat != SII_VAT and latam_document_type_code == '914':
|
||||
raise ValidationError(_('The DIN document is intended to be used only with RUT 60805000-0'
|
||||
' (Tesorería General de La República)'))
|
||||
if not tax_payer_type or not vat:
|
||||
if country_id.code == "CL" and latam_document_type_code not in [
|
||||
'35', '38', '39', '41']:
|
||||
raise ValidationError(_('Tax payer type and vat number are mandatory for this type of '
|
||||
'document. Please set the current tax payer type of this supplier'))
|
||||
if tax_payer_type == '2' and latam_document_type_code not in ['70', '71', '56', '61']:
|
||||
raise ValidationError(_('The tax payer type of this supplier is incorrect for the selected type'
|
||||
' of document.'))
|
||||
if tax_payer_type in ['1', '3']:
|
||||
if latam_document_type_code in ['70', '71']:
|
||||
raise ValidationError(_('The tax payer type of this supplier is not entitled to deliver '
|
||||
'fees documents'))
|
||||
if latam_document_type_code in ['110', '111', '112']:
|
||||
raise ValidationError(_('The tax payer type of this supplier is not entitled to deliver '
|
||||
'imports documents'))
|
||||
if (tax_payer_type == '4' or country_id.code != "CL") and latam_document_type_code != '46':
|
||||
raise ValidationError(_('You need a journal without the use of documents for foreign '
|
||||
'suppliers'))
|
||||
|
||||
@api.onchange('journal_id')
|
||||
def _l10n_cl_onchange_journal(self):
|
||||
if self.company_id.country_id.code == 'CL':
|
||||
self.l10n_latam_document_type_id = False
|
||||
|
||||
def _post(self, soft=True):
|
||||
self._check_document_types_post()
|
||||
return super()._post(soft)
|
||||
|
||||
def _l10n_cl_get_formatted_sequence(self, number=0):
|
||||
return '%s %06d' % (self.l10n_latam_document_type_id.doc_code_prefix, number)
|
||||
|
||||
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 6 padding number """
|
||||
if self.journal_id.l10n_latam_use_documents and self.company_id.account_fiscal_country_id.code == "CL":
|
||||
if self.l10n_latam_document_type_id:
|
||||
return self._l10n_cl_get_formatted_sequence()
|
||||
return super()._get_starting_sequence()
|
||||
|
||||
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 == "CL" and self.l10n_latam_use_documents:
|
||||
where_string = where_string.replace('journal_id = %(journal_id)s AND', '')
|
||||
where_string += ' AND l10n_latam_document_type_id = %(l10n_latam_document_type_id)s AND ' \
|
||||
'company_id = %(company_id)s AND move_type IN %(move_type)s'
|
||||
|
||||
param['company_id'] = self.company_id.id or False
|
||||
param['l10n_latam_document_type_id'] = self.l10n_latam_document_type_id.id or 0
|
||||
param['move_type'] = (('in_invoice', 'in_refund') if
|
||||
self.l10n_latam_document_type_id._is_doc_type_vendor() else ('out_invoice', 'out_refund'))
|
||||
return where_string, param
|
||||
|
||||
def _get_name_invoice_report(self):
|
||||
self.ensure_one()
|
||||
if self.l10n_latam_use_documents and self.company_id.account_fiscal_country_id.code == 'CL':
|
||||
return 'l10n_cl.report_invoice_document'
|
||||
return super()._get_name_invoice_report()
|
||||
|
||||
def _format_lang_totals(self, value, currency):
|
||||
return formatLang(self.env, value, currency_obj=currency)
|
||||
|
||||
def _l10n_cl_get_invoice_totals_for_report(self):
|
||||
self.ensure_one()
|
||||
include_sii = self._l10n_cl_include_sii()
|
||||
|
||||
base_lines = self.line_ids.filtered(lambda x: x.display_type == 'product')
|
||||
tax_lines = self.line_ids.filtered(lambda x: x.display_type == 'tax')
|
||||
|
||||
base_line_vals_list = [x._convert_to_tax_base_line_dict() for x in base_lines]
|
||||
if include_sii:
|
||||
for vals in base_line_vals_list:
|
||||
vals['taxes'] = vals['taxes'].flatten_taxes_hierarchy().filtered(lambda tax: tax.l10n_cl_sii_code != 14)
|
||||
|
||||
tax_line_vals_list = [x._convert_to_tax_line_dict() for x in tax_lines]
|
||||
if include_sii:
|
||||
tax_line_vals_list = [x for x in tax_line_vals_list if x['tax_repartition_line'].tax_id.l10n_cl_sii_code != 14]
|
||||
|
||||
tax_totals = self.env['account.tax']._prepare_tax_totals(
|
||||
base_line_vals_list,
|
||||
self.currency_id,
|
||||
tax_lines=tax_line_vals_list,
|
||||
)
|
||||
|
||||
if include_sii:
|
||||
tax_totals['amount_total'] = self.amount_total
|
||||
tax_totals['amount_untaxed'] = self.currency_id.round(
|
||||
tax_totals['amount_total'] - sum([x['tax_amount'] for x in tax_line_vals_list if 'tax_amount' in x]))
|
||||
tax_totals['formatted_amount_total'] = formatLang(self.env, tax_totals['amount_total'], currency_obj=self.currency_id)
|
||||
tax_totals['formatted_amount_untaxed'] = formatLang(self.env, tax_totals['amount_untaxed'], currency_obj=self.currency_id)
|
||||
if tax_totals['subtotals']:
|
||||
tax_totals['subtotals'][0]['formatted_amount'] = tax_totals['formatted_amount_untaxed']
|
||||
|
||||
return tax_totals
|
||||
|
||||
def _l10n_cl_include_sii(self):
|
||||
self.ensure_one()
|
||||
return self.l10n_latam_document_type_id.code in ['39', '41', '110', '111', '112', '34']
|
||||
|
||||
def _is_manual_document_number(self):
|
||||
if self.journal_id.company_id.country_id.code == 'CL':
|
||||
return self.journal_id.type == 'purchase' and not self.l10n_latam_document_type_id._is_doc_type_vendor()
|
||||
return super()._is_manual_document_number()
|
||||
|
||||
def _l10n_cl_get_amounts(self):
|
||||
"""
|
||||
This method is used to calculate the amount and taxes required in the Chilean localization electronic documents.
|
||||
"""
|
||||
self.ensure_one()
|
||||
global_discounts = self.invoice_line_ids.filtered(lambda x: x.price_subtotal < 0)
|
||||
export = self.l10n_latam_document_type_id._is_doc_type_export()
|
||||
main_currency = self.company_id.currency_id if not export else self.currency_id
|
||||
key_main_currency = 'amount_currency' if export else 'balance'
|
||||
sign_main_currency = -1 if self.move_type == 'out_invoice' else 1
|
||||
currency_round_main_currency = self.currency_id if export else self.company_id.currency_id
|
||||
currency_round_other_currency = self.company_id.currency_id if export else self.currency_id
|
||||
total_amount_main_currency = currency_round_main_currency.round(self.amount_total) if export \
|
||||
else (currency_round_main_currency.round(abs(self.amount_total_signed)))
|
||||
other_currency = self.currency_id != self.company_id.currency_id
|
||||
values = {
|
||||
'main_currency': main_currency,
|
||||
'vat_amount': 0,
|
||||
'subtotal_amount_taxable': 0,
|
||||
'subtotal_amount_exempt': 0, 'total_amount': total_amount_main_currency,
|
||||
'main_currency_round': currency_round_main_currency.decimal_places,
|
||||
'main_currency_name': self._l10n_cl_normalize_currency_name(
|
||||
currency_round_main_currency.name) if export else False
|
||||
}
|
||||
vat_percent = 0
|
||||
|
||||
if other_currency:
|
||||
key_other_currency = 'balance' if export else 'amount_currency'
|
||||
values['second_currency'] = {
|
||||
'subtotal_amount_taxable': 0,
|
||||
'subtotal_amount_exempt': 0,
|
||||
'vat_amount': 0,
|
||||
'total_amount': currency_round_other_currency.round(abs(self.amount_total_signed)) \
|
||||
if export else currency_round_other_currency.round(self.amount_total),
|
||||
'round_currency': currency_round_other_currency.decimal_places,
|
||||
'name': self._l10n_cl_normalize_currency_name(currency_round_other_currency.name),
|
||||
'rate': round(abs(self.amount_total_signed) / self.amount_total, 4),
|
||||
}
|
||||
for line in self.line_ids:
|
||||
if line.tax_line_id and line.tax_line_id.l10n_cl_sii_code == 14:
|
||||
values['vat_amount'] += line[key_main_currency] * sign_main_currency
|
||||
if other_currency:
|
||||
values['second_currency']['vat_amount'] += line[key_other_currency] * sign_main_currency # amount_currency behaves as balance
|
||||
vat_percent = line.tax_line_id.amount if line.tax_line_id.amount > vat_percent else vat_percent
|
||||
if line.display_type == 'product':
|
||||
if line.tax_ids.filtered(lambda x: x.l10n_cl_sii_code == 14):
|
||||
values['subtotal_amount_taxable'] += line[key_main_currency] * sign_main_currency
|
||||
if other_currency:
|
||||
values['second_currency']['subtotal_amount_taxable'] += line[key_other_currency] * sign_main_currency
|
||||
elif not line.tax_ids:
|
||||
values['subtotal_amount_exempt'] += line[key_main_currency] * sign_main_currency
|
||||
if other_currency:
|
||||
values['second_currency']['subtotal_amount_exempt'] += line[key_other_currency] * sign_main_currency
|
||||
values['global_discounts'] = []
|
||||
for gd in global_discounts:
|
||||
main_value = currency_round_main_currency.round(abs(gd.price_subtotal)) if \
|
||||
(not other_currency and not export) or (other_currency and export) else \
|
||||
currency_round_main_currency.round(abs(gd.balance))
|
||||
second_value = currency_round_other_currency.round(abs(gd.balance)) if other_currency and export else \
|
||||
currency_round_other_currency.round(abs(gd.price_subtotal))
|
||||
values['global_discounts'].append(
|
||||
{
|
||||
'name': gd.name,
|
||||
'global_discount_main_value': main_value,
|
||||
'global_discount_second_value': second_value if second_value != main_value else False,
|
||||
'tax_ids': gd.tax_ids,
|
||||
}
|
||||
)
|
||||
values['vat_percent'] = '%.2f' % vat_percent if vat_percent > 0 else False
|
||||
return values
|
||||
|
||||
def _l10n_cl_get_withholdings(self):
|
||||
"""
|
||||
This method calculates the section of withholding taxes, or 'other' taxes for the Chilean electronic invoices.
|
||||
These taxes are not VAT taxes in general; they are special taxes (for example, alcohol or sugar-added beverages,
|
||||
withholdings for meat processing, fuel, etc.
|
||||
The taxes codes used are included here:
|
||||
[15, 17, 18, 19, 24, 25, 26, 27, 271]
|
||||
http://www.sii.cl/declaraciones_juradas/ddjj_3327_3328/cod_otros_imp_retenc.pdf
|
||||
The need of the tax is not just the amount, but the code of the tax, the percentage amount and the amount
|
||||
:return:
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
tax = [{'tax_code': line.tax_line_id.l10n_cl_sii_code,
|
||||
'tax_name': line.tax_line_id.name,
|
||||
'tax_base': abs(sum(self.invoice_line_ids.filtered(
|
||||
lambda x: line.tax_line_id.l10n_cl_sii_code in x.tax_ids.mapped('l10n_cl_sii_code')).mapped(
|
||||
'balance'))),
|
||||
'tax_percent': abs(line.tax_line_id.amount),
|
||||
'tax_amount_currency': self.currency_id.round(abs(line.amount_currency)),
|
||||
'tax_amount': self.currency_id.round(abs(line.balance))} for line in self.line_ids.filtered(
|
||||
lambda x: x.tax_group_id.id in [
|
||||
self.env.ref('l10n_cl.tax_group_ila').id, self.env.ref('l10n_cl.tax_group_retenciones').id])]
|
||||
return tax
|
||||
|
||||
def _float_repr_float_round(self, value, decimal_places):
|
||||
return float_repr(float_round(value, decimal_places), decimal_places)
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models
|
||||
from odoo.tools.float_utils import float_repr
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
def _l10n_cl_prices_and_taxes(self):
|
||||
""" this method is preserved here to allow compatibility with old templates,
|
||||
Nevertheless it will be deprecated in future versions, since it had been replaced by
|
||||
the method _l10n_cl_get_line_amounts, which is the same method used to calculate
|
||||
the values for the XML (DTE) file
|
||||
"""
|
||||
self.ensure_one()
|
||||
invoice = self.move_id
|
||||
included_taxes = self.tax_ids.filtered(lambda x: x.l10n_cl_sii_code == 14) if self.move_id._l10n_cl_include_sii() else self.tax_ids
|
||||
if not included_taxes:
|
||||
price_unit = self.tax_ids.with_context(round=False).compute_all(
|
||||
self.price_unit, invoice.currency_id, 1.0, self.product_id, invoice.partner_id)
|
||||
price_unit = price_unit['total_excluded']
|
||||
price_subtotal = self.price_subtotal
|
||||
else:
|
||||
price_unit = included_taxes.compute_all(
|
||||
self.price_unit, invoice.currency_id, 1.0, self.product_id, invoice.partner_id)['total_included']
|
||||
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)
|
||||
return {
|
||||
'price_unit': price_unit,
|
||||
'price_subtotal': price_subtotal,
|
||||
'price_net': price_net
|
||||
}
|
||||
|
||||
def _l10n_cl_get_line_amounts(self):
|
||||
"""
|
||||
This method is used to calculate the amount and taxes of the lines required in the Chilean localization
|
||||
electronic documents.
|
||||
"""
|
||||
# If in this fix we should check for boletas, we have the following cases, and how this affects the xml
|
||||
# for facturas and boletas:
|
||||
|
||||
# 1. local invoice in same currency tax not included in price
|
||||
# 2. local invoice in same currency tax included in price (there is difference of -1 peso in amount_untaxed
|
||||
# and +1 peso in vat tax amount. The lines are OK
|
||||
# 3. local invoice in different currency tax not included in price
|
||||
# 4. local invoice in different currency tax include in price -> this is the most problematic case because
|
||||
# 5. foreign invoice in different currency (without tax)
|
||||
if self.display_type != 'product':
|
||||
return {
|
||||
'price_subtotal': 0,
|
||||
}
|
||||
line_sign = self.price_subtotal / abs(self.price_subtotal) if self.price_subtotal else 0
|
||||
domestic_invoice_other_currency = self.move_id.currency_id != self.move_id.company_id.currency_id and not \
|
||||
self.move_id.l10n_latam_document_type_id._is_doc_type_export()
|
||||
export = self.move_id.l10n_latam_document_type_id._is_doc_type_export()
|
||||
if not export:
|
||||
# This is to manage case 1, 2, 3 and 4
|
||||
# cases 1 and 2: domestic invoice in same currency and cases 3 and 4 with other currency
|
||||
main_currency = self.move_id.company_id.currency_id
|
||||
main_currency_field = 'balance'
|
||||
second_currency_field = 'price_subtotal'
|
||||
second_currency = self.currency_id
|
||||
main_currency_rate = 1
|
||||
second_currency_rate = abs(self.move_id.amount_total_signed) / self.move_id.amount_total if self.move_id.amount_total else 1
|
||||
inverse_rate = second_currency_rate if domestic_invoice_other_currency else main_currency_rate
|
||||
else:
|
||||
# This is to manage case 5 (export docs)
|
||||
main_currency = self.currency_id
|
||||
second_currency = self.move_id.company_id.currency_id
|
||||
main_currency_field = 'price_subtotal'
|
||||
second_currency_field = 'balance'
|
||||
inverse_rate = abs(self.move_id.amount_total_signed) / self.move_id.amount_total if self.move_id.amount_total else 1
|
||||
price_subtotal = abs(self[main_currency_field]) * line_sign
|
||||
if self.quantity and self.discount != 100.0:
|
||||
price_unit = (price_subtotal / abs(self.quantity)) / (1 - self.discount / 100)
|
||||
if self.move_id.l10n_latam_document_type_id._is_doc_type_electronic_ticket():
|
||||
price_item_document = (self.price_total / abs(self.quantity)) / (1 - self.discount / 100)
|
||||
price_line_document = self.price_total
|
||||
else:
|
||||
price_item_document = price_unit
|
||||
price_line_document = price_subtotal
|
||||
else:
|
||||
price_item_document = price_line_document = 0.0
|
||||
price_unit = self.price_unit
|
||||
|
||||
if self.discount == 100:
|
||||
price_before_discount = price_unit * self.quantity
|
||||
else:
|
||||
price_before_discount = price_subtotal / (1 - self.discount / 100)
|
||||
discount_amount = price_before_discount * self.discount / 100
|
||||
values = {
|
||||
'decimal_places': main_currency.decimal_places,
|
||||
'price_item': round(price_unit, 6),
|
||||
'price_item_document': round(price_item_document, 2),
|
||||
'price_line_document': price_line_document,
|
||||
'total_discount': main_currency.round(discount_amount),
|
||||
'price_subtotal': main_currency.round(price_subtotal),
|
||||
'exempt': bool(not self.tax_ids),
|
||||
'main_currency': main_currency,
|
||||
}
|
||||
if domestic_invoice_other_currency or export:
|
||||
price_subtotal_second = abs(self[second_currency_field]) * line_sign
|
||||
if self.quantity and self.discount != 100.0:
|
||||
price_unit_second = (price_subtotal_second / abs(self.quantity)) / (1 - self.discount / 100)
|
||||
else:
|
||||
price_unit_second = self.price_unit
|
||||
discount_amount_second = price_unit_second * self.quantity - price_subtotal_second
|
||||
values['second_currency'] = {
|
||||
'price': second_currency.round(price_unit_second),
|
||||
'currency_name': self.move_id._format_length(second_currency.name, 3),
|
||||
'conversion_rate': round(inverse_rate, 4),
|
||||
'amount_discount': second_currency.round(discount_amount_second),
|
||||
'total_amount': second_currency.round(price_subtotal_second),
|
||||
'round_currency': second_currency.decimal_places,
|
||||
}
|
||||
|
||||
values['line_description'] = '%s (%s: %s @ %s)' % (
|
||||
self.name,
|
||||
values['second_currency']['currency_name'],
|
||||
float_repr(values['second_currency']['price'], values['second_currency']['round_currency']),
|
||||
self.move_id._float_repr_float_round(values['second_currency']['conversion_rate'], values['second_currency']['round_currency']),
|
||||
) if values.get('second_currency') and not self.l10n_latam_document_type_id._is_doc_type_export() else self.name
|
||||
return values
|
||||
27
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/account_tax.py
Normal file
27
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/account_tax.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountTax(models.Model):
|
||||
_name = 'account.tax'
|
||||
_inherit = 'account.tax'
|
||||
|
||||
l10n_cl_sii_code = fields.Integer('SII Code', group_operator=False)
|
||||
|
||||
|
||||
class AccountTaxTemplate(models.Model):
|
||||
_name = 'account.tax.template'
|
||||
_inherit = 'account.tax.template'
|
||||
|
||||
l10n_cl_sii_code = fields.Integer('SII Code')
|
||||
|
||||
def _get_tax_vals(self, company, tax_template_to_tax):
|
||||
self.ensure_one()
|
||||
vals = super(AccountTaxTemplate, self)._get_tax_vals(company, tax_template_to_tax)
|
||||
vals.update({
|
||||
'l10n_cl_sii_code': self.l10n_cl_sii_code,
|
||||
})
|
||||
if self.tax_group_id:
|
||||
vals['tax_group_id'] = self.tax_group_id.id
|
||||
return vals
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class L10nLatamDocumentType(models.Model):
|
||||
|
||||
_inherit = 'l10n_latam.document.type'
|
||||
|
||||
internal_type = fields.Selection(
|
||||
selection_add=[
|
||||
('invoice', 'Invoices'),
|
||||
('invoice_in', 'Purchase Invoices'),
|
||||
('debit_note', 'Debit Notes'),
|
||||
('credit_note', 'Credit Notes'),
|
||||
('receipt_invoice', 'Receipt Invoice'),
|
||||
('stock_picking', 'Stock Delivery'),
|
||||
],
|
||||
)
|
||||
l10n_cl_active = fields.Boolean(
|
||||
'Active in localization', help='This boolean enables document to be included on invoicing')
|
||||
|
||||
def _format_document_number(self, document_number):
|
||||
""" Make validation of Import Dispatch Number
|
||||
* making validations on the document_number. If it is wrong it should raise an exception
|
||||
* format the document_number against a pattern and return it
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.country_id.code != "CL":
|
||||
return super()._format_document_number(document_number)
|
||||
|
||||
if not document_number:
|
||||
return False
|
||||
|
||||
return document_number.zfill(6)
|
||||
|
||||
def _is_doc_type_vendor(self):
|
||||
return self.code == '46'
|
||||
|
||||
def _is_doc_type_export(self):
|
||||
return self.code in ['110', '111', '112'] and self.country_id.code == 'CL'
|
||||
|
||||
def _is_doc_type_electronic_ticket(self):
|
||||
return self.code in ['39', '41'] and self.country_id.code == 'CL'
|
||||
15
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/res_company.py
Normal file
15
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/res_company.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
l10n_cl_activity_description = fields.Char(
|
||||
string='Company Activity Description', related='partner_id.l10n_cl_activity_description', readonly=False)
|
||||
|
||||
def _localization_use_documents(self):
|
||||
""" Chilean localization use documents """
|
||||
self.ensure_one()
|
||||
return self.account_fiscal_country_id.code == "CL" or super()._localization_use_documents()
|
||||
12
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/res_country.py
Normal file
12
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/res_country.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_name = 'res.country'
|
||||
_inherit = 'res.country'
|
||||
|
||||
l10n_cl_customs_code = fields.Char('Customs Code')
|
||||
l10n_cl_customs_name = fields.Char('Customs Name')
|
||||
l10n_cl_customs_abbreviation = fields.Char('Customs Abbreviation')
|
||||
10
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/res_currency.py
Normal file
10
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/res_currency.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class ResCurrency(models.Model):
|
||||
_name = "res.currency"
|
||||
_inherit = "res.currency"
|
||||
|
||||
l10n_cl_currency_code = fields.Char('Currency Code')
|
||||
l10n_cl_short_name = fields.Char('Short Name')
|
||||
69
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/res_partner.py
Normal file
69
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/res_partner.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import stdnum
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_name = 'res.partner'
|
||||
_inherit = 'res.partner'
|
||||
|
||||
_sii_taxpayer_types = [
|
||||
('1', _('VAT Affected (1st Category)')),
|
||||
('2', _('Fees Receipt Issuer (2nd category)')),
|
||||
('3', _('End Consumer')),
|
||||
('4', _('Foreigner')),
|
||||
]
|
||||
|
||||
l10n_cl_sii_taxpayer_type = fields.Selection(
|
||||
_sii_taxpayer_types, 'Taxpayer Type', index='btree_not_null',
|
||||
help='1 - VAT Affected (1st Category) (Most of the cases)\n'
|
||||
'2 - Fees Receipt Issuer (Applies to suppliers who issue fees receipt)\n'
|
||||
'3 - End consumer (only receipts)\n'
|
||||
'4 - Foreigner')
|
||||
l10n_cl_activity_description = fields.Char(string='Activity Description')
|
||||
|
||||
@api.model
|
||||
def _commercial_fields(self):
|
||||
return super()._commercial_fields() + ['l10n_cl_sii_taxpayer_type']
|
||||
|
||||
def _format_vat_cl(self, values):
|
||||
identification_types = [self.env.ref('l10n_latam_base.it_vat').id, self.env.ref('l10n_cl.it_RUT').id,
|
||||
self.env.ref('l10n_cl.it_RUN').id]
|
||||
country = self.env["res.country"].browse(values.get('country_id'))
|
||||
identification_type = self.env['l10n_latam.identification.type'].browse(
|
||||
values.get('l10n_latam_identification_type_id')
|
||||
)
|
||||
partner_country_is_chile = country.code == "CL" or identification_type.country_id.code == "CL"
|
||||
if partner_country_is_chile and \
|
||||
values.get('l10n_latam_identification_type_id') in identification_types and values.get('vat') and\
|
||||
stdnum.util.get_cc_module('cl', 'vat').is_valid(values['vat']):
|
||||
return stdnum.util.get_cc_module('cl', 'vat').format(values['vat']).replace('.', '').replace(
|
||||
'CL', '').upper()
|
||||
else:
|
||||
return values['vat']
|
||||
|
||||
def _format_dotted_vat_cl(self, vat):
|
||||
vat_l = vat.split('-')
|
||||
n_vat, n_dv = vat_l[0], vat_l[1]
|
||||
return '%s-%s' % (format(int(n_vat), ',d').replace(',', '.'), n_dv)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get('vat'):
|
||||
vals['vat'] = self._format_vat_cl(vals)
|
||||
return super().create(vals_list)
|
||||
|
||||
def write(self, values):
|
||||
if any(field in values for field in ['vat', 'l10n_latam_identification_type_id', 'country_id']):
|
||||
for record in self:
|
||||
vat_values = {
|
||||
'vat': values.get('vat', record.vat),
|
||||
'l10n_latam_identification_type_id': values.get(
|
||||
'l10n_latam_identification_type_id', record.l10n_latam_identification_type_id.id),
|
||||
'country_id': values.get('country_id', record.country_id.id)
|
||||
}
|
||||
values['vat'] = self._format_vat_cl(vat_values)
|
||||
return super().write(values)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResBank(models.Model):
|
||||
_name = 'res.bank'
|
||||
_inherit = 'res.bank'
|
||||
|
||||
l10n_cl_sbif_code = fields.Char('Cod. SBIF', size=10)
|
||||
9
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/uom_uom.py
Normal file
9
odoo-bringout-oca-ocb-l10n_cl/l10n_cl/models/uom_uom.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, api, _
|
||||
|
||||
|
||||
class UomUom(models.Model):
|
||||
_inherit = 'uom.uom'
|
||||
|
||||
l10n_cl_sii_code = fields.Char('SII Code')
|
||||
Loading…
Add table
Add a link
Reference in a new issue