193 lines
9.7 KiB
Python
193 lines
9.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import textwrap
|
|
|
|
from odoo import api, models, fields, _
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
DESCRIPTION_CREDIT_CODE = [
|
|
("1", "Devolución parcial de los bienes y/o no aceptación parcial del servicio"),
|
|
("2", "Anulación de factura electrónica"),
|
|
("3", "Rebaja total aplicada"),
|
|
("4", "Ajuste de precio"),
|
|
("5", "Descuento comercial por pronto pago"),
|
|
("6", "Descuento comercial por volumen de ventas")
|
|
]
|
|
|
|
DESCRIPTION_DEBIT_CODE = [
|
|
('1', 'Intereses'),
|
|
('2', 'Gastos por cobrar'),
|
|
('3', 'Cambio del valor'),
|
|
('4', 'Otros'),
|
|
]
|
|
|
|
L10N_CO_EDI_TYPE = {
|
|
'Sales Invoice': '01',
|
|
'Export Invoice': '02',
|
|
'Electronic transmission document - type 03': '03',
|
|
'Electronic Sales Invoice - type 04': '04',
|
|
'Credit Note': '91',
|
|
'Debit Note': '92',
|
|
'Event (Application Response)': '96',
|
|
}
|
|
|
|
|
|
class AccountMove(models.Model):
|
|
_inherit = 'account.move'
|
|
|
|
l10n_co_edi_type = fields.Selection(
|
|
selection=[(code, label) for label, code in L10N_CO_EDI_TYPE.items()],
|
|
compute='_compute_l10n_co_edi_type',
|
|
store=True,
|
|
string='Electronic Invoice Type',
|
|
)
|
|
l10n_co_edi_attachment_url = fields.Char('Electronic Invoice Attachment URL',
|
|
help='''Will be included in electronic invoice and can point to
|
|
e.g. a ZIP containing additional information about the invoice.''', copy=False)
|
|
l10n_co_edi_operation_type = fields.Selection([('10', 'Estandar'),
|
|
('09', 'AIU'),
|
|
('11', 'Mandatos'),
|
|
('12', 'Transporte'),
|
|
('13', 'Cambiario'),
|
|
('15', 'Compra Divisas'),
|
|
('16', 'Venta Divisas'),
|
|
('20', 'Nota Crédito que referencia una factura electrónica'),
|
|
('22', 'Nota Crédito sin referencia a facturas'),
|
|
('30', 'Nota Débito que referencia una factura electrónica'),
|
|
('32', 'Nota Débito sin referencia a facturas'),
|
|
('23', 'Inactivo: Nota Crédito para facturación electrónica V1 (Decreto 2242)'),
|
|
('33', 'Inactivo: Nota Débito para facturación electrónica V1 (Decreto 2242)')],
|
|
string="Operation Type (CO)", compute='_compute_operation_type', store=True)
|
|
|
|
# field used to track the status of a submission
|
|
l10n_co_edi_transaction = fields.Char('Transaction ID (CO)', copy=False)
|
|
l10n_co_edi_cufe_cude_ref = fields.Char(string="CUFE/CUDE", copy=False, help='Unique ID received by the government when the invoice is signed.')
|
|
l10n_co_edi_payment_option_id = fields.Many2one('l10n_co_edi.payment.option', string="Payment Option",
|
|
default=lambda self: self.env.ref('l10n_co_edi.payment_option_1', raise_if_not_found=False))
|
|
l10n_co_edi_is_direct_payment = fields.Boolean("Direct Payment from Colombia", compute="_compute_l10n_co_edi_is_direct_payment")
|
|
l10n_co_edi_description_code_credit = fields.Selection(DESCRIPTION_CREDIT_CODE, string="Concepto Nota de Credito")
|
|
l10n_co_edi_description_code_debit = fields.Selection(DESCRIPTION_DEBIT_CODE, string="Concepto Nota de Débito")
|
|
l10n_co_edi_debit_note = fields.Boolean(related="journal_id.l10n_co_edi_debit_note")
|
|
l10n_co_edi_is_support_document = fields.Boolean('Support Document', related='journal_id.l10n_co_edi_is_support_document')
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Compute
|
|
# -------------------------------------------------------------------------
|
|
|
|
@api.depends('move_type', 'l10n_co_edi_debit_note')
|
|
def _compute_l10n_co_edi_type(self):
|
|
CO_moves = self.filtered(lambda move: move.company_id.account_fiscal_country_id.code == 'CO')
|
|
for move in CO_moves:
|
|
if move.move_type == 'out_refund':
|
|
move.l10n_co_edi_type = L10N_CO_EDI_TYPE['Credit Note']
|
|
elif move.l10n_co_edi_debit_note:
|
|
move.l10n_co_edi_type = L10N_CO_EDI_TYPE['Debit Note']
|
|
elif not move.l10n_co_edi_type:
|
|
move.l10n_co_edi_type = L10N_CO_EDI_TYPE['Sales Invoice']
|
|
|
|
@api.depends('move_type', 'reversed_entry_id', 'debit_origin_id', 'l10n_co_edi_debit_note')
|
|
def _compute_operation_type(self):
|
|
CO_moves = self.filtered(lambda move: move.country_code == 'CO')
|
|
for move in CO_moves:
|
|
operation_type = '10'
|
|
if move.move_type == 'out_refund':
|
|
operation_type = '20' if move.reversed_entry_id else '22'
|
|
elif move.l10n_co_edi_debit_note:
|
|
operation_type = '30' if move.debit_origin_id else '32'
|
|
move.l10n_co_edi_operation_type = operation_type
|
|
|
|
@api.depends('invoice_date_due', 'date')
|
|
def _compute_l10n_co_edi_is_direct_payment(self):
|
|
for rec in self:
|
|
rec.l10n_co_edi_is_direct_payment = (rec.date == rec.invoice_date_due) and rec.company_id.account_fiscal_country_id.code == 'CO'
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Helpers
|
|
# -------------------------------------------------------------------------
|
|
|
|
def _l10n_co_edi_get_electronic_invoice_type(self):
|
|
if self.move_type == 'out_invoice':
|
|
return 'ND' if self.l10n_co_edi_debit_note else 'INVOIC'
|
|
elif self.move_type == 'in_invoice':
|
|
return 'INVOIC'
|
|
return 'NC'
|
|
|
|
def _l10n_co_edi_get_electronic_invoice_type_info(self):
|
|
if self.move_type == 'out_invoice':
|
|
return 'DIAN 2.1: Nota Débito de Factura Electrónica de Venta' if self.l10n_co_edi_debit_note else 'DIAN 2.1: Factura Electrónica de Venta'
|
|
elif self.move_type == 'in_invoice':
|
|
return 'DIAN 2.1: documento soporte en adquisiciones efectuadas a no obligados a facturar.'
|
|
elif self.move_type == 'in_refund':
|
|
return 'DIAN 2.1: Nota de ajuste al documento soporte en adquisiciones efectuadas a sujetos no obligados a expedir factura o documento equivalente'
|
|
return 'DIAN 2.1: Nota Crédito de Factura Electrónica de Venta'
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Account_edi OVERRIDE
|
|
# -------------------------------------------------------------------------
|
|
|
|
def _retry_edi_documents_error(self):
|
|
# EXTENDS account_edi
|
|
# For CO, remove the l10n_co_edi_transaction to force re-send (otherwise this only triggers a check_status)
|
|
super()._retry_edi_documents_error()
|
|
carvajal = self.env.ref('l10n_co_edi.edi_carvajal')
|
|
self.filtered(lambda m: m._get_edi_document(carvajal).blocking_level == 'error').l10n_co_edi_transaction = None
|
|
|
|
def button_draft(self):
|
|
# OVERRIDE
|
|
for move in self:
|
|
if move.l10n_co_edi_transaction:
|
|
raise UserError(_(
|
|
"You can't edit the following journal entry %s because an electronic document has already been "
|
|
"sent to Carvajal. To edit this entry, you need to create a Credit Note for the invoice and "
|
|
"create a new invoice.",
|
|
move.display_name))
|
|
|
|
return super().button_draft()
|
|
|
|
|
|
class AccountMoveLine(models.Model):
|
|
_inherit = 'account.move.line'
|
|
|
|
def _l10n_co_edi_get_product_code(self):
|
|
"""
|
|
For identifying products, different standards can be used. If there is a barcode, we take that one, because
|
|
normally in the GTIN standard it will be the most specific one. Otherwise, we will check the
|
|
:return: (standard, product_code)
|
|
"""
|
|
self.ensure_one()
|
|
if self.product_id:
|
|
if self.move_id.l10n_co_edi_type == L10N_CO_EDI_TYPE['Export Invoice']:
|
|
if not self.product_id.l10n_co_edi_customs_code:
|
|
raise UserError(_('Exportation invoices require custom code in all the products, please fill in this information before validating the invoice'))
|
|
return (self.product_id.l10n_co_edi_customs_code, '020', 'Partida Alanceraria')
|
|
if (
|
|
self.move_type == "in_refund" and
|
|
self.move_id.l10n_co_edi_is_support_document and
|
|
(code := self.product_id.default_code or self.product_id.barcode or self.product_id.unspsc_code_id.code)
|
|
):
|
|
return (code, '999', 'Estándar de adopción del contribuyente')
|
|
elif self.product_id.barcode:
|
|
return (self.product_id.barcode, '010', 'GTIN')
|
|
elif self.product_id.unspsc_code_id:
|
|
return (self.product_id.unspsc_code_id.code, '001', 'UNSPSC')
|
|
elif self.product_id.default_code:
|
|
return (self.product_id.default_code, '999', 'Estándar de adopción del contribuyente')
|
|
|
|
return ('1010101', '001', '')
|
|
|
|
def _l10n_co_edi_get_iae3_value(self, product_code):
|
|
value = {
|
|
'010': '9',
|
|
'001': '10',
|
|
}
|
|
return value.get(product_code, '')
|
|
|
|
def _l10n_co_edi_get_line_name(self):
|
|
"""
|
|
Ensure the text we use for electronic communications follows
|
|
Carvajal specifications
|
|
"""
|
|
self.ensure_one()
|
|
return textwrap.shorten(self.name or '', 300)
|