forked from Mapan/odoo17e
189 lines
9.7 KiB
Python
189 lines
9.7 KiB
Python
# -*- coding: utf-8 -*-
|
||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
from odoo import api, models, fields, _
|
||
from odoo.exceptions import UserError
|
||
|
||
import textwrap
|
||
|
||
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'),
|
||
]
|
||
|
||
|
||
class AccountMove(models.Model):
|
||
_inherit = 'account.move'
|
||
|
||
l10n_co_edi_type = fields.Selection([
|
||
('1', 'Factura de venta'),
|
||
('2', 'Factura de exportación'),
|
||
('3', 'Documento electrónico de transmisión – tipo 03'),
|
||
('4', 'Factura electrónica de Venta - tipo 04'),
|
||
('91', 'Nota Crédito'),
|
||
('92', 'Nota Débito'),
|
||
('96', 'Eventos (Application Response)'),
|
||
# TODO: remove 'required' in master
|
||
], required=True, default='1', 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'),
|
||
('23', 'Nota Crédito para facturación electrónica V1 (Decreto 2242)'),
|
||
('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', default="10", required=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 = '91'
|
||
elif move.l10n_co_edi_debit_note:
|
||
move.l10n_co_edi_type = '92'
|
||
elif not move.l10n_co_edi_type:
|
||
move.l10n_co_edi_type = '1'
|
||
|
||
@api.depends('move_type', 'reversed_entry_id', 'edi_document_ids.state', 'l10n_co_edi_cufe_cude_ref')
|
||
def _compute_operation_type(self):
|
||
for rec in self:
|
||
operation_type = False
|
||
if rec.move_type == 'out_refund':
|
||
if rec.reversed_entry_id:
|
||
operation_type = '20'
|
||
else:
|
||
operation_type = '22'
|
||
else:
|
||
if rec.l10n_co_edi_debit_note:
|
||
state = rec._get_edi_document(self.env.ref('l10n_co_edi.edi_carvajal')).state
|
||
if state == 'sent' and not rec.l10n_co_edi_cufe_cude_ref:
|
||
operation_type = '23'
|
||
elif rec.debit_origin_id:
|
||
operation_type = '30'
|
||
else:
|
||
operation_type = '32'
|
||
rec.l10n_co_edi_operation_type = operation_type or '10'
|
||
|
||
@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_hook(self):
|
||
# OVERRIDE
|
||
# For CO, remove the l10n_co_edi_transaction to force re-send (otherwise this only triggers a check_status)
|
||
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 == '2':
|
||
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.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)
|