Copy of all modules in original repo
18
account_tax_group_type/README.rst
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
|
||||||
|
:target: https://www.gnu.org/licenses/agpl
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
|
||||||
|
===============
|
||||||
|
Tax Group Types
|
||||||
|
===============
|
||||||
|
|
||||||
|
Types for Tax Groups
|
||||||
|
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Joan Marín <https://github.com/JoanMarin>
|
||||||
5
account_tax_group_type/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import models
|
||||||
19
account_tax_group_type/__manifest__.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Tax Group Types",
|
||||||
|
"summary": "Types for Tax Groups",
|
||||||
|
"version": "14.0.1.0.0",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"website": "https://github.com/JoanMarin",
|
||||||
|
"author": "Joan Marín Github@JoanMarin",
|
||||||
|
"category": "Localization",
|
||||||
|
"depends": ["account_menu"],
|
||||||
|
"data": [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
"views/account_tax_group_views.xml",
|
||||||
|
],
|
||||||
|
"installable": True,
|
||||||
|
}
|
||||||
110
account_tax_group_type/i18n/account_tax_group_type.pot
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * account_tax_group_type
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 14.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2021-08-20 13:59+0000\n"
|
||||||
|
"PO-Revision-Date: 2021-08-20 13:59+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__code
|
||||||
|
msgid "Code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__create_uid
|
||||||
|
msgid "Created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__create_date
|
||||||
|
msgid "Created on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__description
|
||||||
|
msgid "Description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__display_name
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__id
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group____last_update
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__write_uid
|
||||||
|
msgid "Last Updated by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__write_date
|
||||||
|
msgid "Last Updated on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__name
|
||||||
|
msgid "Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields.selection,name:account_tax_group_type.selection__account_tax_group_type__type__tax
|
||||||
|
msgid "Tax"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model,name:account_tax_group_type.model_account_tax_group
|
||||||
|
msgid "Tax Group"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__tax_group_type_id
|
||||||
|
msgid "Tax Group Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model,name:account_tax_group_type.model_account_tax_group_type
|
||||||
|
msgid "Tax Group Types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: code:addons/account_tax_group_type/models/account_tax_group_type.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "The code of Tax Group Type must be unique!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__type
|
||||||
|
msgid "Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields.selection,name:account_tax_group_type.selection__account_tax_group_type__type__withholding_tax
|
||||||
|
msgid "Withholding Tax"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.constraint,message:account_tax_group_type.constraint_account_tax_group_type_code_uniq
|
||||||
|
msgid "¡El código del Tipo de Grupo Impuesto debe ser único!"
|
||||||
|
msgstr ""
|
||||||
106
account_tax_group_type/i18n/es.po
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * account_tax_group_type
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 14.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2021-08-20 14:04+0000\n"
|
||||||
|
"PO-Revision-Date: 2021-08-20 14:04+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__code
|
||||||
|
msgid "Code"
|
||||||
|
msgstr "Código"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__create_uid
|
||||||
|
msgid "Created by"
|
||||||
|
msgstr "Creado por"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__create_date
|
||||||
|
msgid "Created on"
|
||||||
|
msgstr "Creado el"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__description
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Descripción"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__display_name
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr "Nombre Público"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__id
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group____last_update
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr "Última Modificación el"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__write_uid
|
||||||
|
msgid "Last Updated by"
|
||||||
|
msgstr "Última Actualización por"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__write_date
|
||||||
|
msgid "Last Updated on"
|
||||||
|
msgstr "Última Actualización el"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__name
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "Nombre"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields.selection,name:account_tax_group_type.selection__account_tax_group_type__type__tax
|
||||||
|
msgid "Tax"
|
||||||
|
msgstr "Impuesto"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model,name:account_tax_group_type.model_account_tax_group
|
||||||
|
msgid "Tax Group"
|
||||||
|
msgstr "Grupo de impuestos"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__tax_group_type_id
|
||||||
|
msgid "Tax Group Type"
|
||||||
|
msgstr "Tipo de Grupo de Impuesto"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model,name:account_tax_group_type.model_account_tax_group_type
|
||||||
|
msgid "Tax Group Types"
|
||||||
|
msgstr "Tipos de Grupos de Impuestos"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: code:addons/account_tax_group_type/models/account_tax_group_type.py:0
|
||||||
|
#: model:ir.model.constraint,message:account_tax_group_type.constraint_account_tax_group_type_code_uniq
|
||||||
|
#, python-format
|
||||||
|
msgid "The code of Tax Group Type must be unique!"
|
||||||
|
msgstr "¡El código del Tipo de Grupo Impuesto debe ser único!"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__type
|
||||||
|
msgid "Type"
|
||||||
|
msgstr "Tipo"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields.selection,name:account_tax_group_type.selection__account_tax_group_type__type__withholding_tax
|
||||||
|
msgid "Withholding Tax"
|
||||||
|
msgstr "Impuesto de Retención"
|
||||||
106
account_tax_group_type/i18n/es_CO.po
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * account_tax_group_type
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 14.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2021-08-31 20:46+0000\n"
|
||||||
|
"PO-Revision-Date: 2021-08-31 20:46+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__code
|
||||||
|
msgid "Code"
|
||||||
|
msgstr "Código"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__create_uid
|
||||||
|
msgid "Created by"
|
||||||
|
msgstr "Creado por"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__create_date
|
||||||
|
msgid "Created on"
|
||||||
|
msgstr "Creado el"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__description
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Descripción"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__display_name
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr "Nombre Público"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__id
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group____last_update
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr "Última Modificación el"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__write_uid
|
||||||
|
msgid "Last Updated by"
|
||||||
|
msgstr "Última Actualización por"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__write_date
|
||||||
|
msgid "Last Updated on"
|
||||||
|
msgstr "Última Actualización el"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__name
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "Nombre"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields.selection,name:account_tax_group_type.selection__account_tax_group_type__type__tax
|
||||||
|
msgid "Tax"
|
||||||
|
msgstr "Impuesto"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model,name:account_tax_group_type.model_account_tax_group
|
||||||
|
msgid "Tax Group"
|
||||||
|
msgstr "Grupo de impuestos"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group__tax_group_type_id
|
||||||
|
msgid "Tax Group Type"
|
||||||
|
msgstr "Tipo de Grupo de Impuesto"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model,name:account_tax_group_type.model_account_tax_group_type
|
||||||
|
msgid "Tax Group Types"
|
||||||
|
msgstr "Tipos de Grupos de Impuestos"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: code:addons/account_tax_group_type/models/account_tax_group_type.py:0
|
||||||
|
#: model:ir.model.constraint,message:account_tax_group_type.constraint_account_tax_group_type_code_uniq
|
||||||
|
#, python-format
|
||||||
|
msgid "The code of Tax Group Type must be unique!"
|
||||||
|
msgstr "¡El código del Tipo de Grupo Impuesto debe ser único!"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields,field_description:account_tax_group_type.field_account_tax_group_type__type
|
||||||
|
msgid "Type"
|
||||||
|
msgstr "Tipo"
|
||||||
|
|
||||||
|
#. module: account_tax_group_type
|
||||||
|
#: model:ir.model.fields.selection,name:account_tax_group_type.selection__account_tax_group_type__type__withholding_tax
|
||||||
|
msgid "Withholding Tax"
|
||||||
|
msgstr "Impuesto de Retención"
|
||||||
6
account_tax_group_type/models/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import account_tax_group_type
|
||||||
|
from . import account_tax_group
|
||||||
13
account_tax_group_type/models/account_tax_group.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountTaxGroup(models.Model):
|
||||||
|
_inherit = "account.tax.group"
|
||||||
|
|
||||||
|
tax_group_type_id = fields.Many2one(
|
||||||
|
comodel_name="account.tax.group.type",
|
||||||
|
string="Tax Group Type")
|
||||||
23
account_tax_group_type/models/account_tax_group_type.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class AccountTaxGroupType(models.Model):
|
||||||
|
_name = 'account.tax.group.type'
|
||||||
|
_description = 'Tax Group Types'
|
||||||
|
|
||||||
|
code = fields.Char(string='Code', required=True)
|
||||||
|
name = fields.Char(string='Name', required=True)
|
||||||
|
type = fields.Selection(
|
||||||
|
selection=[('tax', 'Tax'), ('withholding_tax', 'Withholding Tax')],
|
||||||
|
string='Type',
|
||||||
|
required=True,
|
||||||
|
default=False)
|
||||||
|
description = fields.Char(string='Description')
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
('code_uniq', 'unique (code)',
|
||||||
|
_('The code of Tax Group Type must be unique!'))]
|
||||||
3
account_tax_group_type/security/ir.model.access.csv
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_account_tax_group_type_user,access_account_tax_group_type_user,model_account_tax_group_type,,1,0,0,0
|
||||||
|
access_account_tax_group_type_manager,access_account_tax_group_type_manager,model_account_tax_group_type,base.group_system,1,1,1,1
|
||||||
|
BIN
account_tax_group_type/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 229 B |
23
account_tax_group_type/views/account_tax_group_views.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_account_tax_group_tree" model="ir.ui.view">
|
||||||
|
<field name="model">account.tax.group</field>
|
||||||
|
<field name="inherit_id" ref="account.view_tax_group_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='sequence']" position="after">
|
||||||
|
<field name="tax_group_type_id"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_account_tax_group_form" model="ir.ui.view">
|
||||||
|
<field name="model">account.tax.group</field>
|
||||||
|
<field name="inherit_id" ref="account_menu.view_account_tax_group_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='sequence']" position="after">
|
||||||
|
<group>
|
||||||
|
<field name="tax_group_type_id"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
7
electronic_invoice_dian/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Module models
|
||||||
|
"""
|
||||||
|
from . import models
|
||||||
|
from . import wizard
|
||||||
|
from . import controllers
|
||||||
38
electronic_invoice_dian/__manifest__.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# pylint: disable=missing-docstring
|
||||||
|
{
|
||||||
|
'name': "Facturación Electrónica DIAN",
|
||||||
|
'summary': """
|
||||||
|
Facturación Electrónica DIAN""",
|
||||||
|
'description': """
|
||||||
|
Envio de facturas y notas electrónicas, por Avancys SAS como
|
||||||
|
proveedor tecnológico o por software propio.
|
||||||
|
""",
|
||||||
|
'author': "Avancys SAS",
|
||||||
|
'website': "http://www.avancys.com",
|
||||||
|
'category': 'Accounting/Accounting',
|
||||||
|
'version': '14.0.0.0',
|
||||||
|
'depends': ['account', 'sale'],
|
||||||
|
'license': 'OEEL-1',
|
||||||
|
'installable': True,
|
||||||
|
'data': [
|
||||||
|
'data/data.xml',
|
||||||
|
# 'data/res.country.state.csv',
|
||||||
|
'security/security.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'wizard/ei_multi_process.xml',
|
||||||
|
'wizard/ei_state_reset.xml',
|
||||||
|
'views/res_company.xml',
|
||||||
|
'views/electronic_invoice_resolution.xml',
|
||||||
|
'views/account_journal.xml',
|
||||||
|
'views/account_move.xml',
|
||||||
|
'views/account_tax.xml',
|
||||||
|
'views/uom_uom.xml',
|
||||||
|
'views/sale_order.xml',
|
||||||
|
'views/res_partner.xml',
|
||||||
|
'templates/customer_acknowledge_email.xml',
|
||||||
|
'templates/customer_acknowledge_response.xml',
|
||||||
|
'templates/electronic_invoice.xml',
|
||||||
|
'report/electronic_invoice_report.xml',
|
||||||
|
],
|
||||||
|
}
|
||||||
0
electronic_invoice_dian/changelog
Executable file
1
electronic_invoice_dian/controllers/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import main
|
||||||
43
electronic_invoice_dian/controllers/main.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import odoo
|
||||||
|
from odoo import api, SUPERUSER_ID
|
||||||
|
import odoo.http as http
|
||||||
|
from odoo.http import request
|
||||||
|
import odoo.addons.web.controllers.main as webmain
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class AccountInvoiceAck(http.Controller):
|
||||||
|
@http.route('/invoice/dian/accept', type='http', auth="public")
|
||||||
|
def accept(self, db, token, id):
|
||||||
|
registry = odoo.modules.registry.Registry(db)
|
||||||
|
state_change = None
|
||||||
|
with api.Environment.manage(), registry.cursor() as cr:
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
invoice_id = env['account.move'].search([
|
||||||
|
('access_token', '=', token),
|
||||||
|
('ei_state', '!=', 'customer_accept'),
|
||||||
|
('id', '=', id)
|
||||||
|
])
|
||||||
|
if invoice_id:
|
||||||
|
state_change = invoice_id.do_accept()
|
||||||
|
if not state_change:
|
||||||
|
return
|
||||||
|
return request.render('electronic_invoice_dian.customer_accept_invoice', {})
|
||||||
|
|
||||||
|
@http.route('/invoice/dian/reject', type='http', auth="public")
|
||||||
|
def reject(self, db, token, id):
|
||||||
|
registry = odoo.modules.registry.Registry(db)
|
||||||
|
state_change = None
|
||||||
|
with api.Environment.manage(), registry.cursor() as cr:
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
invoice_id = env['account.move'].search([
|
||||||
|
('access_token', '=', token),
|
||||||
|
('ei_state', '!=', 'customer_accept'),
|
||||||
|
('id', '=', id)
|
||||||
|
])
|
||||||
|
if invoice_id:
|
||||||
|
state_change = invoice_id.do_accept()
|
||||||
|
if not state_change:
|
||||||
|
return
|
||||||
|
return request.render('electronic_invoice_dian.customer_reject_invoice', {})
|
||||||
4
electronic_invoice_dian/data/data.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<menuitem id="electronic_invoice_menu" name="Facturación Electrónica" parent="account.menu_finance_configuration" sequence="2"/>
|
||||||
|
</odoo>
|
||||||
34
electronic_invoice_dian/data/res.country.state.csv
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"id","code","name"
|
||||||
|
base.state_co_01,"05","ANTIOQUIA"
|
||||||
|
base.state_co_02,"08","ATLANTICO"
|
||||||
|
base.state_co_03,"11","BOGOTA"
|
||||||
|
base.state_co_04,"13","BOLIVAR"
|
||||||
|
base.state_co_05,"15","BOYACA"
|
||||||
|
base.state_co_06,"17","CALDAS"
|
||||||
|
base.state_co_07,"18","CAQUETA"
|
||||||
|
base.state_co_08,"19","CAUCA"
|
||||||
|
base.state_co_09,"20","CESAR"
|
||||||
|
base.state_co_10,"23","CORDOBA"
|
||||||
|
base.state_co_11,"25","CUNDINAMARCA"
|
||||||
|
base.state_co_12,"27","CHOCO"
|
||||||
|
base.state_co_13,"41","HUILA"
|
||||||
|
base.state_co_14,"44","LA GUAJIRA"
|
||||||
|
base.state_co_15,"47","MAGDALENA"
|
||||||
|
base.state_co_16,"50","META"
|
||||||
|
base.state_co_17,"52","NARIÑO"
|
||||||
|
base.state_co_18,"54","N. DE SANTANDER"
|
||||||
|
base.state_co_19,"63","QUINDIO"
|
||||||
|
base.state_co_20,"66","RISARALDA"
|
||||||
|
base.state_co_21,"68","SANTANDER"
|
||||||
|
base.state_co_22,"70","SUCRE"
|
||||||
|
base.state_co_23,"73","TOLIMA"
|
||||||
|
base.state_co_24,"76","VALLE DEL CAUCA"
|
||||||
|
base.state_co_25,"81","ARAUCA"
|
||||||
|
base.state_co_26,"85","CASANARE"
|
||||||
|
base.state_co_27,"86","PUTUMAYO"
|
||||||
|
base.state_co_28,"88","SAN ANDRES"
|
||||||
|
base.state_co_29,"91","AMAZONAS"
|
||||||
|
base.state_co_30,"94","GUAINIA"
|
||||||
|
base.state_co_31,"95","GUAVIARE"
|
||||||
|
base.state_co_32,"97","VAUPES"
|
||||||
|
base.state_co_33,"99","VICHADA"
|
||||||
|
14
electronic_invoice_dian/models/__init__.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Models
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import res_company
|
||||||
|
from . import electronic_invoice_resolution
|
||||||
|
from . import account_journal
|
||||||
|
from . import account_move
|
||||||
|
from . import account_tax
|
||||||
|
from . import ei_transaction_log
|
||||||
|
from . import uom_uom
|
||||||
|
from . import sale_order
|
||||||
|
from . import res_partner
|
||||||
18
electronic_invoice_dian/models/account_journal.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
INVALID_TYPE_WARN = """Tipo de diario no válido para facturación electrónica"""
|
||||||
|
|
||||||
|
|
||||||
|
class AccountJournal(models.Model):
|
||||||
|
_inherit = 'account.journal'
|
||||||
|
|
||||||
|
resolution_id = fields.Many2one(
|
||||||
|
string='Resolución de Numeración',
|
||||||
|
comodel_name='electronic.invoice.resolution')
|
||||||
|
|
||||||
|
@api.constrains('resolution_id')
|
||||||
|
def check_document_type(self):
|
||||||
|
if self.type != 'sale' and self.resolution_id:
|
||||||
|
raise ValidationError(INVALID_TYPE_WARN)
|
||||||
1048
electronic_invoice_dian/models/account_move.py
Normal file
20
electronic_invoice_dian/models/account_tax.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
EI_CODE_SELECTION = [
|
||||||
|
('01', 'IVA'),
|
||||||
|
('02', 'IC'),
|
||||||
|
('03', 'ICA'),
|
||||||
|
('04', 'INC'),
|
||||||
|
('05', 'ReteIVA'),
|
||||||
|
('06', 'ReteFuente'),
|
||||||
|
('07', 'ReteICA')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AccountTax(models.Model):
|
||||||
|
_inherit = 'account.tax'
|
||||||
|
|
||||||
|
ei_code = fields.Selection(
|
||||||
|
string='Código Facturación Electrónica',
|
||||||
|
selection=EI_CODE_SELECTION)
|
||||||
37
electronic_invoice_dian/models/ei_transaction_log.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
DOCUMENT_STATE_SELECTION = [
|
||||||
|
('sent', 'Emitido'),
|
||||||
|
('dian_reject', 'Rechazo Dian'),
|
||||||
|
('dian_accept', 'Aceptación Dian'),
|
||||||
|
('email_sent', 'Email Enviado'),
|
||||||
|
('email_fail', 'Email Fallido'),
|
||||||
|
('customer_accept', 'Aceptación Cliente'),
|
||||||
|
('customer_reject', 'Rechazo Cliente'),
|
||||||
|
('provider_error', 'Error de Procesamiento'),
|
||||||
|
('exception', 'Excepción de Envio'),
|
||||||
|
]
|
||||||
|
DOCUMENT_TYPE_SELECTION = [
|
||||||
|
('json', 'Json'),
|
||||||
|
('app_response', 'Application Response'),
|
||||||
|
('xml_fe', 'Factura XML'),
|
||||||
|
('none', 'Ninguno'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class EITransactionLog(models.Model):
|
||||||
|
_name = 'ei.transaction.log'
|
||||||
|
_description = 'Log de Facturacion Electronica'
|
||||||
|
_order = 'date desc'
|
||||||
|
|
||||||
|
invoice_id = fields.Many2one(
|
||||||
|
string='Factura', comodel_name='account.move', readonly=True)
|
||||||
|
date = fields.Datetime(string='Fecha', readonly=True)
|
||||||
|
document_state = fields.Selection(
|
||||||
|
string='Estado', selection=DOCUMENT_STATE_SELECTION, readonly=True)
|
||||||
|
document_type = fields.Selection(
|
||||||
|
string='Tipo de documento', selection=DOCUMENT_TYPE_SELECTION,
|
||||||
|
readonly=True)
|
||||||
|
content = fields.Text(string='Contenido', readonly=True)
|
||||||
133
electronic_invoice_dian/models/electronic_invoice_resolution.py
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from lxml import etree
|
||||||
|
import requests
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
import operator
|
||||||
|
from functools import reduce
|
||||||
|
from .http_helper import URL_NUMBERING_XML, AVANCYS_NIT, HEADERS
|
||||||
|
|
||||||
|
ID_PARAM_HELP = """Provisto por el Proveedor Tecnológico"""
|
||||||
|
DOCUMENT_TYPE_HELP = """
|
||||||
|
Para Notas Crédito y Débito los datos de resolución no aplican"""
|
||||||
|
DOCUMENT_TYPE_SELECTION = [
|
||||||
|
('01', 'Factura Electrónica'),
|
||||||
|
('91', 'Nota Crédito'),
|
||||||
|
('92', 'Nota Débito'),
|
||||||
|
('03', 'Contingencia')
|
||||||
|
]
|
||||||
|
|
||||||
|
# Warnings
|
||||||
|
|
||||||
|
NOT_ENOUGH_DATA_WARN = """
|
||||||
|
No hay suficientes datos para obtener la información, Por favor ingrese
|
||||||
|
al menos los siguientes:
|
||||||
|
|
||||||
|
- Número de Resolución
|
||||||
|
- Prefijo
|
||||||
|
- Numeración Desde
|
||||||
|
- Numeración Hasta
|
||||||
|
"""
|
||||||
|
MULTI_RESOLUTION_WARN = """
|
||||||
|
Se encontro mas de una resolución con los mismos parametros.
|
||||||
|
"""
|
||||||
|
NO_RESOLUTION_WARN = """
|
||||||
|
No se encontro ninguna resolución que coincida con los datos."""
|
||||||
|
RESOLUTION_SAMPLE = """
|
||||||
|
Ejemplo: Resolución DIAN N° 00000000 desde PREFIJO 1 hasta PREFIJO 1000
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ElectronicInvoiceResolution(models.Model):
|
||||||
|
_name = 'electronic.invoice.resolution'
|
||||||
|
_description = 'Resolucion de Facturacion Electronica'
|
||||||
|
|
||||||
|
name = fields.Char(string='Nombre', required=True)
|
||||||
|
number = fields.Char(string='Número de Resolución')
|
||||||
|
prefix = fields.Char('Prefijo', required=True)
|
||||||
|
from_number = fields.Integer(string='Numeración Desde', copy=False)
|
||||||
|
to_number = fields.Integer(string='Numeración Hasta', copy=False)
|
||||||
|
valid_date_from = fields.Date(string='Fecha Inicio Resolución', copy=False)
|
||||||
|
valid_date_to = fields.Date(string='Fecha Fin Resolución', copy=False)
|
||||||
|
id_param = fields.Integer(
|
||||||
|
string='ID Resolución', help=ID_PARAM_HELP, required=True, default=0)
|
||||||
|
technical_key = fields.Char(string='Clave Técnica', copy=False)
|
||||||
|
document_type = fields.Selection(
|
||||||
|
string='Tipo de Documento', selection=DOCUMENT_TYPE_SELECTION,
|
||||||
|
required=True, copy=False, help=DOCUMENT_TYPE_HELP)
|
||||||
|
description = fields.Text(
|
||||||
|
string='Texto de Resolución', help=RESOLUTION_SAMPLE)
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
'res.company', 'Company',
|
||||||
|
default=lambda self: self.env.company,
|
||||||
|
index=True, required=True)
|
||||||
|
|
||||||
|
def get_resolution_xml(self):
|
||||||
|
self.ensure_one()
|
||||||
|
company = self.company_id
|
||||||
|
body_dict = {
|
||||||
|
"datos_conexion":
|
||||||
|
{
|
||||||
|
"token": company.software_token,
|
||||||
|
"documento": company.partner_id.ref_num,
|
||||||
|
"documentoT": (AVANCYS_NIT
|
||||||
|
if company.ei_software_operation == 'provider'
|
||||||
|
else company.partner_id.ref_num)
|
||||||
|
},
|
||||||
|
"Datos_software":
|
||||||
|
{
|
||||||
|
"codigo": company.software_code_dian,
|
||||||
|
"ambiente": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = 'json_data=' + \
|
||||||
|
base64.b64encode(json.dumps(body_dict).encode(
|
||||||
|
'utf-8')).decode('utf-8')
|
||||||
|
response = requests.post(URL_NUMBERING_XML, data=data,
|
||||||
|
headers=HEADERS, verify=False)
|
||||||
|
return str(response.content, 'utf-8')
|
||||||
|
|
||||||
|
def parse_resolution(self, resolution):
|
||||||
|
return {
|
||||||
|
'ResolutionNumber': re.search(
|
||||||
|
r'<c:ResolutionNumber>(.*?)</c:ResolutionNumber>', resolution).group(1),
|
||||||
|
'ResolutionDate': re.search(
|
||||||
|
r'<c:ResolutionDate>(.*?)</c:ResolutionDate>', resolution).group(1),
|
||||||
|
'Prefix': re.search(r'<c:Prefix>(.*?)</c:Prefix>', resolution).group(1),
|
||||||
|
'FromNumber': re.search(r'<c:FromNumber>(.*?)</c:FromNumber>', resolution).group(1),
|
||||||
|
'ToNumber': re.search(r'<c:ToNumber>(.*?)</c:ToNumber>', resolution).group(1),
|
||||||
|
'ValidDateFrom': re.search(
|
||||||
|
r'<c:ValidDateFrom>(.*?)</c:ValidDateFrom>', resolution).group(1),
|
||||||
|
'ValidDateTo': re.search(r'<c:ValidDateTo>(.*?)</c:ValidDateTo>', resolution).group(1),
|
||||||
|
'TechnicalKey': re.search(r'<c:TechnicalKey>(.*?)</c:TechnicalKey>',
|
||||||
|
resolution).group(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
def resolucion_match(self, resolucion_match):
|
||||||
|
return reduce(operator.and_, [
|
||||||
|
self.number == resolucion_match.get('ResolutionNumber', False),
|
||||||
|
self.prefix == resolucion_match.get('Prefix', False),
|
||||||
|
str(self.from_number) == resolucion_match.get('FromNumber', False),
|
||||||
|
str(self.to_number) == resolucion_match.get('ToNumber', False),
|
||||||
|
])
|
||||||
|
|
||||||
|
def get_technical_key(self):
|
||||||
|
if not self.prefix or not self.number or not self.from_number or not self.to_number:
|
||||||
|
raise ValidationError(NOT_ENOUGH_DATA_WARN)
|
||||||
|
self.technical_key = ''
|
||||||
|
resolution_xml = self.get_resolution_xml()
|
||||||
|
resolutions = re.findall(
|
||||||
|
r'<c:NumberRangeResponse>([\s\S.]*?)</c:NumberRangeResponse>', resolution_xml)
|
||||||
|
if not resolutions:
|
||||||
|
return
|
||||||
|
resolutions_list = list(map(self.parse_resolution, resolutions))
|
||||||
|
matching_resolution = list(
|
||||||
|
filter(self.resolucion_match, resolutions_list))
|
||||||
|
if not matching_resolution:
|
||||||
|
raise ValidationError(NO_RESOLUTION_WARN)
|
||||||
|
if len(matching_resolution) > 1:
|
||||||
|
raise ValidationError(MULTI_RESOLUTION_WARN)
|
||||||
|
self.technical_key = matching_resolution[0]['TechnicalKey']
|
||||||
58
electronic_invoice_dian/models/http_helper.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from collections import OrderedDict
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
HEADERS = {
|
||||||
|
'Host': 'terabyte.com.co',
|
||||||
|
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0',
|
||||||
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
||||||
|
'Accept-Language': 'en-US,en;q=0.5',
|
||||||
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
|
'Referer': 'https://www.avancyserp.com/',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Content-Length': '5580',
|
||||||
|
'Origin': 'https://www.avancyserp.com',
|
||||||
|
'Connection': 'close',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
INVOICE = {
|
||||||
|
"datos_conexion": {},
|
||||||
|
"tipo_documento": {},
|
||||||
|
"basicos_factura": {},
|
||||||
|
"respuesta": {},
|
||||||
|
"param_basico": {},
|
||||||
|
"facturador": {},
|
||||||
|
"autorizacion_descarga": {},
|
||||||
|
"WithholdingTaxTotal": {},
|
||||||
|
"datos_empresa": {},
|
||||||
|
"datos_cliente": {},
|
||||||
|
"datos_transportadora": {},
|
||||||
|
"QR": {},
|
||||||
|
"Periodo_pago": {},
|
||||||
|
"Metodo_pago": {},
|
||||||
|
"Referencia_factura": {},
|
||||||
|
"Referencia_factura2": {},
|
||||||
|
"respuesta_discrepancia": {},
|
||||||
|
"order_de_referencia": {},
|
||||||
|
"Referencia_envio": {},
|
||||||
|
"Referencia_recibido": {},
|
||||||
|
"Terminos_de_entrega": {},
|
||||||
|
"Tasa_cambio": {},
|
||||||
|
"AdditionalDocumentReference": {},
|
||||||
|
"Anticipos": [],
|
||||||
|
"Productos_servicios": []
|
||||||
|
}
|
||||||
|
|
||||||
|
URL_XML = 'https://facturaelectronica.avancyserp.com/facturacion_electronica/v5.2/xml/formatos/GetXmlByDocumentKey.php'
|
||||||
|
URL_NUMBERING_XML = 'https://facturaelectronica.avancyserp.com/facturacion_electronica/v5.2/xml/formatos/GetNumberingRange.php'
|
||||||
|
DEFAULT_SERVICE_POST = 'https://www.avancyserp.com/response_ajax.php'
|
||||||
|
DEFAULT_SERVICE_URL = 'https://facturaelectronica.avancyserp.com/facturacion_electronica/v5.2/send.php'
|
||||||
|
DEFAULT_SERVICE_URL_GET = 'https://facturaelectronica.avancyserp.com/facturacion_electronica/v5.2/view_response.php'
|
||||||
|
AVANCYS_NIT = 900297700
|
||||||
|
DIAN_INVOICE_URL_BASE = 'https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey='
|
||||||
108
electronic_invoice_dian/models/res_company.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, fields, models
|
||||||
|
from .http_helper import DEFAULT_SERVICE_URL
|
||||||
|
from .http_helper import DEFAULT_SERVICE_URL_GET
|
||||||
|
from .http_helper import DEFAULT_SERVICE_POST
|
||||||
|
|
||||||
|
EI_DATABASE_HELP = """
|
||||||
|
Base de Datos en la cual funcionará la Facturación Electrónica"""
|
||||||
|
EI_ENVIRONMENT_HELP = """
|
||||||
|
Indicador del tipo de ambiente de Facturación Electrónica que se usará en
|
||||||
|
esta Base de Datos"""
|
||||||
|
SERVICE_URL_HELP = """URL de envio de Facturación Electrónica del Proveedor"""
|
||||||
|
SERVICE_URL_GET_HELP = """URL de Respuesta de Proveedor"""
|
||||||
|
SERVICE_URL_POST_HELP = """
|
||||||
|
URL de envio de Facturación Electrónica del Facturador"""
|
||||||
|
SOFTWARE_TOKEN_HELP = """Token de Proveedor Tecnológico"""
|
||||||
|
SOFTWARE_CODE_DIAN_HELP = """Codigo de software dado por la DIAN"""
|
||||||
|
EI_TMP_PATH_HELP = """Directorio local para archivos temporales"""
|
||||||
|
EI_AUTOMATIC_GENERATION_HELP = """
|
||||||
|
Enviar factura electrónica automáticamente al validar la factura"""
|
||||||
|
ATTACH_CUSTOMER_ORDER_HELP = """
|
||||||
|
Adjuntar en el correo la orden de compra del cliente"""
|
||||||
|
ATTACH_DELIVERY_NOTE_HELP = """Adjuntar documento de remisión de pedido"""
|
||||||
|
ATTACH_INVOICE_DOCS_HELP = """
|
||||||
|
Adjuntar documentos adicionales al pdf y xml adjuntos en la factura"""
|
||||||
|
AUTO_ACCEPTANCE_EMAIL_HELP = """Enviar correo de aceptación al cliente
|
||||||
|
una vez la factura haya quedado aceptada por la DIAN"""
|
||||||
|
EI_ID_CUSTOMIZATION_HELP = """
|
||||||
|
Tipo de Operación principal de facturación electrónica de la compañía"""
|
||||||
|
TRIBUTARY_OBLIGATIONS_HELP = """
|
||||||
|
Usadas en la construcción del XML AttachedDocument"""
|
||||||
|
|
||||||
|
|
||||||
|
class ResCompany(models.Model):
|
||||||
|
_inherit = 'res.company'
|
||||||
|
|
||||||
|
# Basic config
|
||||||
|
|
||||||
|
electronic_invoice = fields.Boolean(
|
||||||
|
string='Activar Facturación Electrónica')
|
||||||
|
ei_database = fields.Char(
|
||||||
|
string='Base de Datos', default=lambda self: self.env.cr.dbname,
|
||||||
|
required=True, help=EI_DATABASE_HELP)
|
||||||
|
ei_environment = fields.Selection(
|
||||||
|
string='Ambiente',
|
||||||
|
selection=[
|
||||||
|
('production', u'Producción'), ('test', u'Habilitación')],
|
||||||
|
default='test', required=True, help=EI_ENVIRONMENT_HELP)
|
||||||
|
ei_software_operation = fields.Selection(
|
||||||
|
string='Operación de Software',
|
||||||
|
selection=[
|
||||||
|
('own', u'Software Propio'),
|
||||||
|
('provider', u'Proveedor Tecnológico')],
|
||||||
|
default='provider', required=True)
|
||||||
|
|
||||||
|
# Server
|
||||||
|
|
||||||
|
service_url = fields.Char(
|
||||||
|
string='URL Servicio', help=SERVICE_URL_HELP,
|
||||||
|
default=DEFAULT_SERVICE_URL)
|
||||||
|
service_url_get = fields.Char(
|
||||||
|
string='URL Respuesta', help=SERVICE_URL_GET_HELP,
|
||||||
|
default=DEFAULT_SERVICE_URL_GET)
|
||||||
|
service_url_post = fields.Char(
|
||||||
|
string='URL Peticion', help=SERVICE_URL_POST_HELP,
|
||||||
|
default=DEFAULT_SERVICE_POST)
|
||||||
|
software_token = fields.Char(
|
||||||
|
string='Token', help=SOFTWARE_TOKEN_HELP)
|
||||||
|
software_code_dian = fields.Char(
|
||||||
|
string='Codigo Software DIAN', help=SOFTWARE_CODE_DIAN_HELP)
|
||||||
|
ei_tmp_path = fields.Char(
|
||||||
|
string='Ruta de Archivos Temporales',
|
||||||
|
help=EI_TMP_PATH_HELP, default='/tmp')
|
||||||
|
|
||||||
|
# Policies
|
||||||
|
|
||||||
|
ei_automatic_generation = fields.Boolean(
|
||||||
|
string='Enviar Factura al Validar',
|
||||||
|
help=EI_AUTOMATIC_GENERATION_HELP)
|
||||||
|
attach_customer_order = fields.Boolean(
|
||||||
|
string='Adjuntar OC Cliente', help=ATTACH_CUSTOMER_ORDER_HELP)
|
||||||
|
attach_delivery_note = fields.Boolean(
|
||||||
|
string='Adjuntar Remisión', help=ATTACH_DELIVERY_NOTE_HELP)
|
||||||
|
attach_invoice_docs = fields.Boolean(
|
||||||
|
string='Envíar Adjuntos de Factura',
|
||||||
|
help=ATTACH_INVOICE_DOCS_HELP)
|
||||||
|
auto_acceptance_email = fields.Boolean(
|
||||||
|
string='Email de Aceptación Automático',
|
||||||
|
help=AUTO_ACCEPTANCE_EMAIL_HELP)
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
|
||||||
|
ei_id_customization = fields.Selection(
|
||||||
|
string='Operación Principal',
|
||||||
|
selection=[('09', 'AIU'), ('10', 'Estandar'), ('11', 'Mandatos')],
|
||||||
|
required=True, default='10',
|
||||||
|
help=EI_ID_CUSTOMIZATION_HELP)
|
||||||
|
mail_server_id = fields.Many2one(
|
||||||
|
string='Servidor Email Preferido',
|
||||||
|
comodel_name='ir.mail_server')
|
||||||
|
ei_report_id = fields.Many2one(
|
||||||
|
string='Formato PDF', comodel_name='ir.actions.report',
|
||||||
|
domain=[('model', '=', 'account.move')])
|
||||||
|
tributary_obligations = fields.Char(
|
||||||
|
string='Obligaciones Tributarias',
|
||||||
|
help=TRIBUTARY_OBLIGATIONS_HELP,
|
||||||
|
default='R-99-PN')
|
||||||
|
invoice_batch_process = fields.Boolean(string='Procesamiento Masivo')
|
||||||
8
electronic_invoice_dian/models/res_partner.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
ei_email = fields.Char(string='E-mail Facturación Electrónica')
|
||||||
25
electronic_invoice_dian/models/sale_order.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
DATASIZE_RATIO = 3.0 / 4.0 # converts from len to actual filesize
|
||||||
|
MAX_FILE_SIZE = 2000000
|
||||||
|
MAX_SIZE_WARN = """
|
||||||
|
El tamaño del archivo de la Orden de Compra Cliente supera los 2Mb permitidos"""
|
||||||
|
|
||||||
|
|
||||||
|
class SaleOrder(models.Model):
|
||||||
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
|
customer_po_file = fields.Binary(
|
||||||
|
string='Orden de Compra Cliente', copy=False)
|
||||||
|
customer_po_name = fields.Char('Nombre OC Cliente', copy=False)
|
||||||
|
customer_po_policy = fields.Boolean(
|
||||||
|
string='Política Envío OC', related='company_id.attach_customer_order',
|
||||||
|
readonly=True)
|
||||||
|
|
||||||
|
@api.constrains('customer_po_file')
|
||||||
|
def _check_file_size(self):
|
||||||
|
if self.customer_po_file:
|
||||||
|
if len(self.customer_po_file) * DATASIZE_RATIO > MAX_FILE_SIZE:
|
||||||
|
raise ValidationError(MAX_SIZE_WARN)
|
||||||
8
electronic_invoice_dian/models/uom_uom.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class UomUom(models.Model):
|
||||||
|
_inherit = 'uom.uom'
|
||||||
|
|
||||||
|
code_dian = fields.Char(string='Código DIAN')
|
||||||
172
electronic_invoice_dian/models/xml_helper.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# pylint: disable=invalid-name, bare-except, eval-used, cell-var-from-loop
|
||||||
|
# pylint: disable=unused-wildcard-import,wildcard-import,too-many-locals
|
||||||
|
"""
|
||||||
|
XML Helper to build invoice
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from xml.etree.ElementTree import SubElement as ETSE
|
||||||
|
from xml.etree.ElementTree import Element as ETE
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from .xml_schemas import ATTDOC_ATTRS
|
||||||
|
from .xml_schemas import Prefix as pfx
|
||||||
|
from .xml_schemas import NAMESPACES
|
||||||
|
|
||||||
|
for ns in NAMESPACES:
|
||||||
|
ET.register_namespace(ns, NAMESPACES[ns])
|
||||||
|
|
||||||
|
|
||||||
|
def set_document_root(ET, doc_type):
|
||||||
|
"""Attachem Document Creation"""
|
||||||
|
AttachedDocument = ET.Element(
|
||||||
|
doc_type,
|
||||||
|
attrib=ATTDOC_ATTRS
|
||||||
|
)
|
||||||
|
return AttachedDocument
|
||||||
|
|
||||||
|
|
||||||
|
def set_header(AttachedDocument, tags, tag_attributes):
|
||||||
|
UBLVersionID = ET.SubElement(AttachedDocument, pfx.cbc + 'UBLVersionID')
|
||||||
|
CustomizationID = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cbc + 'CustomizationID')
|
||||||
|
ProfileID = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cbc + 'ProfileID')
|
||||||
|
ProfileExecutionID = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cbc + 'ProfileExecutionID')
|
||||||
|
ID = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cbc + 'ID')
|
||||||
|
IssueDate = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cbc + 'IssueDate')
|
||||||
|
IssueTime = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cbc + 'IssueTime')
|
||||||
|
DocumentType = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cbc + 'DocumentType')
|
||||||
|
ParentDocumentID = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cbc + 'ParentDocumentID')
|
||||||
|
for tag in tags:
|
||||||
|
try:
|
||||||
|
eval(tag).text = tags[tag]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return AttachedDocument
|
||||||
|
|
||||||
|
|
||||||
|
def set_party(Party, tags, tag_attributes):
|
||||||
|
PartyTaxScheme = ET.SubElement(
|
||||||
|
Party, pfx.cac + 'PartyTaxScheme')
|
||||||
|
RegistrationName = ET.SubElement(
|
||||||
|
PartyTaxScheme, pfx.cbc + 'RegistrationName')
|
||||||
|
CompanyID = ET.SubElement(
|
||||||
|
PartyTaxScheme, pfx.cbc + 'CompanyID',
|
||||||
|
attrib=tag_attributes.get('CompanyID', {}))
|
||||||
|
TaxLevelCode = ET.SubElement(
|
||||||
|
PartyTaxScheme, pfx.cbc + 'TaxLevelCode',
|
||||||
|
attrib=tag_attributes.get('TaxLevelCode', {}))
|
||||||
|
TaxScheme = ET.SubElement(
|
||||||
|
PartyTaxScheme, pfx.cac + 'TaxScheme')
|
||||||
|
ID = ET.SubElement(
|
||||||
|
TaxScheme, pfx.cbc + 'ID')
|
||||||
|
Name = ET.SubElement(
|
||||||
|
TaxScheme, pfx.cbc + 'Name')
|
||||||
|
for tag in tags:
|
||||||
|
try:
|
||||||
|
eval(tag).text = tags[tag]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return Party
|
||||||
|
|
||||||
|
|
||||||
|
def set_sender_party(AttachedDocument, values, tag_attributes):
|
||||||
|
SenderParty = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cac + 'SenderParty')
|
||||||
|
set_party(SenderParty, values, tag_attributes)
|
||||||
|
return AttachedDocument
|
||||||
|
|
||||||
|
|
||||||
|
def set_receiver_party(AttachedDocument, values, tag_attributes):
|
||||||
|
ReceiverParty = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cac + 'ReceiverParty')
|
||||||
|
set_party(ReceiverParty, values, tag_attributes)
|
||||||
|
return AttachedDocument
|
||||||
|
|
||||||
|
|
||||||
|
def set_attchment(Document, tags, tag_attributes):
|
||||||
|
Attachment = ET.SubElement(
|
||||||
|
Document, pfx.cac + 'Attachment')
|
||||||
|
ExternalReference = ET.SubElement(
|
||||||
|
Attachment, pfx.cac + 'ExternalReference')
|
||||||
|
MimeCode = ET.SubElement(
|
||||||
|
ExternalReference, pfx.cbc + 'MimeCode')
|
||||||
|
EncodingCode = ET.SubElement(
|
||||||
|
ExternalReference, pfx.cbc + 'EncodingCode')
|
||||||
|
Description = ET.SubElement(
|
||||||
|
ExternalReference, pfx.cbc + 'Description')
|
||||||
|
for tag in tags:
|
||||||
|
try:
|
||||||
|
eval(tag).text = tags[tag]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return Document
|
||||||
|
|
||||||
|
|
||||||
|
def set_parent_document_line_reference(AttachedDocument, tags, tag_attributes):
|
||||||
|
ParentDocumentLineReference = ET.SubElement(
|
||||||
|
AttachedDocument, pfx.cac + 'ParentDocumentLineReference')
|
||||||
|
LineID = ET.SubElement(
|
||||||
|
ParentDocumentLineReference, pfx.cbc + 'LineID')
|
||||||
|
DocumentReference = ET.SubElement(
|
||||||
|
ParentDocumentLineReference, pfx.cac + 'DocumentReference')
|
||||||
|
ID = ET.SubElement(DocumentReference, pfx.cbc + 'ID')
|
||||||
|
UUID = ET.SubElement(DocumentReference, pfx.cbc + 'UUID',
|
||||||
|
attrib=tag_attributes.get('UUID', {}))
|
||||||
|
IssueDate = ET.SubElement(DocumentReference, pfx.cbc + 'IssueDate')
|
||||||
|
DocumentType = ET.SubElement(DocumentReference, pfx.cbc + 'DocumentType')
|
||||||
|
set_attchment(DocumentReference, tags, tag_attributes)
|
||||||
|
ResultOfVerification = ET.SubElement(
|
||||||
|
DocumentReference, pfx.cac + 'ResultOfVerification')
|
||||||
|
ValidatorID = ET.SubElement(
|
||||||
|
ResultOfVerification, pfx.cbc + 'ValidatorID')
|
||||||
|
ValidationResultCode = ET.SubElement(
|
||||||
|
ResultOfVerification, pfx.cbc + 'ValidationResultCode')
|
||||||
|
ValidationDate = ET.SubElement(
|
||||||
|
ResultOfVerification, pfx.cbc + 'ValidationDate')
|
||||||
|
ValidationTime = ET.SubElement(
|
||||||
|
ResultOfVerification, pfx.cbc + 'ValidationTime')
|
||||||
|
for tag in tags:
|
||||||
|
try:
|
||||||
|
eval(tag).text = tags[tag]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return AttachedDocument
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_utf_es(text):
|
||||||
|
chars = {
|
||||||
|
'ñ': u'ñ', 'á': u'á', 'é': u'é', 'í': u'í',
|
||||||
|
'ó': u'ó', 'ú': u'ú', 'Á': u'Á', 'É': u'É',
|
||||||
|
'Í': u'Í', 'Ó': u'Ó', 'Ú': u'Ú'}
|
||||||
|
sanitized_text = text
|
||||||
|
for es_char, utf_char in chars.items():
|
||||||
|
sanitized_text = sanitized_text.replace(es_char, utf_char)
|
||||||
|
return sanitized_text
|
||||||
|
|
||||||
|
|
||||||
|
def build_xml_attached_document(xml_fe, app_response, values):
|
||||||
|
xml_declaration = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n'
|
||||||
|
document = set_document_root(ET, doc_type='AttachedDocument')
|
||||||
|
document = set_header(
|
||||||
|
document, values['header']['tags'], values['header']['attrs'])
|
||||||
|
document = set_sender_party(
|
||||||
|
document, values['sender']['tags'], values['sender']['attrs'])
|
||||||
|
document = set_receiver_party(
|
||||||
|
document, values['receiver']['tags'], values['receiver']['attrs'])
|
||||||
|
document = set_attchment(
|
||||||
|
document, values['attachment']['tags'], values['attachment']['attrs'])
|
||||||
|
document = set_parent_document_line_reference(
|
||||||
|
document, values['doc_line']['tags'], values['doc_line']['attrs'])
|
||||||
|
# return document
|
||||||
|
doc_string = xml_declaration + ET.tostring(document).decode('utf-8').replace(
|
||||||
|
'ei_xml_content', '<![CDATA[%s]]>' % xml_fe).replace(
|
||||||
|
'ei_app_response', '<![CDATA[%s]]>' % app_response)
|
||||||
|
return sanitize_utf_es(doc_string).encode('utf-8')
|
||||||
40
electronic_invoice_dian/models/xml_schemas.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"""
|
||||||
|
Namespaces declarations
|
||||||
|
"""
|
||||||
|
|
||||||
|
NAMESPACES = {
|
||||||
|
'ds': "http://www.w3.org/2000/09/xmldsig#",
|
||||||
|
'cac': "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2",
|
||||||
|
'cbc': "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2",
|
||||||
|
'ccts': "urn:un:unece:uncefact:data:specification:CoreComponentTypeSchemaModule:2",
|
||||||
|
'ext': "urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2",
|
||||||
|
'xades': "http://uri.etsi.org/01903/v1.3.2#",
|
||||||
|
'xades141': "http://uri.etsi.org/01903/v1.4.1#"
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTDOC_ATTRS = {
|
||||||
|
"xmlns:ext": "urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2",
|
||||||
|
"xmlns": "urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2",
|
||||||
|
}
|
||||||
|
cac = '{%s}' % NAMESPACES['cac']
|
||||||
|
cbc = '{%s}' % NAMESPACES['cbc']
|
||||||
|
ccts = '{%s}' % NAMESPACES['ccts']
|
||||||
|
ext = '{%s}' % NAMESPACES['ext']
|
||||||
|
xades = '{%s}' % NAMESPACES['xades']
|
||||||
|
xades141 = '{%s}' % NAMESPACES['xades141']
|
||||||
|
ds = 'ds:'
|
||||||
|
|
||||||
|
|
||||||
|
class DictMap(dict):
|
||||||
|
__getattr__ = dict.__getitem__
|
||||||
|
|
||||||
|
|
||||||
|
PREFIXMAP = {
|
||||||
|
'cac': cac,
|
||||||
|
'cbc': cbc,
|
||||||
|
'ccts': ccts,
|
||||||
|
'ext': ext,
|
||||||
|
'xades': xades,
|
||||||
|
'xades141': xades141,
|
||||||
|
}
|
||||||
|
Prefix = DictMap(PREFIXMAP)
|
||||||
17
electronic_invoice_dian/report/electronic_invoice_report.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<report
|
||||||
|
id="electronic_invoice_report"
|
||||||
|
model="account.move"
|
||||||
|
string="Factura Electrónica DIAN"
|
||||||
|
report_type="qweb-pdf"
|
||||||
|
name="electronic_invoice_dian.report_electronic_invoice"
|
||||||
|
file="electronic_invoice_dian.report_electronic_invoice"
|
||||||
|
attachment="(object.state == 'posted') and ((object.name or 'INV').replace('/','_')+'.pdf')"
|
||||||
|
print_report_name="(object._get_report_base_filename())"
|
||||||
|
groups="electronic_invoice_dian.group_user"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
11
electronic_invoice_dian/security/ir.model.access.csv
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_electronic_invoice_resolution_manager,access_electronic_invoice_resolution_manager,model_electronic_invoice_resolution,electronic_invoice_dian.group_manager,1,1,1,1
|
||||||
|
access_electronic_invoice_resolution_user,access_electronic_invoice_resolution_user,model_electronic_invoice_resolution,electronic_invoice_dian.group_user,1,1,0,0
|
||||||
|
access_electronic_invoice_resolution_invoice_user,access_electronic_invoice_resolution_user,model_electronic_invoice_resolution,account.group_account_invoice,1,1,0,0
|
||||||
|
access_ei_transaction_log_manager,access_ei_transaction_log_manager,model_ei_transaction_log,electronic_invoice_dian.group_manager,1,1,1,1
|
||||||
|
access_ei_transaction_log_user,access_ei_transaction_log_user,model_ei_transaction_log,electronic_invoice_dian.group_user,1,1,0,0
|
||||||
|
access_ei_transaction_log_invoice_user,access_ei_transaction_log_user,model_ei_transaction_log,account.group_account_invoice,1,1,0,0
|
||||||
|
access_ei_multi_process_manager,access_ei_multi_process_manager,model_ei_multi_process,electronic_invoice_dian.group_manager,1,1,1,1
|
||||||
|
access_ei_multi_process_user,access_ei_multi_process_user,model_ei_multi_process,electronic_invoice_dian.group_user,1,1,1,0
|
||||||
|
access_ei_multi_process_invoice_user,access_ei_multi_process_user,model_ei_multi_process,account.group_account_invoice,1,1,1,0
|
||||||
|
access_ei_state_reset_manager,access_ei_state_Reset_manager,model_ei_state_reset,electronic_invoice_dian.group_manager,1,1,1,1
|
||||||
|
24
electronic_invoice_dian/security/security.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record model="ir.module.category" id="module_category_electronic_invoice_dian">
|
||||||
|
<field name="name">Facturación Electrónica DIAN</field>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_manager">
|
||||||
|
<field name="name">Administrador</field>
|
||||||
|
<field name="category_id" ref="module_category_electronic_invoice_dian" />
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]" />
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_user">
|
||||||
|
<field name="name">Usuario</field>
|
||||||
|
<field name="category_id" ref="module_category_electronic_invoice_dian" />
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]" />
|
||||||
|
</record>
|
||||||
|
<record model="ir.rule" id="electronic_invoice_resolution_rule">
|
||||||
|
<field name="name">Resolución de Facturación Electronica: multi-company</field>
|
||||||
|
<field name="model_id" ref="model_electronic_invoice_resolution" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
BIN
electronic_invoice_dian/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,60 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record id="electronic_invoice_customer_acknowlegement" model="mail.template">
|
||||||
|
<field name="name">Envio de factura electrónica</field>
|
||||||
|
<field name="email_from">${object.company_id.email or ''}</field>
|
||||||
|
<field name="subject">${object.company_id.partner_id.ref_num};${object.company_id.partner_id.name};${object.name};${object.journal_id.resolution_id.document_type};${object.company_id.partner_id.name}</field>
|
||||||
|
<field name="model_id" ref="account.model_account_move" />
|
||||||
|
<field name="email_to">${(object.partner_id.ei_email or object.partner_id.email)}</field>
|
||||||
|
<field name="partner_to">${False}</field>
|
||||||
|
<field name="auto_delete" eval="True" />
|
||||||
|
<field name="body_html">
|
||||||
|
<![CDATA[
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
|
<title>${object.name}</title>
|
||||||
|
<style>
|
||||||
|
span.oe_mail_footer_access {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div
|
||||||
|
style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
|
||||||
|
<div style="height:auto;text-align: center;font-size : 30px;color: #8A89BA;">
|
||||||
|
Factura electronica <strong>${object.name}</strong>
|
||||||
|
</div>
|
||||||
|
<div style="height: 50px;text-align: left;font-size : 14px;border-collapse: separate;margin-top:10px">
|
||||||
|
<strong style="margin-left:12px">Estimado: ${object.partner_id.name}</strong>,<br />
|
||||||
|
<p style="margin-left:12px">Se ha generado un factura electrónica a su nombre</p>
|
||||||
|
<strong style="margin-left:12px">Emisor: ${object.company_id.partner_id.name}</strong>
|
||||||
|
<p style="margin-left:12px">Adjunto encontrará los archivos de la factura, por favor indique la
|
||||||
|
aceptación o rechazo de la misma</p>
|
||||||
|
</div>
|
||||||
|
<br><br><br><br>
|
||||||
|
|
||||||
|
<div style="height: auto;width:450px; margin:0 auto;padding-top:20px;padding-bottom:40px;">
|
||||||
|
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#3b9c17;margin : 0 15px 0 0;text-decoration: none;color:#FFFFFF;" href="/invoice/dian/accept?db=${'dbname' in ctx and ctx['dbname'] or ''}&token=${object.access_token}&id=${object.id}">Aceptar</a>
|
||||||
|
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#d66058;margin : 0 15px 0 0;text-decoration: none;color:#FFFFFF;" href="/invoice/dian/reject?db=${'dbname' in ctx and ctx['dbname'] or '' }&token=${object.access_token}&id=${object.id}">Rechazar</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="height: 50px;text-align: center;font-size : 10px;border-collapse: separate;margin-top:10px">
|
||||||
|
<p style="margin-left:12px">
|
||||||
|
Por Avancys SAS <a href="https://www.avancys.com">https://www.avancys.com</a> Proveedor tecnologico.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
]]>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<template id="customer_accept_invoice">
|
||||||
|
|
||||||
|
<title>Factura Aceptada</title>
|
||||||
|
|
||||||
|
<div style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
|
||||||
|
<div style="height:auto;text-align: center;font-size : 30px;color: #3b9c17;">
|
||||||
|
<strong>Factura Aceptada</strong>
|
||||||
|
</div>
|
||||||
|
<div style="height: 50px;text-align: center;font-size : 10px;border-collapse: separate;margin-top:10px">
|
||||||
|
<p style="margin-left:12px">
|
||||||
|
Por Avancys SAS <a href="https://www.avancys.com">https://www.avancys.com</a> Proveedor tecnologico.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<template id="customer_reject_invoice">
|
||||||
|
|
||||||
|
<title>Factura Rechazada</title>
|
||||||
|
|
||||||
|
<div style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
|
||||||
|
<div style="height:auto;text-align: center;font-size : 30px;color: #d66058;">
|
||||||
|
<strong>Factura Rechazada</strong>
|
||||||
|
</div>
|
||||||
|
<div style="height: 50px;text-align: center;font-size : 10px;border-collapse: separate;margin-top:10px">
|
||||||
|
<p style="margin-left:12px">
|
||||||
|
Por Avancys SAS <a href="https://www.avancys.com">https://www.avancys.com</a> Proveedor tecnologico.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
396
electronic_invoice_dian/templates/electronic_invoice.xml
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
|
||||||
|
<template id="report_electronic_invoice_document">
|
||||||
|
<t t-call="web.basic_layout">
|
||||||
|
<style>
|
||||||
|
.infolabel {
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
div.infolabel {
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
div.infolabelbig {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
span.infodata{
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
span.infodatabottom{
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
strong.infodatabottom{
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
tr.inforow {
|
||||||
|
line-height: 25px;
|
||||||
|
min-height: 25px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
.borderless td, .borderless tr {
|
||||||
|
border: none;
|
||||||
|
line-height: 15px;
|
||||||
|
min-height: 15px;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="header">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3">
|
||||||
|
<img t-if="o.company_id.logo" t-att-src="image_data_uri(o.company_id.logo)" width="130" height="auto" />
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.partner_id.name" />
|
||||||
|
<span style="font-size:10px">NIT </span><span style="font-size:10px" t-field="o.company_id.partner_id.ref_num" />
|
||||||
|
<br/>
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.street" />
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.street2" />
|
||||||
|
<br/>
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.partner_id.city_id.name" />
|
||||||
|
,
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.partner_id.country_id.name" />
|
||||||
|
<br/>
|
||||||
|
<span style="font-size:10px">TELEFONO </span><span style="font-size:10px" t-field="o.company_id.phone" />
|
||||||
|
<span style="font-size:10px" t-if="o.company_id.partner_id.mobile">/</span>
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.partner_id.mobile" />
|
||||||
|
<br/>
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.email" />
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
</div>
|
||||||
|
<div t-if="o.ei_qr" class="col-3">
|
||||||
|
<img t-att-src="'/report/barcode/?type=QR&value=%s&width=%s&height=%s' % (o.ei_qr, 110, 110)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6 card card-body">
|
||||||
|
<table class="table borderless">
|
||||||
|
<tr class="inforow">
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
CLIENTE
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.name" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
NIT
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.ref_num" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
DIRECCIÓN
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.street" />
|
||||||
|
<span class="infodata" t-field="o.partner_id.street2" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
CIUDAD
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.city_id.name" />
|
||||||
|
-
|
||||||
|
<span class="infodata" t-field="o.partner_id.country_id.name" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
TELEFONO
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.phone" />
|
||||||
|
<span class="infodata" t-if="o.partner_id.mobile">/</span>
|
||||||
|
<span class="infodata" t-field="o.partner_id.mobile" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
EMAIL
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.email" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
REFERENCIA
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.ref" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 card">
|
||||||
|
<div class="infolabelbig card-header">
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and o.ei_cufe">FACTURA ELECTRÓNICA DE VENTA</span>
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and not o.ei_cufe">FACTURA DE VENTA</span>
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and o.ei_cude">NOTA DÉBITO ELECTRONICA</span>
|
||||||
|
<span t-if="o.move_type == 'out_refund' and o.ei_cude">NOTA CRÉDITO ELECTRONICA</span>
|
||||||
|
<span t-if="o.move_type == 'out_refund' and not o.ei_cude">NOTA CRÉDITO</span>
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and not o.ei_cude and o.debit_origin_id">NOTA DÉBITO</span>
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and o.state == 'cancel'">CANCELADA</span>
|
||||||
|
<span t-if="o.move_type == 'in_invoice'">DOCUMENTO DE SOPORTE</span>
|
||||||
|
<span t-if="o.name != '/'" t-field="o.name"/>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table borderless">
|
||||||
|
<tr class="inforow">
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
FECHA DE EMISION
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.ei_generation_date" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
FECHA DE VENCIMIENTO
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.invoice_date_due" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr t-if="o.invoice_payment_term_id">
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
TERMINO DE PAGO
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.invoice_payment_term_id.name" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
<t t-if="o.ei_cufe">
|
||||||
|
<div class="col-12" style="font-size:8px; float:left; text-align:left; white-space: pre;"><strong>CUFE : </strong><span t-field="o.ei_cufe" /></div>
|
||||||
|
</t>
|
||||||
|
<t t-if="o.ei_cude">
|
||||||
|
<div class="col-12" style="font-size:8px; float:left; text-align:left; white-space: pre;"><strong>CUDE : </strong><span t-field="o.ei_cude" /></div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
|
||||||
|
<t t-set="display_discount" t-value="any(l.discount for l in o.invoice_line_ids)"/>
|
||||||
|
|
||||||
|
<table class="table table-sm o_main_table" name="invoice_line_table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th name="th_defaultcode" class="text-left"><span class="infolabel">N°</span></th>
|
||||||
|
<th name="th_defaultcode" class="text-left"><span class="infolabel">REFERENCIA</span></th>
|
||||||
|
<th name="th_description" class="text-left"><span class="infolabel">DESCRIPCIÓN</span></th>
|
||||||
|
<th name="th_quantity" class="text-right"><span class="infolabel">CANTIDAD</span></th>
|
||||||
|
<th name="th_priceunit" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span class="infolabel">PRECIO UNIT.</span></th>
|
||||||
|
<th name="th_price_unit" t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||||
|
<span class="infolabel">DESC.%</span>
|
||||||
|
</th>
|
||||||
|
<th name="th_taxes" t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span class="infolabel">IMPUESTOS</span></th>
|
||||||
|
<th name="th_subtotal" class="text-right">
|
||||||
|
<span groups="account.group_show_line_subtotals_tax_excluded" class="infolabel">VALOR</span>
|
||||||
|
<span groups="account.group_show_line_subtotals_tax_included" class="infolabel">PRECIO TOTAL</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="invoice_tbody">
|
||||||
|
<t t-set="current_subtotal" t-value="0"/>
|
||||||
|
<t t-set="lines" t-value="o.invoice_line_ids.sorted(key=lambda l: (-l.sequence, l.date, l.move_name, -l.id), reverse=True)"/>
|
||||||
|
<t t-set="n_item" t-value="0"/>
|
||||||
|
<t t-foreach="lines" t-as="line">
|
||||||
|
<t t-set="n_item" t-value="n_item+1"/>
|
||||||
|
<t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||||
|
<t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
|
||||||
|
|
||||||
|
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
|
||||||
|
<t t-if="not line.display_type" name="account_invoice_line_accountable">
|
||||||
|
<td name="account_invoice_line_number"><span class="infodatabottom" t-esc="n_item"/></td>
|
||||||
|
<td name="account_invoice_default_code"><span class="infodatabottom" t-field="line.product_id.default_code" t-options="{'widget': 'text'}"/></td>
|
||||||
|
<td name="account_invoice_line_name"><span class="infodatabottom" t-field="line.name" t-options="{'widget': 'text'}"/></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span class="infodatabottom" t-field="line.quantity"/>
|
||||||
|
<span class="infodatabottom" t-field="line.product_uom_id" groups="uom.group_uom"/>
|
||||||
|
</td>
|
||||||
|
<td t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="line.price_unit"/>
|
||||||
|
</td>
|
||||||
|
<td t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="line.discount"/>
|
||||||
|
</td>
|
||||||
|
<td t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||||
|
<span class="infodatabottom" t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_ids))" id="line_tax_ids"/>
|
||||||
|
</td>
|
||||||
|
<td class="text-right o_price_total">
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
<t t-if="line.display_type == 'line_section'">
|
||||||
|
<td colspan="99">
|
||||||
|
<span t-field="line.name" t-options="{'widget': 'text'}"/>
|
||||||
|
</td>
|
||||||
|
<t t-set="current_section" t-value="line"/>
|
||||||
|
<t t-set="current_subtotal" t-value="0"/>
|
||||||
|
</t>
|
||||||
|
<t t-if="line.display_type == 'line_note'">
|
||||||
|
<td colspan="99">
|
||||||
|
<span t-field="line.name" t-options="{'widget': 'text'}"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<t t-if="current_section and (line_last or lines[line_index+1].display_type == 'line_section')">
|
||||||
|
<tr class="is-subtotal text-right">
|
||||||
|
<td colspan="99">
|
||||||
|
<strong class="mr16">SUBTOTAL</strong>
|
||||||
|
<span t-esc="current_subtotal" t-options="{"widget": "monetary", "display_currency": o.currency_id}"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div>
|
||||||
|
<span class="infodata" t-esc="n_item"></span> <span class="infodata" >Items</span>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div class="clearfix">
|
||||||
|
<div id="total" class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<t t-if="o.journal_id.resolution_id.description">
|
||||||
|
<div class="col-12" style="font-size:10px; float:left; text-align:left;"><span t-field="o.journal_id.resolution_id.description" /></div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-6">
|
||||||
|
<table class="table table-sm" style="page-break-inside: avoid;">
|
||||||
|
<tr class="border-black o_subtotal" style="">
|
||||||
|
<td><strong class="infodatabottom">SUBTOTAL</strong></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span class="infodatabottom" t-field="o.amount_untaxed"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<t t-foreach="o.amount_by_group" t-as="amount_by_group">
|
||||||
|
<tr style="">
|
||||||
|
<t t-if="len(o.line_ids.filtered(lambda line: line.tax_line_id)) in [0, 1] and o.amount_untaxed == amount_by_group[2]">
|
||||||
|
<td><span class="text-nowrap infodatabottom" t-esc="amount_by_group[0]"/></td>
|
||||||
|
<td class="text-right o_price_total">
|
||||||
|
<span class="text-nowrap infodatabottom" t-esc="amount_by_group[3]"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<td>
|
||||||
|
<span class="infodatabottom" t-esc="amount_by_group[0]"/>
|
||||||
|
<span class="text-nowrap infodatabottom"> sobre
|
||||||
|
<t t-esc="amount_by_group[4]"/>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right o_price_total">
|
||||||
|
<span class="text-nowrap infodatabottom" t-esc="amount_by_group[3]"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
<tr class="border-black o_total">
|
||||||
|
<td><strong class="infodatabottom">TOTAL</strong></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="o.amount_total"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p t-if="o.narration" name="comment">
|
||||||
|
<span class="infodata" t-field="o.narration"/>
|
||||||
|
</p>
|
||||||
|
<p t-if="o.fiscal_position_id.note" name="note">
|
||||||
|
<span class="infodata" t-field="o.fiscal_position_id.note"/>
|
||||||
|
</p>
|
||||||
|
<p t-if="o.invoice_incoterm_id" name="incoterm">
|
||||||
|
<strong class="infodatabottom">Incoterm: </strong><span class="infodata" t-field="o.invoice_incoterm_id.code"/> - <span class="infodata" t-field="o.invoice_incoterm_id.name"/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<t t-if="bool(o.ei_cufe or o.ei_cude)">
|
||||||
|
<span style="font-size:9px"><b>FECHA DE VALIDACION : </b></span><span style="font-size:9px" t-esc="o.ei_validation_date"/><br/>
|
||||||
|
<span style="font-size:9px"><b>INFORMACIÓN : </b></span><a style="font-size:9px">https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=<span t-esc="o.ei_cufe or o.ei_cude" /></a>
|
||||||
|
</t>
|
||||||
|
<div class="text-center border-top" >
|
||||||
|
<small>
|
||||||
|
<strong style="font-size:10px">
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.website" />
|
||||||
|
</strong>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="text-left">
|
||||||
|
<p style="font-size:9px; margin-bottom: 10px">AvancysERP, por Avancys SAS, NIT 900297700</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="report_electronic_invoice">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<t t-set="lang" t-value="o.invoice_user_id.sudo().lang if o.move_type in ('in_invoice', 'in_refund') else o.partner_id.lang"/>
|
||||||
|
<t t-call="electronic_invoice_dian.report_electronic_invoice_document" t-lang="lang"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
22
electronic_invoice_dian/views/account_journal.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="view_account_journal_form" model="ir.ui.view">
|
||||||
|
<field name="name">account.journal.ei.view.form</field>
|
||||||
|
<field name="model">account.journal</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_journal_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[@name='advanced_settings']" position="after">
|
||||||
|
<page string="Facturación Electrónica" groups="electronic_invoice_dian.group_manager">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="resolution_id" />
|
||||||
|
</group>
|
||||||
|
<group></group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
66
electronic_invoice_dian/views/account_move.xml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record id="view_move_form" model="ir.ui.view">
|
||||||
|
<field name="name">account.move.ei.view.form</field>
|
||||||
|
<field name="model">account.move</field>
|
||||||
|
<field name="inherit_id" ref="account.view_move_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[@name='other_info']" position="after">
|
||||||
|
<page string="Factura Electrónica" attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund'))]}" groups="electronic_invoice_dian.group_manager,electronic_invoice_dian.group_user">
|
||||||
|
<button name="generate_electronic_invoice" type="object" string="Enviar Factura" class="oe_stat_button" icon="fa-upload" attrs="{'invisible': ['|', ('ei_state', 'not in', ('pending','dian_reject','supplier_reject','exception')), ('state','not in',('open','paid','posted'))]}" />
|
||||||
|
<button name="get_invoice_attachments" type="object" string="Recargar Adjuntos" class="oe_stat_button" icon="fa-refresh" attrs="{'invisible': ['|', ('ei_state', 'in', ('customer_reject','customer_accept')), ('state','not in',('open','paid','posted'))]}" help="Cargar archivos XML-PDF para esta factura" />
|
||||||
|
<button name="resend_acknowlegement_email" type="object" string="Enviar FE por Email" class="oe_stat_button" icon="fa-envelope-o" attrs="{'invisible': ['|', ('ei_state', '!=', 'dian_accept'), ('state', 'not in', ('open','paid','posted'))]}" style="float: right;" />
|
||||||
|
<group>
|
||||||
|
<field name="ei_cufe" attrs="{'invisible':['|', '|', ('reversed_entry_id', '!=', False), ('debit_origin_id', '!=', False), ('out_refund_id', '!=', False)]}" />
|
||||||
|
<field name="ei_cude" attrs="{'invisible':[('reversed_entry_id', '=', False), ('debit_origin_id', '=', False), ('out_refund_id', '=', False)]}" />
|
||||||
|
<field name="ei_qr" />
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="ei_generation_date" />
|
||||||
|
<!-- <field name="ei_validation_date" /> -->
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<label for="ei_state" />
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="ei_state" />
|
||||||
|
<button name="%(electronic_invoice_dian.action_ei_state_reset)d" type="action" class="oe_link" attrs="{'invisible': [('ei_state', '=', 'pending')]}" groups="electronic_invoice_dian.group_manager" icon="fa-rotate-left" help="Revertir Estado" />
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group string="Logs">
|
||||||
|
<field name="transaction_log_ids" readonly="1" nolabel="1">
|
||||||
|
<tree>
|
||||||
|
<field name="date" />
|
||||||
|
<field name="document_type" />
|
||||||
|
<field name="document_state" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='invoice_vendor_bill_id']" position="after">
|
||||||
|
<field name="out_refund_id" domain="[('partner_id', '=', partner_id)]" attrs="{'invisible':[('move_type', '!=', 'out_refund')]}" />
|
||||||
|
<field name="invoice_reference" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_account_invoice_filter" model="ir.ui.view">
|
||||||
|
<field name="name">account.move.ei.search.form</field>
|
||||||
|
<field name="model">account.move</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_invoice_filter" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//group/filter[@name='duedate']" position="after">
|
||||||
|
<filter name="group_by_ei_state" string="Estado FE" context="{'group_by':'ei_state'}" />
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//filter[@name='due_date']" position="after">
|
||||||
|
<separator />
|
||||||
|
<filter name="eifailed" string="FE Fallidas" domain="[('ei_state', 'in', ('exception', 'dian_deject'))]" />
|
||||||
|
<filter name="eirejected" string="FE Rechazadas" domain="[('ei_state', '=', 'customer_reject')]" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
15
electronic_invoice_dian/views/account_tax.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="view_tax_form" model="ir.ui.view">
|
||||||
|
<field name="name">account.tax.ei.view.form</field>
|
||||||
|
<field name="model">account.tax</field>
|
||||||
|
<field name="inherit_id" ref="account.view_tax_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='company_id']" position="before">
|
||||||
|
<field name="ei_code" groups="electronic_invoice_dian.group_user,electronic_invoice_dian.group_manager" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
30
electronic_invoice_dian/views/ei_transaction_log.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record id="ei_transaction_log_form" model="ir.ui.view">
|
||||||
|
<field name="name">ei.transaction.log.form</field>
|
||||||
|
<field name="model">ei.transaction.log</field>
|
||||||
|
<field name='type'>form</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Log de Factura Electrónica" create="false" edit="false">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="date" />
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="document_state" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<notebook>
|
||||||
|
<page string="Contenido">
|
||||||
|
<field name="data" />
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record id="electronic_invoice_resolution_view_form" model="ir.ui.view">
|
||||||
|
<field name="name">electronic.invoice.resolution.view.form</field>
|
||||||
|
<field name="model">electronic.invoice.resolution</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="name" />
|
||||||
|
<field name="document_type" />
|
||||||
|
<field name="number" attrs="{'invisible':[('document_type', 'in', ('91', '92'))]}" />
|
||||||
|
<field name="prefix" />
|
||||||
|
<field name="from_number" attrs="{'invisible':[('document_type', 'in', ('91', '92'))]}" />
|
||||||
|
<field name="to_number" attrs="{'invisible':[('document_type', 'in', ('91', '92'))]}" />
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="id_param" />
|
||||||
|
<field name="valid_date_from" attrs="{'invisible':[('document_type', 'in', ('91', '92'))]}" />
|
||||||
|
<field name="valid_date_to" attrs="{'invisible':[('document_type', 'in', ('91', '92'))]}" />
|
||||||
|
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<label for="technical_key" attrs="{'invisible':[('document_type', 'in', ('91', '92'))]}" />
|
||||||
|
<div class="o_row" attrs="{'invisible':[('document_type', 'in', ('91', '92'))]}">
|
||||||
|
<field name="technical_key" />
|
||||||
|
<button name="get_technical_key" type="object" class="oe_link" groups="electronic_invoice_dian.group_manager" icon="fa-key" help="Consultar Clave Técnica" />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<label for="description"></label>
|
||||||
|
<field name="description" />
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="electronic_invoice_resolution_view_tree" model="ir.ui.view">
|
||||||
|
<field name="name">electronic.invoice.resolution.view.tree</field>
|
||||||
|
<field name="model">electronic.invoice.resolution</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree>
|
||||||
|
<field name="name" />
|
||||||
|
<field name="number" />
|
||||||
|
<field name="prefix" />
|
||||||
|
<field name="from_number" />
|
||||||
|
<field name="to_number" />
|
||||||
|
<field name="valid_date_from" />
|
||||||
|
<field name="valid_date_to" />
|
||||||
|
<field name="document_type" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="electronic_invoice_resolution_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Resolución de Numeración</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">electronic.invoice.resolution</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="oe_view_nocontent_create">
|
||||||
|
Crear Resolución de Facturación Electŕonica
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Las Resoluciones de numeración son expedidas por la DIAN
|
||||||
|
y son usadas por cada diario de facturación electrónica
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<menuitem id="electronic_invoice_resolution_menu_act" name="Resoluciónes" parent="electronic_invoice_dian.electronic_invoice_menu" action="electronic_invoice_dian.electronic_invoice_resolution_action" sequence="1" />
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
63
electronic_invoice_dian/views/res_company.xml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record id="view_company_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.company.ei.view.form</field>
|
||||||
|
<field name="model">res.company</field>
|
||||||
|
<field name="inherit_id" ref="base.view_company_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[@name='general_info']" position="after">
|
||||||
|
<page string="Facturación Electrónica" groups="electronic_invoice_dian.group_manager">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="electronic_invoice" />
|
||||||
|
<field name="ei_database" attrs="{'required': [('electronic_invoice', '=', True)]}" />
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="ei_environment" attrs="{'required': [('electronic_invoice', '=', True)]}" />
|
||||||
|
<field name="ei_software_operation" attrs="{'required': [('electronic_invoice', '=', True)]}" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<notebook>
|
||||||
|
<page string="Servicio WEB" name="web_service">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="service_url" attrs="{'required': [('electronic_invoice', '=', True)]}" invisible="1" />
|
||||||
|
<field name="service_url_get" attrs="{'required': [('electronic_invoice', '=', True)]}" invisible="1" />
|
||||||
|
<field name="service_url_post" attrs="{'required': [('electronic_invoice', '=', True)]}" />
|
||||||
|
<field name="software_token" attrs="{'required': [('electronic_invoice', '=', True)]}" />
|
||||||
|
<field name="software_code_dian" attrs="{'required': [('electronic_invoice', '=', True)]}" />
|
||||||
|
<field name="ei_tmp_path" attrs="{'required': [('electronic_invoice', '=', True)]}" />
|
||||||
|
</group>
|
||||||
|
<group></group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
<page string="Políticas" name="policies">
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="ei_automatic_generation" />
|
||||||
|
<field name="auto_acceptance_email" />
|
||||||
|
<field name="invoice_batch_process" invisible="1" />
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="attach_customer_order" />
|
||||||
|
<field name="attach_delivery_note" />
|
||||||
|
<field name="attach_invoice_docs" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
<page string="Configuraciónes" name="settings">
|
||||||
|
<group string="Tipo de Operación">
|
||||||
|
<field name="ei_id_customization" />
|
||||||
|
<field name="tributary_obligations" />
|
||||||
|
<field name="ei_report_id" />
|
||||||
|
<field name="mail_server_id" />
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
19
electronic_invoice_dian/views/res_partner.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="view_partner_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.ei.view.form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//sheet/notebook/page[@name='internal_notes']" position="after">
|
||||||
|
<page name="electronic_invoice" string="Facturación Electrónica" groups="electronic_invoice_dian.group_user,electronic_invoice_dian.group_manager">
|
||||||
|
<group name="group_ei_email">
|
||||||
|
<field name="ei_email" />
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
17
electronic_invoice_dian/views/sale_order.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="view_order_form" model="ir.ui.view">
|
||||||
|
<field name="name">sale.order.ei.view.form</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[@name='other_information']/group/group/field[@name='invoice_status']" position="after">
|
||||||
|
<field name="customer_po_policy" invisible="1" />
|
||||||
|
<field name="customer_po_name" invisible="1" />
|
||||||
|
<field name="customer_po_file" attrs="{'readonly': [('state', 'not in', ['draft', 'sent'])], 'invisible': [('customer_po_policy', '=', False)]}" filename="customer_po_name" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
15
electronic_invoice_dian/views/uom_uom.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="product_uom_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">uom.uom.ei.view.form</field>
|
||||||
|
<field name="model">uom.uom</field>
|
||||||
|
<field name="inherit_id" ref="uom.product_uom_form_view" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='rounding']" position="after">
|
||||||
|
<field name="code_dian" groups="electronic_invoice_dian.group_manager,electronic_invoice_dian.group_user" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
6
electronic_invoice_dian/wizard/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Multi Invoice Process
|
||||||
|
"""
|
||||||
|
from . import ei_multi_process
|
||||||
|
from . import ei_state_reset
|
||||||
39
electronic_invoice_dian/wizard/ei_multi_process.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class EIMultiProcess(models.TransientModel):
|
||||||
|
_name = 'ei.multi.process'
|
||||||
|
_description = 'Wizard para enviar multiples facturas electrónica'
|
||||||
|
|
||||||
|
@api.onchange('invoices')
|
||||||
|
def populate_invoices(self):
|
||||||
|
invoices = self.env['account.move'].browse(
|
||||||
|
self._context['active_ids'])
|
||||||
|
txt = ''
|
||||||
|
for invoice in invoices.filtered(
|
||||||
|
lambda invoice: invoice.move_type in (
|
||||||
|
'out_invoice', 'out_refund')
|
||||||
|
and invoice.state == 'posted'
|
||||||
|
and invoice.ei_state in ('pending', 'dian_reject')):
|
||||||
|
txt += invoice.name + '\n'
|
||||||
|
self.invoices = txt
|
||||||
|
|
||||||
|
invoices = fields.Text(string='Facturas por Procesar', readonly=True,
|
||||||
|
help="Facturas con Estado 'No Transferido'")
|
||||||
|
|
||||||
|
def send_multiple_invoices(self):
|
||||||
|
active_invoices = self.env['account.move'].browse(
|
||||||
|
self._context['active_ids'])
|
||||||
|
to_send_invoices = active_invoices.filtered(
|
||||||
|
lambda invoice: invoice.move_type in ('out_invoice', 'out_refund')
|
||||||
|
and invoice.state == 'posted'
|
||||||
|
and invoice.ei_state in ('pending', 'dian_reject'))
|
||||||
|
if not to_send_invoices:
|
||||||
|
return
|
||||||
|
if self.env.user.company_id.invoice_batch_process:
|
||||||
|
ei_batch_process = self.env['ei.batch.process']
|
||||||
|
ei_batch_process.process_batch(to_send_invoices)
|
||||||
|
else:
|
||||||
|
for invoice in to_send_invoices:
|
||||||
|
invoice.generate_electronic_invoice()
|
||||||
30
electronic_invoice_dian/wizard/ei_multi_process.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="ei_multi_process_view_form" model="ir.ui.view">
|
||||||
|
<field name="name">ei.multi.process.view.form</field>
|
||||||
|
<field name="model">ei.multi.process</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="">
|
||||||
|
<group string="Facturas por Procesar">
|
||||||
|
<field name="invoices" nolabel="1" />
|
||||||
|
</group>
|
||||||
|
<h4 class="oe_grey">Facturas con Estado No Transferido o Rechazado DIAN</h4>
|
||||||
|
<footer>
|
||||||
|
<button name="send_multiple_invoices" string="Confirmar" type="object" class="oe_stat_button" icon="fa-upload" />
|
||||||
|
<button string="Cancel" class="oe_stat_button" icon="fa-times" special="cancel" />
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="action_ei_multi_process" model="ir.actions.act_window">
|
||||||
|
<field name="name">Enviar Factura Electrónica</field>
|
||||||
|
<field name="res_model">ei.multi.process</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="view_id" ref="electronic_invoice_dian.ei_multi_process_view_form" />
|
||||||
|
<field name="target">new</field>
|
||||||
|
<field name="binding_model_id" ref="account.model_account_move" />
|
||||||
|
<field name="binding_view_types">list</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
11
electronic_invoice_dian/wizard/ei_state_reset.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class EIStateReset(models.TransientModel):
|
||||||
|
_name = 'ei.state.reset'
|
||||||
|
_description = 'Wizard para devolver el estado de la factura electrónica'
|
||||||
|
|
||||||
|
def reset_ei_state(self):
|
||||||
|
self.env['account.move'].browse(
|
||||||
|
self._context['active_id']).ei_state = 'pending'
|
||||||
29
electronic_invoice_dian/wizard/ei_state_reset.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<data>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="ei_state_reset_view" model="ir.ui.view">
|
||||||
|
<field name="name">Revertir Estado de Factura Electrónica</field>
|
||||||
|
<field name="model">ei.state.reset</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Revertir Estado de Factura Electrónica">
|
||||||
|
<h4>El estado sera revertido a No Transferido, ¿Desea Continuar?</h4>
|
||||||
|
<h5>El Log generado previamente, no será eliminado."</h5>
|
||||||
|
<footer>
|
||||||
|
<button name="reset_ei_state" string="Confirmar" type="object" class="oe_stat_button" icon="fa-check-circle" />
|
||||||
|
<button string="Cancel" class="oe_stat_button" icon="fa-times-circle" special="cancel" />
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_ei_state_reset" model="ir.actions.act_window">
|
||||||
|
<field name="name">Resetear Estado de Factura Electrónica</field>
|
||||||
|
<field name="res_model">ei.state.reset</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="ei_state_reset_view" />
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</data>
|
||||||
40
l10n_co_account_e_invoicing/README.rst
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
|
||||||
|
:target: https://www.gnu.org/licenses/agpl
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
|
||||||
|
=============================
|
||||||
|
Colombian Account E-Invoicing
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Configuacion del Server:
|
||||||
|
|
||||||
|
Configurar "server_wide_modules" en el archivo de configuracion para que el modulo funcione correctamente.
|
||||||
|
Ejemplo:
|
||||||
|
server_wide_modules = web,web_kanban,l10n_co_account_e_invoicing
|
||||||
|
|
||||||
|
Configuacion:
|
||||||
|
|
||||||
|
1) Configurar Secuencias DIAN, principalmente la clave técnica:
|
||||||
|
|
||||||
|
.. image:: https://raw.githubusercontent.com/odooloco/l10n-colombia/10.0/l10n_co_account_e_invoicing/static/images/ir_sequence.png
|
||||||
|
|
||||||
|
2) Configurar datos de la compañía:
|
||||||
|
|
||||||
|
.. image:: https://raw.githubusercontent.com/odooloco/l10n-colombia/10.0/l10n_co_account_e_invoicing/static/images/res_company.png
|
||||||
|
|
||||||
|
3) Hacer pruebas con las facturas:
|
||||||
|
|
||||||
|
.. image:: https://raw.githubusercontent.com/odooloco/l10n-colombia/10.0/l10n_co_account_e_invoicing/static/images/account_invoice.png
|
||||||
|
|
||||||
|
4) Resultados:
|
||||||
|
|
||||||
|
.. image:: https://raw.githubusercontent.com/odooloco/l10n-colombia/10.0/l10n_co_account_e_invoicing/static/images/dian_document.png
|
||||||
|
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Joan Marín <https://github.com/JoanMarin>
|
||||||
6
l10n_co_account_e_invoicing/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from . import controllers
|
||||||
58
l10n_co_account_e_invoicing/__manifest__.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
{
|
||||||
|
"name": "Colombian Account E-Invoicing",
|
||||||
|
"category": "E-Invoicing",
|
||||||
|
"version": "14.0.1.0.0",
|
||||||
|
"author": "Avancys SAS",
|
||||||
|
"website": "https://www.avancys.com/",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"summary": "Colombian Account E-Invoicing",
|
||||||
|
"depends": [
|
||||||
|
"l10n_co_account_move_discrepancy_response",
|
||||||
|
"l10n_co_account_move_payment_mean",
|
||||||
|
"l10n_co_account_tax_group_type",
|
||||||
|
"l10n_co_base_location",
|
||||||
|
"l10n_co_partner_person_type",
|
||||||
|
"l10n_co_partner_vat",
|
||||||
|
"l10n_co_uom",
|
||||||
|
"l10n_co_sequence_resolution",
|
||||||
|
"account_fiscal_year",
|
||||||
|
"partner_coc",
|
||||||
|
"partner_commercial_name",
|
||||||
|
"partner_fax",
|
||||||
|
"product_brand",
|
||||||
|
"product_manufacturer"
|
||||||
|
],
|
||||||
|
"external_dependencies": {
|
||||||
|
"python": [
|
||||||
|
"pyopenssl",
|
||||||
|
"xades",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"data": [
|
||||||
|
"security/ir.model.access.csv",
|
||||||
|
"data/account_fiscal_position_party_tax_scheme_data.xml",
|
||||||
|
"data/account_fiscal_position_tax_level_code_data.xml",
|
||||||
|
"data/ir_cron_data.xml",
|
||||||
|
"data/ir_module_category_data.xml",
|
||||||
|
"data/mail_template_data.xml",
|
||||||
|
"data/product_scheme_data.xml",
|
||||||
|
"data/res_groups_data.xml",
|
||||||
|
"views/account_fiscal_position_party_tax_scheme_views.xml",
|
||||||
|
"views/account_fiscal_position_tax_level_code_views.xml",
|
||||||
|
"views/account_fiscal_position_views.xml",
|
||||||
|
"views/account_fiscal_year_views.xml",
|
||||||
|
"views/account_move_views.xml",
|
||||||
|
"views/account_move_dian_document_views.xml",
|
||||||
|
"views/account_tax_group_views.xml",
|
||||||
|
"views/ir_sequence_views.xml",
|
||||||
|
"views/product_template_views.xml",
|
||||||
|
"views/res_company_views.xml",
|
||||||
|
"views/res_partner_views.xml",
|
||||||
|
"report/template_report.xml",
|
||||||
|
"report/report.xml",
|
||||||
|
],
|
||||||
|
"installable": True,
|
||||||
|
}
|
||||||
5
l10n_co_account_e_invoicing/controllers/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import main
|
||||||
44
l10n_co_account_e_invoicing/controllers/main.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from pytz import timezone
|
||||||
|
from odoo import http
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
|
||||||
|
class DianDocumentController(http.Controller):
|
||||||
|
@http.route('/dian/document/accept', type='http', auth="none")
|
||||||
|
def accept(self, db, token, id, **kwargs):
|
||||||
|
request.session.db = db
|
||||||
|
move_id = request.env['account.move'].sudo().search([
|
||||||
|
('access_token', '=', token), ('id', '=', id)])
|
||||||
|
|
||||||
|
if move_id and not move_id.accepted_rejected_datetime:
|
||||||
|
move_id.write({
|
||||||
|
'is_accepted_rejected': True,
|
||||||
|
'accepted_rejected_datetime': datetime.now().replace(
|
||||||
|
tzinfo=None), #timezone('UTC')),
|
||||||
|
'dian_document_state': 'customer_accept'})
|
||||||
|
|
||||||
|
return request.render('l10n_co_account_e_invoicing.customer_accept_invoice', {})
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@http.route('/dian/document/reject', type='http', auth="none")
|
||||||
|
def reject(self, db, token, id, **kwargs):
|
||||||
|
request.session.db = db
|
||||||
|
move_id = request.env['account.move'].sudo().search([
|
||||||
|
('access_token', '=', token), ('id', '=', id)])
|
||||||
|
|
||||||
|
if move_id and not move_id.accepted_rejected_datetime:
|
||||||
|
move_id.write({
|
||||||
|
'is_accepted_rejected': False,
|
||||||
|
'accepted_rejected_datetime': datetime.now().replace(
|
||||||
|
tzinfo=None), #timezone('UTC')),
|
||||||
|
'dian_document_state': 'customer_reject'})
|
||||||
|
|
||||||
|
return request.render('l10n_co_account_e_invoicing.customer_reject_invoice', {})
|
||||||
|
|
||||||
|
return
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="account_fiscal_position_party_tax_scheme_01" model="account.fiscal.position.party.tax.scheme">
|
||||||
|
<field name="name">IVA</field>
|
||||||
|
<field name="code">01</field>
|
||||||
|
</record>
|
||||||
|
<record id="account_fiscal_position_party_tax_scheme_04" model="account.fiscal.position.party.tax.scheme">
|
||||||
|
<field name="name">INC</field>
|
||||||
|
<field name="code">04</field>
|
||||||
|
</record>
|
||||||
|
<record id="account_fiscal_position_party_tax_scheme_za" model="account.fiscal.position.party.tax.scheme">
|
||||||
|
<field name="name">IVA e INC</field>
|
||||||
|
<field name="code">ZA</field>
|
||||||
|
</record>
|
||||||
|
<record id="account_fiscal_position_party_tax_scheme_zz" model="account.fiscal.position.party.tax.scheme">
|
||||||
|
<field name="name">No aplica</field>
|
||||||
|
<field name="code">ZZ</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="account_fiscal_position_tax_level_code_o_13" model="account.fiscal.position.tax.level.code">
|
||||||
|
<field name="name">Gran contribuyente</field>
|
||||||
|
<field name="code">O-13</field>
|
||||||
|
</record>
|
||||||
|
<record id="account_fiscal_position_tax_level_code_o_15" model="account.fiscal.position.tax.level.code">
|
||||||
|
<field name="name">Autorretenedor</field>
|
||||||
|
<field name="code">O-15</field>
|
||||||
|
</record>
|
||||||
|
<record id="account_fiscal_position_tax_level_code_o_23" model="account.fiscal.position.tax.level.code">
|
||||||
|
<field name="name">Agente de retención IVA</field>
|
||||||
|
<field name="code">O-23</field>
|
||||||
|
</record>
|
||||||
|
<record id="account_fiscal_position_tax_level_code_o_47" model="account.fiscal.position.tax.level.code">
|
||||||
|
<field name="name">Régimen simple de tributación</field>
|
||||||
|
<field name="code">O-47</field>
|
||||||
|
</record>
|
||||||
|
<record id="account_fiscal_position_tax_level_code_r_99_pn" model="account.fiscal.position.tax.level.code">
|
||||||
|
<field name="name">No aplica - Otros</field>
|
||||||
|
<field name="code">R-99-PN</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
15
l10n_co_account_e_invoicing/data/ir_cron_data.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo noupdate="1">
|
||||||
|
<record id="ir_cron_process_dian_documents" model="ir.cron">
|
||||||
|
<field name="name">Process DIAN Documents</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
<field name="user_id" ref="base.user_root"/>
|
||||||
|
<field name="interval_number">1</field>
|
||||||
|
<field name="nextcall" eval="(DateTime.today()).strftime('%Y-%m-%d 06:00:00')" />
|
||||||
|
<field name="interval_type">days</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="model_id" ref="model_res_company"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.cron_process_dian_documents()</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo noupdate="0">
|
||||||
|
<record id="category_einvoicing" model="ir.module.category">
|
||||||
|
<field name="name">E-Invoicing</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
48
l10n_co_account_e_invoicing/data/mail_template_data.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="email_template_einvoice" model="mail.template">
|
||||||
|
<field name="name">E-Invoice: Send by Email</field>
|
||||||
|
<field name="email_from">${object.company_id.einvoicing_email}</field>
|
||||||
|
<field name="reply_to">${object.company_id.einvoicing_email}</field>
|
||||||
|
<field name="subject">${object.company_id.partner_id.ref_num};${object.company_id.name};${object.name};${object.invoice_type_code if not object.refund_type else ('91' if object.refund_type == 'credit' else '92')};${object.company_id.name};</field>
|
||||||
|
<field name="email_to">${(object.partner_id.einvoicing_email or object.partner_id.email) or object.company_id.einvoicing_partner_no_email}${',' + object.company_id.einvoicing_receives_all_emails if object.company_id.einvoicing_receives_all_emails else ''}</field>
|
||||||
|
<field name="model_id" ref="account.model_account_move"/>
|
||||||
|
<field name="auto_delete" eval="True"/>
|
||||||
|
<field name="lang">${object.partner_id.lang}</field>
|
||||||
|
<field name="body_html" type="html">
|
||||||
|
<div >
|
||||||
|
<p>Señor(es), ${object.partner_id.name}</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p>Le informamos que ha recibido una factura/nota electrónica de ${object.company_id.name}:</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p>Número de factura: <strong>${object.number}</strong></p>
|
||||||
|
<p>Valor Total: <strong>${object.amount_total} ${object.currency_id.name}</strong></p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p>Si tiene inquietud respecto a la información contenida en la factura/nota electrónica, responda este correo electrónico.</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p>Si pasados tres (3) días hábiles siguientes a la recepción de la factura/nota electrónica, no se ha recibido rechazo de la factura/nota electrónica, el sistema la dará por aceptada.</p>
|
||||||
|
|
||||||
|
<br/><br/><br/>
|
||||||
|
|
||||||
|
<div style="height: auto;width:450px; margin:0 auto;padding-top:20px;padding-bottom:40px;">
|
||||||
|
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#3b9c17;margin : 0 15px 0 0;text-decoration: none;color:#FFFFFF;"
|
||||||
|
href="/dian/document/accept?db=${object.dbname}&token=${object.access_token}&id=${object.id}">Aceptar</a>
|
||||||
|
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#d66058;margin : 0 15px 0 0;text-decoration: none;color:#FFFFFF;"
|
||||||
|
href="/dian/document/reject?db=${object.dbname}&token=${object.access_token}&id=${object.id}">Rechazar</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<p>Cordialmente, <strong>${object.company_id.name}</strong></p>
|
||||||
|
</div>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
22
l10n_co_account_e_invoicing/data/product_scheme_data.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="product_scheme_001" model="product.scheme">
|
||||||
|
<field name="name">UNSPSC</field>
|
||||||
|
<field name="code">001</field>
|
||||||
|
<field name="scheme_agency_id">10</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_scheme_010" model="product.scheme">
|
||||||
|
<field name="name">GTIN</field>
|
||||||
|
<field name="code">010</field>
|
||||||
|
<field name="scheme_agency_id">9</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_scheme_020" model="product.scheme">
|
||||||
|
<field name="name">Partida Arancelarias</field>
|
||||||
|
<field name="code">020</field>
|
||||||
|
<field name="scheme_agency_id">195</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_scheme_999" model="product.scheme">
|
||||||
|
<field name="name">Estándar de adopción del contribuyente</field>
|
||||||
|
<field name="code">999</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
58
l10n_co_account_e_invoicing/data/res_groups_data.xml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record model="res.groups" id="group_view_einvoicing_email_fields">
|
||||||
|
<field name="name">View 'E-Invoicing Email' Fields</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_edit_is_einvoicing_agent_field">
|
||||||
|
<field name="name">Edit 'Is an E-Invoicing Agent?' Field</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_view_einvoicing_setting_product">
|
||||||
|
<field name="name">View E-Invoicing Setting of the Product</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_view_dian_documents">
|
||||||
|
<field name="name">View DIAN Documents</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_view_invoice_refund_buttons">
|
||||||
|
<field name="name">View Invoice Refund Buttons</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_view_send_invoice_dian_field">
|
||||||
|
<field name="name">View 'Send Invoice to DIAN?' Field</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_view_operation_type_field">
|
||||||
|
<field name="name">View 'Operation Type' Field</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_view_invoice_type_field">
|
||||||
|
<field name="name">View 'Invoice Type' Field</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_view_accepted_rejected_fields">
|
||||||
|
<field name="name">View 'Accepted/Rejected' Fields</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_view_summary_fields">
|
||||||
|
<field name="name">View 'Summary of Invoice Lines' Fields</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
<record model="res.groups" id="group_allow_cancel_invoice_dian_document_done">
|
||||||
|
<field name="name">Allow to Cancel Invoice With DIAN Document in Done</field>
|
||||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||||
|
<field name="category_id" ref="l10n_co_account_e_invoicing.category_einvoicing"/>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
1660
l10n_co_account_e_invoicing/i18n/es.po
Normal file
1660
l10n_co_account_e_invoicing/i18n/es_CO.po
Normal file
1590
l10n_co_account_e_invoicing/i18n/l10n_co_account_e_invoicing.pot
Normal file
21
l10n_co_account_e_invoicing/models/__init__.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from . import res_company
|
||||||
|
from . import res_partner
|
||||||
|
from . import product_scheme
|
||||||
|
from . import product_template
|
||||||
|
from . import ir_sequence
|
||||||
|
from . import ir_sequence_date_range
|
||||||
|
from . import account_fiscal_position_party_tax_scheme
|
||||||
|
from . import account_fiscal_position_tax_level_code
|
||||||
|
from . import account_fiscal_position
|
||||||
|
from . import account_fiscal_year
|
||||||
|
from . import account_tax_group
|
||||||
|
from . import account_move_summary_line
|
||||||
|
from . import account_move_line
|
||||||
|
from . import account_move
|
||||||
|
from . import account_move_dian_document_line
|
||||||
|
from . import account_move_dian_document
|
||||||
|
from . import einvoice_notification_group
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class AccountFiscalPosition(models.Model):
|
||||||
|
_inherit = 'account.fiscal.position'
|
||||||
|
|
||||||
|
listname = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
('48', 'Responsable del impuesto sobre las ventas - IVA'),
|
||||||
|
('49', 'No responsable de IVA')],
|
||||||
|
string='Fiscal Regime',
|
||||||
|
default=False)
|
||||||
|
tax_level_code_ids = fields.Many2many(
|
||||||
|
comodel_name='account.fiscal.position.tax.level.code',
|
||||||
|
relation='account_fiscal_position_tax_level_code_rel',
|
||||||
|
column1='account_fiscal_position_id',
|
||||||
|
column2='tax_level_code_id',
|
||||||
|
string='Fiscal Responsibilities (TaxLevelCode)')
|
||||||
|
party_tax_scheme_id = fields.Many2one(
|
||||||
|
comodel_name='account.fiscal.position.party.tax.scheme',
|
||||||
|
string="Fiscal Responsibilities (PartyTaxScheme)")
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountFiscalPositionPartyTaxScheme(models.Model):
|
||||||
|
_name = 'account.fiscal.position.party.tax.scheme'
|
||||||
|
_description = 'Fiscal Responsibilities (PartyTaxScheme)'
|
||||||
|
|
||||||
|
name = fields.Char(string='Name')
|
||||||
|
code = fields.Char(string='Code')
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountFiscalPositionTaxLevelCode(models.Model):
|
||||||
|
_name = 'account.fiscal.position.tax.level.code'
|
||||||
|
_description = 'Fiscal Responsibilities (TaxLevelCode)'
|
||||||
|
|
||||||
|
name = fields.Char(string='Name')
|
||||||
|
code = fields.Char(string='Code')
|
||||||
14
l10n_co_account_e_invoicing/models/account_fiscal_year.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class AccountFiscalYear(models.Model):
|
||||||
|
_inherit = 'account.fiscal.year'
|
||||||
|
|
||||||
|
out_invoice_sent = fields.Integer(string='Invoices Sent', default=0)
|
||||||
|
out_refund_credit_sent = fields.Integer(string='Credit Notes Sent', default=0)
|
||||||
|
out_refund_debit_sent = fields.Integer(string='Debit Notes Sent', default=0)
|
||||||
|
in_invoice_sent = fields.Integer(string='Support Documents Sent', default=0)
|
||||||
809
l10n_co_account_e_invoicing/models/account_move.py
Normal file
@ -0,0 +1,809 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
import pytz
|
||||||
|
from dateutil import tz
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from uuid import uuid4
|
||||||
|
from odoo import api, models, fields, SUPERUSER_ID, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
DIAN_TYPES = ('e-support_document', 'e-invoicing', 'e-credit_note', 'e-debit_note')
|
||||||
|
|
||||||
|
|
||||||
|
class AccountMove(models.Model):
|
||||||
|
_inherit = "account.move"
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_operation_type(self):
|
||||||
|
user = self.env['res.users'].search([('id', '=', self.env.user.id)])
|
||||||
|
view_operation_type_field = False
|
||||||
|
|
||||||
|
if (user.has_group('l10n_co_account_e_invoicing.group_view_operation_type_field')
|
||||||
|
and self.env.user.id != SUPERUSER_ID):
|
||||||
|
view_operation_type_field = True
|
||||||
|
|
||||||
|
if 'move_type' in self._context.keys():
|
||||||
|
if self._context['move_type'] == 'out_invoice' and not view_operation_type_field:
|
||||||
|
return '10'
|
||||||
|
elif self._context['move_type'] == 'in_invoice':
|
||||||
|
return '10'
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
elif not view_operation_type_field:
|
||||||
|
return '10'
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_invoice_type_code(self):
|
||||||
|
user = self.env['res.users'].search([('id', '=', self.env.user.id)])
|
||||||
|
view_invoice_type_field = False
|
||||||
|
|
||||||
|
if (user.has_group('l10n_co_account_e_invoicing.group_view_invoice_type_field')
|
||||||
|
and self.env.user.id != SUPERUSER_ID):
|
||||||
|
view_invoice_type_field = True
|
||||||
|
|
||||||
|
if 'move_type' in self._context.keys():
|
||||||
|
if self._context['move_type'] == 'out_invoice' and not view_invoice_type_field:
|
||||||
|
return '01'
|
||||||
|
elif self._context['move_type'] == 'in_invoice':
|
||||||
|
return '05'
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
elif not view_invoice_type_field:
|
||||||
|
return '01'
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_send_invoice_to_dian(self):
|
||||||
|
return self.env.user.company_id.send_invoice_to_dian or '0'
|
||||||
|
|
||||||
|
def _compute_warn_certificate(self):
|
||||||
|
warn_remaining_certificate = False
|
||||||
|
warn_inactive_certificate = False
|
||||||
|
|
||||||
|
if self.company_id.einvoicing_enabled:
|
||||||
|
warn_inactive_certificate = True
|
||||||
|
|
||||||
|
if (self.company_id.certificate_file
|
||||||
|
and self.company_id.certificate_password
|
||||||
|
and self.company_id.certificate_date):
|
||||||
|
remaining_days = self.company_id.certificate_remaining_days or 0
|
||||||
|
today = fields.Date.context_today(self)
|
||||||
|
date_to = self.company_id.certificate_date
|
||||||
|
days = (date_to - today).days
|
||||||
|
warn_inactive_certificate = False
|
||||||
|
|
||||||
|
if days < remaining_days:
|
||||||
|
if days < 0:
|
||||||
|
warn_inactive_certificate = True
|
||||||
|
else:
|
||||||
|
warn_remaining_certificate = True
|
||||||
|
|
||||||
|
self.warn_inactive_certificate = warn_inactive_certificate
|
||||||
|
self.warn_remaining_certificate = warn_remaining_certificate
|
||||||
|
|
||||||
|
def _compute_sequence_resolution_id(self):
|
||||||
|
for invoice_id in self:
|
||||||
|
sequence_resolution = False
|
||||||
|
|
||||||
|
if (invoice_id.move_type == "out_invoice"
|
||||||
|
or (invoice_id.journal_id.sequence_id.dian_type == "e-support_document"
|
||||||
|
and invoice_id.move_type == "in_invoice")):
|
||||||
|
sequence_resolution_ids = self.env['ir.sequence.date_range'].search([
|
||||||
|
('sequence_id', '=', invoice_id.journal_id.sequence_id.id)])
|
||||||
|
|
||||||
|
for sequence_resolution_id in sequence_resolution_ids:
|
||||||
|
move_name = invoice_id.move_name or ''
|
||||||
|
number = move_name.replace(sequence_resolution_id.prefix or '', '')
|
||||||
|
|
||||||
|
if sequence_resolution_id.active_resolution:
|
||||||
|
sequence_resolution = sequence_resolution_id
|
||||||
|
|
||||||
|
if (number.isnumeric()
|
||||||
|
and sequence_resolution_id.number_from <= int(number)
|
||||||
|
and int(number) <= sequence_resolution_id.number_to):
|
||||||
|
sequence_resolution = sequence_resolution_id
|
||||||
|
|
||||||
|
invoice_id.sequence_resolution_id = sequence_resolution
|
||||||
|
|
||||||
|
def _compute_dbname(self):
|
||||||
|
self.dbname = self._cr.dbname
|
||||||
|
|
||||||
|
warn_remaining_certificate = fields.Boolean(
|
||||||
|
string="Warn About Remainings?",
|
||||||
|
compute="_compute_warn_certificate",
|
||||||
|
store=False)
|
||||||
|
warn_inactive_certificate = fields.Boolean(
|
||||||
|
string="Warn About Inactive Certificate?",
|
||||||
|
compute="_compute_warn_certificate",
|
||||||
|
store=False)
|
||||||
|
sequence_resolution_id = fields.Many2one(
|
||||||
|
comodel_name='ir.sequence.date_range',
|
||||||
|
string='Sequence Resolution',
|
||||||
|
compute='_compute_sequence_resolution_id',
|
||||||
|
store=False)
|
||||||
|
dbname = fields.Char(string="DB Name", compute="_compute_dbname", store=False)
|
||||||
|
invoice_datetime = fields.Datetime(string='Invoice Datetime', copy=False)
|
||||||
|
delivery_datetime = fields.Datetime(string='Delivery Datetime', copy=False)
|
||||||
|
operation_type = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
('09', 'AIU'),
|
||||||
|
('10', 'Standard'),
|
||||||
|
('20', 'Credit note that references an e-invoice'),
|
||||||
|
('22', 'Credit note without reference to invoices *'),
|
||||||
|
('30', 'Debit note that references an e-invoice'),
|
||||||
|
('32', 'Debit note without reference to invoices *')],
|
||||||
|
string='Operation Type',
|
||||||
|
default=_default_operation_type,
|
||||||
|
copy=False)
|
||||||
|
invoice_type_code = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
('01', 'E-invoice of sale'),
|
||||||
|
('03', 'E-document of transmission - type 03'),
|
||||||
|
('04', 'E-invoice of sale - type 04'),
|
||||||
|
('05', 'E-Support Document')],
|
||||||
|
string='Invoice Type',
|
||||||
|
default=_default_invoice_type_code,
|
||||||
|
copy=False)
|
||||||
|
send_invoice_to_dian = fields.Selection(
|
||||||
|
selection=[('0', 'Immediately'), ('1', 'After 1 Day'), ('2', 'After 2 Days')],
|
||||||
|
string='Send Invoice to DIAN?',
|
||||||
|
default=_default_send_invoice_to_dian,
|
||||||
|
copy=False)
|
||||||
|
access_token = fields.Char(string='Access Token', copy=False)
|
||||||
|
is_accepted_rejected = fields.Boolean(string='Is Accepted/Rejected?', copy=False)
|
||||||
|
accepted_rejected_datetime = fields.Datetime(
|
||||||
|
string='Datetime of Accepted/Rejected', copy=False)
|
||||||
|
receipt_document_reference = fields.Char(
|
||||||
|
string='Merchandise / Service Receipt Document',
|
||||||
|
copy=False)
|
||||||
|
dian_document_state = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
('pending', 'No Transferido'),
|
||||||
|
('done', 'Emitido'),
|
||||||
|
('exception', 'Excepción de Envio'),
|
||||||
|
('dian_reject', 'Rechazado DIAN'),
|
||||||
|
('dian_accept', 'Aceptado DIAN'),
|
||||||
|
('customer_reject', 'Rechazado Cliente'),
|
||||||
|
('customer_accept', 'Aceptado Cliente')],
|
||||||
|
string='DIAN Document State',
|
||||||
|
default='pending',
|
||||||
|
copy=False,
|
||||||
|
readonly=True)
|
||||||
|
sale_order_id = fields.Many2one(
|
||||||
|
comodel_name='sale.order',
|
||||||
|
string='Sale Order',
|
||||||
|
copy=False,
|
||||||
|
readonly=True,
|
||||||
|
states={'draft': [('readonly', False)]})
|
||||||
|
merge_with_sale_order = fields.Boolean(
|
||||||
|
string='Merge With Sale Order?',
|
||||||
|
copy=False,
|
||||||
|
readonly=True,
|
||||||
|
states={'draft': [('readonly', False)]})
|
||||||
|
summary_line_ids = fields.One2many(
|
||||||
|
comodel_name='account.move.summary.line',
|
||||||
|
inverse_name='move_id',
|
||||||
|
string='Summary of Invoice Lines',
|
||||||
|
copy=False,
|
||||||
|
readonly=True,
|
||||||
|
states={'draft': [('readonly', False)]})
|
||||||
|
summary_amount_untaxed = fields.Monetary(
|
||||||
|
string='Untaxed Amount',
|
||||||
|
readonly=True,
|
||||||
|
copy=False)
|
||||||
|
summary_amount_tax = fields.Monetary(string='Tax', readonly=True, copy=False)
|
||||||
|
summary_amount_total = fields.Monetary(string='Total', readonly=True, copy=False)
|
||||||
|
dian_document_ids = fields.One2many(
|
||||||
|
comodel_name='account.move.dian.document',
|
||||||
|
inverse_name='invoice_id',
|
||||||
|
string='DIAN Documents',
|
||||||
|
copy=False,
|
||||||
|
readonly=True,
|
||||||
|
states={'draft': [('readonly', False)]})
|
||||||
|
|
||||||
|
def _create_token(self):
|
||||||
|
self.access_token = self.access_token or uuid4().hex
|
||||||
|
|
||||||
|
def _get_active_dian_resolution(self):
|
||||||
|
msg = _("You do not have an active dian resolution, contact with your administrator.")
|
||||||
|
resolution_number = False
|
||||||
|
|
||||||
|
if self.sequence_resolution_id:
|
||||||
|
resolution_number = self.sequence_resolution_id.resolution_number
|
||||||
|
date_from = self.sequence_resolution_id.date_from
|
||||||
|
date_to = self.sequence_resolution_id.date_to_resolution
|
||||||
|
prefix = self.sequence_resolution_id.prefix
|
||||||
|
number_from = self.sequence_resolution_id.number_from
|
||||||
|
number_to = self.sequence_resolution_id.number_to
|
||||||
|
technical_key = self.sequence_resolution_id.technical_key
|
||||||
|
|
||||||
|
if not resolution_number:
|
||||||
|
raise UserError(msg)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'InvoiceAuthorization': resolution_number,
|
||||||
|
'StartDate': date_from,
|
||||||
|
'EndDate': date_to,
|
||||||
|
'Prefix': prefix,
|
||||||
|
'From': number_from,
|
||||||
|
'To': number_to,
|
||||||
|
'technical_key': technical_key
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_billing_reference(self):
|
||||||
|
msg1 = _("You can not make a refund invoice of an invoice with state different "
|
||||||
|
"to 'Posted'.")
|
||||||
|
msg2 = _("You can not make a refund invoice of an invoice with DIAN documents "
|
||||||
|
"with state 'Draft', 'Sent' or 'Cancelled'.")
|
||||||
|
billing_reference = {}
|
||||||
|
refund_invoice_id = self.reversed_entry_id or self.debit_origin_id
|
||||||
|
|
||||||
|
if refund_invoice_id:
|
||||||
|
if refund_invoice_id.state != 'posted':
|
||||||
|
raise UserError(msg1)
|
||||||
|
|
||||||
|
if refund_invoice_id.state == 'posted':
|
||||||
|
dian_document_state_done = False
|
||||||
|
dian_document_state_cancel = False
|
||||||
|
dian_document_state_sent = False
|
||||||
|
dian_document_state_draft = False
|
||||||
|
|
||||||
|
for dian_document_id in refund_invoice_id.dian_document_ids:
|
||||||
|
if dian_document_id.state == 'done':
|
||||||
|
dian_document_state_done = True
|
||||||
|
billing_reference['ID'] = refund_invoice_id.name
|
||||||
|
billing_reference['UUID'] = dian_document_id.cufe_cude
|
||||||
|
billing_reference[
|
||||||
|
'IssueDate'] = refund_invoice_id.invoice_date
|
||||||
|
billing_reference[
|
||||||
|
'CustomizationID'] = refund_invoice_id.operation_type
|
||||||
|
|
||||||
|
if dian_document_id.state == 'cancel':
|
||||||
|
dian_document_state_cancel = True
|
||||||
|
|
||||||
|
if dian_document_id.state == 'draft':
|
||||||
|
dian_document_state_draft = True
|
||||||
|
|
||||||
|
if dian_document_id.state == 'sent':
|
||||||
|
dian_document_state_sent = True
|
||||||
|
|
||||||
|
if ((not dian_document_state_done
|
||||||
|
and dian_document_state_cancel)
|
||||||
|
or dian_document_state_draft
|
||||||
|
or dian_document_state_sent):
|
||||||
|
raise UserError(msg2)
|
||||||
|
|
||||||
|
return billing_reference
|
||||||
|
|
||||||
|
def _get_payment_exchange_rate(self):
|
||||||
|
rate = 1
|
||||||
|
|
||||||
|
if self.currency_id != self.company_id.currency_id:
|
||||||
|
rate = self.currency_id._convert(
|
||||||
|
rate,
|
||||||
|
self.company_id.currency_id,
|
||||||
|
self.company_id,
|
||||||
|
self.date)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'SourceCurrencyCode': self.currency_id.name,
|
||||||
|
'TargetCurrencyCode': self.company_id.currency_id.name,
|
||||||
|
'CalculationRate': rate,
|
||||||
|
'Date': self.date
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_einvoicing_taxes(self):
|
||||||
|
msg1 = _("Your tax: '%s', has no e-invoicing tax group type, contact with your "
|
||||||
|
"administrator.")
|
||||||
|
msg2 = _("Your withholding tax: '%s', has amount equal to zero (0), the "
|
||||||
|
"withholding taxes must have amount different to zero (0), contact with "
|
||||||
|
"your administrator.")
|
||||||
|
msg3 = _("Your tax: '%s', has negative amount or an amount equal to zero (0), "
|
||||||
|
"the taxes must have an amount greater than zero (0), contact with your "
|
||||||
|
"administrator.")
|
||||||
|
taxes = {}
|
||||||
|
tax_total_base = 0
|
||||||
|
withholding_taxes = {}
|
||||||
|
currency_rate = self._get_payment_exchange_rate()
|
||||||
|
|
||||||
|
for move_line in self.line_ids.filtered(lambda l: l.tax_line_id):
|
||||||
|
if move_line.tax_line_id.tax_group_id.is_einvoicing:
|
||||||
|
if not move_line.tax_line_id.tax_group_id.tax_group_type_id:
|
||||||
|
raise UserError(msg1 % move_line.name)
|
||||||
|
|
||||||
|
tax_code = move_line.tax_line_id.tax_group_id.tax_group_type_id.code
|
||||||
|
tax_name = move_line.tax_line_id.tax_group_id.tax_group_type_id.name
|
||||||
|
tax_type = move_line.tax_line_id.tax_group_id.tax_group_type_id.type
|
||||||
|
tax_percent = '{:.2f}'.format(move_line.tax_line_id.amount)
|
||||||
|
tax_amount = move_line.price_total
|
||||||
|
tax_base = move_line.tax_base_amount / currency_rate['CalculationRate']
|
||||||
|
|
||||||
|
if tax_type == 'withholding_tax' and move_line.tax_line_id.amount == 0:
|
||||||
|
raise UserError(msg2 % move_line.name)
|
||||||
|
|
||||||
|
if tax_type == 'tax' and move_line.tax_line_id.amount <= 0:
|
||||||
|
raise UserError(msg3 % move_line.name)
|
||||||
|
|
||||||
|
if tax_amount != (tax_base * move_line.tax_line_id.amount / 100):
|
||||||
|
tax_amount = tax_base * move_line.tax_line_id.amount / 100
|
||||||
|
|
||||||
|
if tax_type == 'withholding_tax' and move_line.tax_line_id.amount > 0:
|
||||||
|
if tax_code not in withholding_taxes:
|
||||||
|
withholding_taxes[tax_code] = {}
|
||||||
|
withholding_taxes[tax_code]['total'] = 0
|
||||||
|
withholding_taxes[tax_code]['name'] = tax_name
|
||||||
|
withholding_taxes[tax_code]['taxes'] = {}
|
||||||
|
|
||||||
|
if tax_percent not in withholding_taxes[tax_code]['taxes']:
|
||||||
|
withholding_taxes[tax_code]['taxes'][tax_percent] = {}
|
||||||
|
withholding_taxes[tax_code]['taxes'][tax_percent][
|
||||||
|
'base'] = 0
|
||||||
|
withholding_taxes[tax_code]['taxes'][tax_percent][
|
||||||
|
'amount'] = 0
|
||||||
|
|
||||||
|
withholding_taxes[tax_code]['total'] += tax_amount * (-1)
|
||||||
|
withholding_taxes[tax_code]['taxes'][tax_percent]['base'] += tax_base
|
||||||
|
tax_total_base += tax_base
|
||||||
|
withholding_taxes[tax_code]['taxes'][tax_percent][
|
||||||
|
'amount'] += tax_amount * (-1)
|
||||||
|
elif tax_type == 'withholding_tax' and move_line.tax_line_id.amount < 0:
|
||||||
|
# TODO 3.0 Las retenciones se recomienda no enviarlas a la DIAN
|
||||||
|
# Solo las positivas que indicarian una autorretencion, Si la DIAN
|
||||||
|
# pide que se envien las retenciones, seria quitar o comentar este if
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if tax_code not in taxes:
|
||||||
|
taxes[tax_code] = {}
|
||||||
|
taxes[tax_code]['total'] = 0
|
||||||
|
taxes[tax_code]['name'] = tax_name
|
||||||
|
taxes[tax_code]['taxes'] = {}
|
||||||
|
|
||||||
|
if tax_percent not in taxes[tax_code]['taxes']:
|
||||||
|
taxes[tax_code]['taxes'][tax_percent] = {}
|
||||||
|
taxes[tax_code]['taxes'][tax_percent]['base'] = 0
|
||||||
|
taxes[tax_code]['taxes'][tax_percent]['amount'] = 0
|
||||||
|
|
||||||
|
taxes[tax_code]['total'] += tax_amount
|
||||||
|
taxes[tax_code]['taxes'][tax_percent]['base'] += tax_base
|
||||||
|
tax_total_base += tax_base
|
||||||
|
taxes[tax_code]['taxes'][tax_percent]['amount'] += tax_amount
|
||||||
|
|
||||||
|
if '01' not in taxes:
|
||||||
|
taxes['01'] = {}
|
||||||
|
taxes['01']['total'] = 0
|
||||||
|
taxes['01']['name'] = 'IVA'
|
||||||
|
taxes['01']['taxes'] = {}
|
||||||
|
taxes['01']['taxes']['0.00'] = {}
|
||||||
|
taxes['01']['taxes']['0.00']['base'] = 0
|
||||||
|
taxes['01']['taxes']['0.00']['amount'] = 0
|
||||||
|
|
||||||
|
if '04' not in taxes:
|
||||||
|
taxes['04'] = {}
|
||||||
|
taxes['04']['total'] = 0
|
||||||
|
taxes['04']['name'] = 'ICA'
|
||||||
|
taxes['04']['taxes'] = {}
|
||||||
|
taxes['04']['taxes']['0.00'] = {}
|
||||||
|
taxes['04']['taxes']['0.00']['base'] = 0
|
||||||
|
taxes['04']['taxes']['0.00']['amount'] = 0
|
||||||
|
|
||||||
|
if '03' not in taxes:
|
||||||
|
taxes['03'] = {}
|
||||||
|
taxes['03']['total'] = 0
|
||||||
|
taxes['03']['name'] = 'INC'
|
||||||
|
taxes['03']['taxes'] = {}
|
||||||
|
taxes['03']['taxes']['0.00'] = {}
|
||||||
|
taxes['03']['taxes']['0.00']['base'] = 0
|
||||||
|
taxes['03']['taxes']['0.00']['amount'] = 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
'TaxesTotal': taxes,
|
||||||
|
'TaxesTotalBase': tax_total_base,
|
||||||
|
'WithholdingTaxesTotal': withholding_taxes}
|
||||||
|
|
||||||
|
def _get_invoice_lines(self):
|
||||||
|
msg1 = _("Your Unit of Measure: '%s', has no Unit of Measure Code, contact " +
|
||||||
|
"with your administrator.")
|
||||||
|
msg2 = _("The invoice line %s has no reference")
|
||||||
|
msg3 = _("Your product: '%s', has no reference price, contact with your " +
|
||||||
|
"administrator.")
|
||||||
|
msg4 = _("Your tax: '%s', has no e-invoicing tax group type, contact with " +
|
||||||
|
"your administrator.")
|
||||||
|
msg5 = _("Your product: '%s', cannot have two VAT type taxes.")
|
||||||
|
msg6 = _("Your withholding tax: '%s', has amount equal to zero (0), the " +
|
||||||
|
"withholding taxes must have amount different to zero (0), contact " +
|
||||||
|
"with your administrator.")
|
||||||
|
msg7 = _("Your tax: '%s', has negative amount or an amount equal to zero " +
|
||||||
|
"(0), the taxes must have an amount greater than zero (0), contact " +
|
||||||
|
"with your administrator.")
|
||||||
|
invoice_lines = {}
|
||||||
|
count = 1
|
||||||
|
exception = False
|
||||||
|
|
||||||
|
for invoice_line in self.invoice_line_ids.filtered(
|
||||||
|
lambda line: not line.display_type):
|
||||||
|
if not invoice_line.product_uom_id.product_uom_code_id:
|
||||||
|
raise UserError(msg1 % invoice_line.product_uom_id.name)
|
||||||
|
|
||||||
|
disc_amount = 0
|
||||||
|
total_wo_disc = 0
|
||||||
|
brand_name = False
|
||||||
|
model_name = invoice_line.product_id.default_code
|
||||||
|
|
||||||
|
if invoice_line.price_unit != 0 and invoice_line.quantity != 0:
|
||||||
|
total_wo_disc = invoice_line.price_unit * invoice_line.quantity
|
||||||
|
|
||||||
|
if total_wo_disc != 0 and invoice_line.discount != 0:
|
||||||
|
disc_amount = (total_wo_disc * invoice_line.discount) / 100
|
||||||
|
|
||||||
|
if not invoice_line.product_id or not invoice_line.product_id.default_code:
|
||||||
|
raise UserError(msg2 % invoice_line.name)
|
||||||
|
|
||||||
|
if invoice_line.price_subtotal <= 0 and invoice_line.reference_price <= 0:
|
||||||
|
raise UserError(msg3 % invoice_line.product_id.default_code)
|
||||||
|
|
||||||
|
if self.invoice_type_code == '02':
|
||||||
|
if invoice_line.product_id.product_brand_id:
|
||||||
|
brand_name = invoice_line.product_id.product_brand_id.name
|
||||||
|
|
||||||
|
if invoice_line.product_id.manufacturer_pref:
|
||||||
|
model_name = invoice_line.product_id.manufacturer_pref
|
||||||
|
|
||||||
|
invoice_lines[count] = {}
|
||||||
|
invoice_lines[count][
|
||||||
|
'unitCode'] = invoice_line.product_uom_id.product_uom_code_id.code
|
||||||
|
invoice_lines[count]['Quantity'] = '{:.2f}'.format(
|
||||||
|
invoice_line.quantity)
|
||||||
|
invoice_lines[count][
|
||||||
|
'PricingReferencePriceAmount'] = '{:.2f}'.format(
|
||||||
|
invoice_line.reference_price)
|
||||||
|
invoice_lines[count]['LineExtensionAmount'] = '{:.2f}'.format(
|
||||||
|
invoice_line.price_subtotal)
|
||||||
|
invoice_lines[count]['MultiplierFactorNumeric'] = '{:.2f}'.format(
|
||||||
|
invoice_line.discount)
|
||||||
|
invoice_lines[count]['AllowanceChargeAmount'] = '{:.2f}'.format(
|
||||||
|
disc_amount)
|
||||||
|
invoice_lines[count][
|
||||||
|
'AllowanceChargeBaseAmount'] = '{:.2f}'.format(total_wo_disc)
|
||||||
|
invoice_lines[count]['TaxesTotal'] = {}
|
||||||
|
invoice_lines[count]['WithholdingTaxesTotal'] = {}
|
||||||
|
invoice_lines[count][
|
||||||
|
'StandardItemIdentification'] = invoice_line.product_id.default_code
|
||||||
|
iva_count = 0
|
||||||
|
|
||||||
|
for tax in invoice_line.tax_ids:
|
||||||
|
if tax.amount_type == 'group':
|
||||||
|
tax_ids = tax.children_tax_ids
|
||||||
|
else:
|
||||||
|
tax_ids = tax
|
||||||
|
|
||||||
|
for tax_id in tax_ids:
|
||||||
|
if tax_id.tax_group_id.is_einvoicing:
|
||||||
|
if not tax_id.tax_group_id.tax_group_type_id:
|
||||||
|
raise UserError(msg4 % tax.name)
|
||||||
|
|
||||||
|
tax_type = tax_id.tax_group_id.tax_group_type_id.type
|
||||||
|
|
||||||
|
if tax_id.tax_group_id.tax_group_type_id.name == 'IVA':
|
||||||
|
iva_count += 1
|
||||||
|
|
||||||
|
if iva_count > 1:
|
||||||
|
dian_document_id = self.dian_document_ids.filtered(
|
||||||
|
lambda d: d.state != 'cancel')
|
||||||
|
exception = True
|
||||||
|
msg = msg5 % invoice_line.product_id.default_code
|
||||||
|
dian_document_id.write({'get_status_zip_response': msg})
|
||||||
|
|
||||||
|
if tax_type == 'withholding_tax' and tax_id.amount == 0:
|
||||||
|
raise UserError(msg6 % tax_id.name)
|
||||||
|
|
||||||
|
if tax_type == 'tax' and tax_id.amount <= 0:
|
||||||
|
raise UserError(msg7 % tax_id.name)
|
||||||
|
|
||||||
|
if tax_type == 'withholding_tax' and tax_id.amount > 0:
|
||||||
|
invoice_lines[count]['WithholdingTaxesTotal'] = (
|
||||||
|
invoice_line._get_invoice_lines_taxes(
|
||||||
|
tax_id,
|
||||||
|
tax_id.amount,
|
||||||
|
invoice_lines[count]['WithholdingTaxesTotal']))
|
||||||
|
elif tax_type == 'withholding_tax' and tax_id.amount < 0:
|
||||||
|
# TODO 3.0 Las retenciones se recomienda no enviarlas a la DIAN.
|
||||||
|
# Solo la parte positiva que indicaria una autoretencion, Si la DIAN
|
||||||
|
# pide que se envie la parte negativa, seria quitar o comentar este if
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
invoice_lines[count]['TaxesTotal'] = (
|
||||||
|
invoice_line._get_invoice_lines_taxes(
|
||||||
|
tax_id,
|
||||||
|
tax_id.amount,
|
||||||
|
invoice_lines[count]['TaxesTotal']))
|
||||||
|
|
||||||
|
if '01' not in invoice_lines[count]['TaxesTotal']:
|
||||||
|
invoice_lines[count]['TaxesTotal']['01'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['01']['total'] = 0
|
||||||
|
invoice_lines[count]['TaxesTotal']['01']['name'] = 'IVA'
|
||||||
|
invoice_lines[count]['TaxesTotal']['01']['taxes'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['01']['taxes']['0.00'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['01']['taxes']['0.00'][
|
||||||
|
'base'] = invoice_line.price_subtotal
|
||||||
|
invoice_lines[count]['TaxesTotal']['01']['taxes']['0.00'][
|
||||||
|
'amount'] = 0
|
||||||
|
|
||||||
|
if '04' not in invoice_lines[count]['TaxesTotal']:
|
||||||
|
invoice_lines[count]['TaxesTotal']['04'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['04']['total'] = 0
|
||||||
|
invoice_lines[count]['TaxesTotal']['04']['name'] = 'ICA'
|
||||||
|
invoice_lines[count]['TaxesTotal']['04']['taxes'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['04']['taxes']['0.00'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['04']['taxes']['0.00'][
|
||||||
|
'base'] = invoice_line.price_subtotal
|
||||||
|
invoice_lines[count]['TaxesTotal']['04']['taxes']['0.00'][
|
||||||
|
'amount'] = 0
|
||||||
|
|
||||||
|
if '03' not in invoice_lines[count]['TaxesTotal']:
|
||||||
|
invoice_lines[count]['TaxesTotal']['03'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['03']['total'] = 0
|
||||||
|
invoice_lines[count]['TaxesTotal']['03']['name'] = 'INC'
|
||||||
|
invoice_lines[count]['TaxesTotal']['03']['taxes'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['03']['taxes']['0.00'] = {}
|
||||||
|
invoice_lines[count]['TaxesTotal']['03']['taxes']['0.00'][
|
||||||
|
'base'] = invoice_line.price_subtotal
|
||||||
|
invoice_lines[count]['TaxesTotal']['03']['taxes']['0.00'][
|
||||||
|
'amount'] = 0
|
||||||
|
|
||||||
|
invoice_lines[count]['BrandName'] = brand_name
|
||||||
|
invoice_lines[count]['ModelName'] = model_name
|
||||||
|
invoice_lines[count]['ItemDescription'] = invoice_line.name
|
||||||
|
invoice_lines[count]['InformationContentProviderParty'] = (
|
||||||
|
invoice_line._get_information_content_provider_party_values())
|
||||||
|
invoice_lines[count]['PriceAmount'] = '{:.2f}'.format(
|
||||||
|
invoice_line.price_unit)
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
if exception:
|
||||||
|
self.write({'dian_document_state': 'exception'})
|
||||||
|
else:
|
||||||
|
self.write({'dian_document_state': 'pending'})
|
||||||
|
|
||||||
|
return invoice_lines
|
||||||
|
|
||||||
|
def _set_invoice_lines_price_reference(self):
|
||||||
|
for invoice_line in self.invoice_line_ids.filtered(
|
||||||
|
lambda line: not line.display_type):
|
||||||
|
percentage = 100
|
||||||
|
margin_percentage = invoice_line.product_id.margin_percentage
|
||||||
|
|
||||||
|
if invoice_line.product_id.reference_price > 0:
|
||||||
|
reference_price = invoice_line.product_id.reference_price
|
||||||
|
elif 0 < margin_percentage < 100:
|
||||||
|
percentage = (percentage - margin_percentage) / 100
|
||||||
|
reference_price = invoice_line.product_id.standard_price / percentage
|
||||||
|
else:
|
||||||
|
reference_price = 0
|
||||||
|
|
||||||
|
invoice_line.write({
|
||||||
|
'cost_price': invoice_line.product_id.standard_price,
|
||||||
|
'reference_price': reference_price
|
||||||
|
})
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update(self, values):
|
||||||
|
res = super(AccountMove, self).update(values)
|
||||||
|
|
||||||
|
for invoice_id in self:
|
||||||
|
if values.get('refund_type') == "credit":
|
||||||
|
invoice_id.operation_type = '20'
|
||||||
|
elif values.get('refund_type') == "debit":
|
||||||
|
invoice_id.operation_type = '30'
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def action_set_dian_document(self):
|
||||||
|
msg = _("The 'delivery date' must be equal or greater per maximum 10 days to "
|
||||||
|
"the 'invoice date'.")
|
||||||
|
timezone = pytz.timezone(self.env.user.tz or 'America/Bogota')
|
||||||
|
from_zone = tz.gettz('UTC')
|
||||||
|
to_zone = tz.gettz(timezone.zone)
|
||||||
|
|
||||||
|
for invoice_id in self:
|
||||||
|
if not invoice_id.company_id.einvoicing_enabled:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if invoice_id.journal_id.sequence_id.dian_type not in DIAN_TYPES:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if invoice_id.dian_document_ids.filtered(lambda d: d.state != 'cancel'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not invoice_id.invoice_datetime:
|
||||||
|
invoice_datetime = datetime.strptime(
|
||||||
|
str(invoice_id.invoice_date) + ' 13:00:00',
|
||||||
|
'%Y-%m-%d %H:%M:%S').replace(tzinfo=from_zone)
|
||||||
|
invoice_datetime = invoice_datetime.astimezone(
|
||||||
|
to_zone).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
invoice_id.invoice_datetime = invoice_datetime
|
||||||
|
|
||||||
|
if (invoice_id.company_id.automatic_delivery_datetime
|
||||||
|
and not invoice_id.delivery_datetime):
|
||||||
|
invoice_datetime = invoice_id.invoice_datetime
|
||||||
|
hours_added = timedelta(
|
||||||
|
hours=invoice_id.company_id.additional_hours_delivery_datetime)
|
||||||
|
invoice_id.delivery_datetime = invoice_datetime + hours_added
|
||||||
|
|
||||||
|
if not invoice_id.delivery_datetime:
|
||||||
|
raise UserError(msg)
|
||||||
|
|
||||||
|
invoice_date = invoice_id.invoice_date
|
||||||
|
delivery_date = datetime.strftime(invoice_id.delivery_datetime, '%Y-%m-%d')
|
||||||
|
delivery_date = datetime.strptime(delivery_date, '%Y-%m-%d').date()
|
||||||
|
days = (delivery_date - invoice_date).days
|
||||||
|
|
||||||
|
if days < 0 or days > 10:
|
||||||
|
raise UserError(msg)
|
||||||
|
|
||||||
|
invoice_id._set_invoice_lines_price_reference()
|
||||||
|
xml_filename = False
|
||||||
|
zipped_filename = False
|
||||||
|
ar_xml_filename = False
|
||||||
|
ad_zipped_filename = False
|
||||||
|
|
||||||
|
for dian_document_id in invoice_id.dian_document_ids:
|
||||||
|
xml_filename = dian_document_id.xml_filename
|
||||||
|
zipped_filename = dian_document_id.zipped_filename
|
||||||
|
ar_xml_filename = dian_document_id.ar_xml_filename
|
||||||
|
ad_zipped_filename = dian_document_id.ad_zipped_filename
|
||||||
|
break
|
||||||
|
|
||||||
|
dian_document_id = self.env['account.move.dian.document'].create({
|
||||||
|
'invoice_id': invoice_id.id,
|
||||||
|
'company_id': invoice_id.company_id.id,
|
||||||
|
'xml_filename': xml_filename,
|
||||||
|
'zipped_filename': zipped_filename,
|
||||||
|
'ar_xml_filename': ar_xml_filename,
|
||||||
|
'ad_zipped_filename': ad_zipped_filename})
|
||||||
|
set_files = dian_document_id.action_set_files()
|
||||||
|
|
||||||
|
if (not invoice_id.send_invoice_to_dian
|
||||||
|
and invoice_id.company_id.send_invoice_to_dian):
|
||||||
|
invoice_id.send_invoice_to_dian = invoice_id.company_id.send_invoice_to_dian
|
||||||
|
|
||||||
|
if (not invoice_id.invoice_type_code
|
||||||
|
and invoice_id.company_id.invoice_type_code):
|
||||||
|
invoice_id.invoice_type_code = invoice_id.company_id.invoice_type_code
|
||||||
|
|
||||||
|
if invoice_id.send_invoice_to_dian == '0':
|
||||||
|
if set_files:
|
||||||
|
if invoice_id.invoice_type_code in ('01', '02', '05'):
|
||||||
|
if dian_document_id.zip_key:
|
||||||
|
to_return = dian_document_id.action_GetStatusZip()
|
||||||
|
else:
|
||||||
|
to_return = dian_document_id._get_GetStatus(True)
|
||||||
|
|
||||||
|
if not to_return:
|
||||||
|
dian_document_id.action_send_zipped_file()
|
||||||
|
elif invoice_id.invoice_type_code == '04':
|
||||||
|
dian_document_id.action_send_mail()
|
||||||
|
else:
|
||||||
|
dian_document_id.send_failure_mail()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action_process_dian_document(self):
|
||||||
|
for invoice_id in self:
|
||||||
|
dian_document_id = invoice_id.dian_document_ids.filtered(
|
||||||
|
lambda d: d.state not in ('cancel', 'done'))
|
||||||
|
|
||||||
|
if dian_document_id:
|
||||||
|
dian_document_id.action_process()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action_merge_with_sale_order(self):
|
||||||
|
for invoice_id in self:
|
||||||
|
sale_order_id = invoice_id.sale_order_id
|
||||||
|
|
||||||
|
if sale_order_id:
|
||||||
|
move_id = sale_order_id._create_invoices()
|
||||||
|
move_line_data = []
|
||||||
|
|
||||||
|
for invoice_line_id in invoice_id.invoice_line_ids:
|
||||||
|
move_line_data.append({
|
||||||
|
'move_id': move_id.id,
|
||||||
|
'currency_id': invoice_line_id.currency_id.id,
|
||||||
|
'product_id': invoice_line_id.product_id.id,
|
||||||
|
'name': invoice_line_id.name,
|
||||||
|
'quantity': invoice_line_id.quantity,
|
||||||
|
'product_uom_id': invoice_line_id.product_uom_id.id,
|
||||||
|
'price_unit': invoice_line_id.price_unit,
|
||||||
|
'discount': invoice_line_id.discount,
|
||||||
|
'tax_ids': invoice_line_id.tax_ids,
|
||||||
|
'price_subtotal': invoice_line_id.price_subtotal,
|
||||||
|
'price_total': invoice_line_id.price_total,
|
||||||
|
'cost_price': invoice_line_id.cost_price,
|
||||||
|
'reference_price': invoice_line_id.reference_price})
|
||||||
|
|
||||||
|
self.env['account.move.summary.line'].create(move_line_data)
|
||||||
|
move_id.write({
|
||||||
|
'name': invoice_id.name,
|
||||||
|
'move_name': invoice_id.move_name,
|
||||||
|
'invoice_date': invoice_id.invoice_date,
|
||||||
|
'summary_amount_untaxed': invoice_id.amount_untaxed,
|
||||||
|
'summary_amount_tax': invoice_id.amount_tax,
|
||||||
|
'summary_amount_total': invoice_id.amount_total})
|
||||||
|
mail_message_ids = self.env['mail.message'].search(
|
||||||
|
[('model', '=', 'account.move'), ('res_id', '=', invoice_id.id)])
|
||||||
|
|
||||||
|
if mail_message_ids:
|
||||||
|
mail_message_ids.write({'res_id': move_id.id})
|
||||||
|
|
||||||
|
if invoice_id.dian_document_ids:
|
||||||
|
invoice_id.dian_document_ids.write({'invoice_id': move_id.id})
|
||||||
|
move_id.write({
|
||||||
|
'invoice_datetime': invoice_id.invoice_datetime,
|
||||||
|
'delivery_datetime': invoice_id.delivery_datetime,
|
||||||
|
'operation_type': invoice_id.operation_type,
|
||||||
|
'invoice_type_code': invoice_id.invoice_type_code,
|
||||||
|
'send_invoice_to_dian': invoice_id.send_invoice_to_dian,
|
||||||
|
'access_token': invoice_id.access_token,
|
||||||
|
'is_accepted_rejected': invoice_id.is_accepted_rejected,
|
||||||
|
'accepted_rejected_datetime': invoice_id.accepted_rejected_datetime,
|
||||||
|
'receipt_document_reference': invoice_id.receipt_document_reference,
|
||||||
|
'dian_document_state': invoice_id.dian_document_state,
|
||||||
|
'send_invoice_to_dian': invoice_id.send_invoice_to_dian})
|
||||||
|
|
||||||
|
if not invoice_id.dian_document_ids.filtered(lambda d: d.state == 'done'):
|
||||||
|
move_id.write({'merge_with_sale_order': True})
|
||||||
|
|
||||||
|
invoice_id.write({'move_name': False})
|
||||||
|
invoice_id.with_context(force_delete=True).unlink()
|
||||||
|
|
||||||
|
return sale_order_id.action_view_invoice()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action_post(self):
|
||||||
|
msg = _('Invoice totals do not match summary totals.')
|
||||||
|
res = super(AccountMove, self).action_post()
|
||||||
|
|
||||||
|
for invoice_id in self:
|
||||||
|
invoice_id.action_set_dian_document()
|
||||||
|
|
||||||
|
if invoice_id.merge_with_sale_order:
|
||||||
|
if not invoice_id.summary_line_ids:
|
||||||
|
invoice_id.button_draft()
|
||||||
|
else:
|
||||||
|
if (invoice_id.summary_amount_untaxed != invoice_id.amount_untaxed
|
||||||
|
or invoice_id.summary_amount_tax != invoice_id.amount_tax
|
||||||
|
or invoice_id.summary_amount_total != invoice_id.amount_total):
|
||||||
|
raise UserError(msg)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def button_draft(self):
|
||||||
|
msg = _('You cannot cancel a invoice sent to the DIAN and that was approved.')
|
||||||
|
user = self.env['res.users'].search([('id', '=', self.env.user.id)])
|
||||||
|
allow_cancel_invoice_dian_document_done = False
|
||||||
|
|
||||||
|
if user.has_group(
|
||||||
|
'l10n_co_account_e_invoicing.group_allow_cancel_invoice_dian_document_done'):
|
||||||
|
allow_cancel_invoice_dian_document_done = True
|
||||||
|
|
||||||
|
for invoice_id in self:
|
||||||
|
if invoice_id.merge_with_sale_order and not invoice_id.summary_line_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for dian_document_id in invoice_id.dian_document_ids:
|
||||||
|
if dian_document_id.state == 'done':
|
||||||
|
if not allow_cancel_invoice_dian_document_done:
|
||||||
|
raise UserError(msg)
|
||||||
|
else:
|
||||||
|
dian_document_id.state = 'cancel'
|
||||||
|
self.write({'dian_document_state': 'pending'})
|
||||||
|
|
||||||
|
return super(AccountMove, self).button_draft()
|
||||||
1108
l10n_co_account_e_invoicing/models/account_move_dian_document.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class AccountInvoiceDianDocumentLine(models.Model):
|
||||||
|
_name = "account.move.dian.document.line"
|
||||||
|
_description = 'DIAN Document Lines'
|
||||||
|
|
||||||
|
dian_document_id = fields.Many2one(
|
||||||
|
comodel_name='account.move.dian.document',
|
||||||
|
string='DIAN Document')
|
||||||
|
send_async_status_code = fields.Char(string='Status Code')
|
||||||
|
send_async_reason = fields.Char(string='Reason')
|
||||||
|
send_async_response = fields.Text(string='Response')
|
||||||
44
l10n_co_account_e_invoicing/models/account_move_line.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class AccountMoveLine(models.Model):
|
||||||
|
_inherit = "account.move.line"
|
||||||
|
|
||||||
|
cost_price = fields.Float(
|
||||||
|
string='Cost Price',
|
||||||
|
digits='Product Price')
|
||||||
|
reference_price = fields.Float(
|
||||||
|
string='Reference Price',
|
||||||
|
digits='Product Price')
|
||||||
|
|
||||||
|
def _get_invoice_lines_taxes(self, tax, tax_amount, invoice_line_taxes_total):
|
||||||
|
tax_code = tax.tax_group_id.tax_group_type_id.code
|
||||||
|
tax_name = tax.tax_group_id.tax_group_type_id.name
|
||||||
|
tax_percent = '{:.2f}'.format(tax_amount)
|
||||||
|
|
||||||
|
if tax_code not in invoice_line_taxes_total:
|
||||||
|
invoice_line_taxes_total[tax_code] = {}
|
||||||
|
invoice_line_taxes_total[tax_code]['total'] = 0
|
||||||
|
invoice_line_taxes_total[tax_code]['name'] = tax_name
|
||||||
|
invoice_line_taxes_total[tax_code]['taxes'] = {}
|
||||||
|
|
||||||
|
if tax_percent not in invoice_line_taxes_total[tax_code]['taxes']:
|
||||||
|
invoice_line_taxes_total[tax_code]['taxes'][tax_percent] = {}
|
||||||
|
invoice_line_taxes_total[tax_code]['taxes'][tax_percent]['base'] = 0
|
||||||
|
invoice_line_taxes_total[tax_code]['taxes'][tax_percent]['amount'] = 0
|
||||||
|
|
||||||
|
invoice_line_taxes_total[tax_code]['total'] += (
|
||||||
|
self.price_subtotal * tax_amount / 100)
|
||||||
|
invoice_line_taxes_total[tax_code]['taxes'][tax_percent]['base'] += (
|
||||||
|
self.price_subtotal)
|
||||||
|
invoice_line_taxes_total[tax_code]['taxes'][tax_percent]['amount'] += (
|
||||||
|
self.price_subtotal * tax_amount / 100)
|
||||||
|
|
||||||
|
return invoice_line_taxes_total
|
||||||
|
|
||||||
|
def _get_information_content_provider_party_values(self):
|
||||||
|
return {'IDschemeID': False, 'IDschemeName': False, 'ID': False}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class AccountMoveLineSummary(models.Model):
|
||||||
|
_name = "account.move.line.summary"
|
||||||
|
_description = "Journal Item Summary"
|
||||||
|
|
||||||
|
move_id = fields.Many2one(
|
||||||
|
'account.move',
|
||||||
|
string='Journal Entry',
|
||||||
|
index=True,
|
||||||
|
required=True,
|
||||||
|
readonly=True,
|
||||||
|
auto_join=True,
|
||||||
|
ondelete="cascade",
|
||||||
|
check_company=True,
|
||||||
|
help="The move of this entry line.")
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
related='move_id.company_id',
|
||||||
|
store=True,
|
||||||
|
readonly=True,
|
||||||
|
default=lambda self: self.env.company)
|
||||||
|
product_id = fields.Many2one(
|
||||||
|
'product.product',
|
||||||
|
string='Product',
|
||||||
|
ondelete='restrict')
|
||||||
|
product_uom_category_id = fields.Many2one(
|
||||||
|
'uom.category',
|
||||||
|
related='product_id.uom_id.category_id')
|
||||||
|
name = fields.Char(string='Label', tracking=True)
|
||||||
|
quantity = fields.Float(
|
||||||
|
string='Quantity',
|
||||||
|
default=1.0,
|
||||||
|
digits='Product Unit of Measure')
|
||||||
|
product_uom_id = fields.Many2one(
|
||||||
|
'uom.uom',
|
||||||
|
string='Unit of Measure',
|
||||||
|
domain="[('category_id', '=', product_uom_category_id)]")
|
||||||
|
price_unit = fields.Float(
|
||||||
|
string='Unit Price',
|
||||||
|
digits='Product Price')
|
||||||
|
discount = fields.Float(
|
||||||
|
string='Discount (%)',
|
||||||
|
digits='Discount',
|
||||||
|
default=0.0)
|
||||||
|
cost_price = fields.Float(
|
||||||
|
string='Cost Price',
|
||||||
|
digits='Product Price')
|
||||||
|
reference_price = fields.Float(
|
||||||
|
string='Reference Price',
|
||||||
|
digits='Product Price')
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class AccountMoveSummaryLine(models.Model):
|
||||||
|
_name = "account.move.summary.line"
|
||||||
|
_description = "Summary of Invoice Lines"
|
||||||
|
|
||||||
|
move_id = fields.Many2one(
|
||||||
|
comodel_name='account.move',
|
||||||
|
string='Journal Entry',
|
||||||
|
index=True,
|
||||||
|
required=True,
|
||||||
|
readonly=True,
|
||||||
|
auto_join=True,
|
||||||
|
ondelete="cascade",
|
||||||
|
check_company=True,
|
||||||
|
help="The move of this entry line.")
|
||||||
|
currency_id = fields.Many2one(
|
||||||
|
comodel_name='res.currency',
|
||||||
|
string='Currency',
|
||||||
|
required=True)
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
related='move_id.company_id',
|
||||||
|
store=True,
|
||||||
|
readonly=True,
|
||||||
|
default=lambda self: self.env.company)
|
||||||
|
product_id = fields.Many2one(
|
||||||
|
comodel_name='product.product',
|
||||||
|
string='Product',
|
||||||
|
ondelete='restrict')
|
||||||
|
name = fields.Char(string='Label', tracking=True)
|
||||||
|
quantity = fields.Float(
|
||||||
|
string='Quantity',
|
||||||
|
default=1.0,
|
||||||
|
digits='Product Unit of Measure')
|
||||||
|
product_uom_category_id = fields.Many2one(
|
||||||
|
comodel_name='uom.category',
|
||||||
|
related='product_id.uom_id.category_id')
|
||||||
|
product_uom_id = fields.Many2one(
|
||||||
|
comodel_name='uom.uom',
|
||||||
|
string='Unit of Measure',
|
||||||
|
domain="[('category_id', '=', product_uom_category_id)]")
|
||||||
|
price_unit = fields.Float(
|
||||||
|
string='Unit Price',
|
||||||
|
digits='Product Price')
|
||||||
|
discount = fields.Float(
|
||||||
|
string='Discount (%)',
|
||||||
|
digits='Discount',
|
||||||
|
default=0.0)
|
||||||
|
tax_ids = fields.Many2many(
|
||||||
|
comodel_name='account.tax',
|
||||||
|
string="Taxes",
|
||||||
|
context={'active_test': False},
|
||||||
|
check_company=True,
|
||||||
|
help="Taxes that apply on the base amount")
|
||||||
|
price_subtotal = fields.Monetary(
|
||||||
|
string='Subtotal',
|
||||||
|
store=True,
|
||||||
|
readonly=True,
|
||||||
|
currency_field='currency_id')
|
||||||
|
price_total = fields.Monetary(
|
||||||
|
string='Total',
|
||||||
|
store=True,
|
||||||
|
readonly=True,
|
||||||
|
currency_field='currency_id')
|
||||||
|
cost_price = fields.Float(string='Cost Price', digits='Product Price')
|
||||||
|
reference_price = fields.Float(string='Reference Price', digits='Product Price')
|
||||||
13
l10n_co_account_e_invoicing/models/account_tax_group.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountTaxGroup(models.Model):
|
||||||
|
_inherit = "account.tax.group"
|
||||||
|
|
||||||
|
is_einvoicing = fields.Boolean(
|
||||||
|
string="Does it Apply for E-Invoicing?",
|
||||||
|
default=True)
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
import re
|
||||||
|
from odoo import api, fields, models
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class EInvoiceNotificationGroup(models.Model):
|
||||||
|
_name = 'einvoice.notification.group'
|
||||||
|
_description = 'Notification Groups'
|
||||||
|
|
||||||
|
name = fields.Char(string='Name')
|
||||||
|
email = fields.Char(string='Email')
|
||||||
|
company_id = fields.Many2one(comodel_name='res.company', string='Company')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
rec = super(EInvoiceNotificationGroup, self).create(vals)
|
||||||
|
# Check email address is valid or not
|
||||||
|
if rec.email:
|
||||||
|
if re.match(
|
||||||
|
"^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",
|
||||||
|
rec.email) is None:
|
||||||
|
raise ValidationError("Please enter a valid email address")
|
||||||
|
|
||||||
|
return rec
|
||||||
|
|
||||||
|
def write(self, values):
|
||||||
|
result = super(EInvoiceNotificationGroup, self).write(values)
|
||||||
|
# Check email address is valid or not
|
||||||
|
if values.get('email'):
|
||||||
|
if re.match(
|
||||||
|
"^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$",
|
||||||
|
values.get('email')) is None:
|
||||||
|
raise ValidationError("Please enter a valid email address")
|
||||||
|
|
||||||
|
return result
|
||||||
244
l10n_co_account_e_invoicing/models/global_functions.py
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
from os import path
|
||||||
|
from uuid import uuid4
|
||||||
|
from base64 import b64encode, b64decode
|
||||||
|
from io import BytesIO
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import OpenSSL.crypto as crypto
|
||||||
|
from lxml import etree
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from pgxades import XAdESContext, PolicyId, template
|
||||||
|
import xmlsig
|
||||||
|
from qrcode import QRCode, constants
|
||||||
|
from odoo import _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
def get_software_security_code(IdSoftware, Pin, NroDocumentos):
|
||||||
|
uncoded_value = (IdSoftware + ' + ' + Pin + ' + ' + NroDocumentos)
|
||||||
|
software_security_code = IdSoftware + Pin + NroDocumentos
|
||||||
|
software_security_code = hashlib.sha384(software_security_code.encode('utf-8'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'SoftwareSecurityCodeUncoded': uncoded_value,
|
||||||
|
'SoftwareSecurityCode': software_security_code.hexdigest()}
|
||||||
|
|
||||||
|
|
||||||
|
def get_cufe_cude(
|
||||||
|
NumFac,
|
||||||
|
FecFac,
|
||||||
|
HorFac,
|
||||||
|
ValFac,
|
||||||
|
CodImp1,
|
||||||
|
ValImp1,
|
||||||
|
CodImp2,
|
||||||
|
ValImp2,
|
||||||
|
CodImp3,
|
||||||
|
ValImp3,
|
||||||
|
ValTot,
|
||||||
|
NitOFE,
|
||||||
|
DocAdq,
|
||||||
|
ClTec,
|
||||||
|
SoftwarePIN,
|
||||||
|
TipoAmbie):
|
||||||
|
# CUFE = SHA-384(NumFac + FecFac + HorFac + ValFac + CodImp1 + ValImp1 +
|
||||||
|
# CodImp2 + ValImp2 + CodImp3 + ValImp3 + ValTot + NitOFE + DocAdq +
|
||||||
|
# ClTec + TipoAmbie)
|
||||||
|
# CUDE = SHA-384(NumFac + FecFac + HorFac + ValFac + CodImp1 + ValImp1 +
|
||||||
|
# CodImp2 + ValImp2 + CodImp3 + ValImp3 + ValTot + NitOFE + DocAdq +
|
||||||
|
# Software-PIN + TipoAmbie)
|
||||||
|
uncoded_value = (NumFac + ' + ' + FecFac + ' + ' + HorFac + ' + ' +
|
||||||
|
ValFac + ' + ' + CodImp1 + ' + ' + ValImp1 + ' + ' +
|
||||||
|
CodImp2 + ' + ' + ValImp2 + ' + ' + CodImp3 + ' + ' +
|
||||||
|
ValImp3 + ' + ' + ValTot + ' + ' + NitOFE + ' + ' +
|
||||||
|
DocAdq + ' + ' + (ClTec if ClTec else SoftwarePIN) +
|
||||||
|
' + ' + TipoAmbie)
|
||||||
|
CUFE_CUDE = (
|
||||||
|
NumFac + FecFac + HorFac + ValFac + CodImp1 + ValImp1 + CodImp2 +
|
||||||
|
ValImp2 + CodImp3 + ValImp3 + ValTot + NitOFE + DocAdq +
|
||||||
|
(ClTec if ClTec else SoftwarePIN) + TipoAmbie)
|
||||||
|
CUFE_CUDE = hashlib.sha384(CUFE_CUDE.encode('utf-8'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'CUFE/CUDEUncoded': uncoded_value,
|
||||||
|
'CUFE/CUDE': CUFE_CUDE.hexdigest()}
|
||||||
|
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/38432809/dynamic-xml-template-generation-using-get-template-jinja2
|
||||||
|
def get_template_xml(values, template_name):
|
||||||
|
base_path = path.dirname(path.dirname(__file__))
|
||||||
|
env = Environment(loader=FileSystemLoader(path.join(base_path, 'templates')))
|
||||||
|
template_xml = env.get_template('{}.xml'.format(template_name))
|
||||||
|
xml = template_xml.render(values)
|
||||||
|
|
||||||
|
return xml.replace('&', '&').encode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def get_pkcs12(certificate_file, certificate_password):
|
||||||
|
msg = _("The certificate password or certificate file is not valid.\n\n"
|
||||||
|
"Exception: %s")
|
||||||
|
|
||||||
|
try:
|
||||||
|
return crypto.load_pkcs12(b64decode(certificate_file), certificate_password)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValidationError(msg % e)
|
||||||
|
|
||||||
|
|
||||||
|
# https://www.decalage.info/en/python/lxml-c14n
|
||||||
|
def get_xml_with_c14n(xml):
|
||||||
|
if not isinstance(xml, etree._Element):
|
||||||
|
xml = etree.fromstring(xml.encode('utf-8'))
|
||||||
|
|
||||||
|
out = BytesIO()
|
||||||
|
xml.getroottree().write_c14n(out)
|
||||||
|
value = b64encode(out.getvalue()).decode("utf-8")
|
||||||
|
out.close()
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# https://github.com/etobella/python-xades
|
||||||
|
def get_xml_with_signature(
|
||||||
|
xml_without_signature,
|
||||||
|
signature_policy_url,
|
||||||
|
signature_policy_file,
|
||||||
|
signature_policy_description,
|
||||||
|
certificate_file,
|
||||||
|
certificate_password):
|
||||||
|
# https://lxml.de/tutorial.html
|
||||||
|
# root = etree.fromstring(response.content)
|
||||||
|
# root = etree.tostring(root, encoding='utf-8')
|
||||||
|
# parser = etree.XMLParser(encoding='utf-8', remove_blank_text=True)
|
||||||
|
ds = "http://www.w3.org/2000/09/xmldsig#"
|
||||||
|
ext = "urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
|
||||||
|
parser = etree.XMLParser(remove_comments=True, remove_blank_text=True)
|
||||||
|
root = etree.fromstring(xml_without_signature, parser=parser)
|
||||||
|
signature_id = "xmldsig-{}".format(uuid4())
|
||||||
|
signature = xmlsig.template.create(
|
||||||
|
xmlsig.constants.TransformInclC14N,
|
||||||
|
xmlsig.constants.TransformRsaSha512,
|
||||||
|
signature_id)
|
||||||
|
|
||||||
|
# Complememto para añadir atributo faltante
|
||||||
|
for element in root.iter("{%s}SignatureValue" % ds):
|
||||||
|
element.attrib['Id'] = signature_id + "-sigvalue"
|
||||||
|
|
||||||
|
ref = xmlsig.template.add_reference(
|
||||||
|
signature,
|
||||||
|
xmlsig.constants.TransformSha512,
|
||||||
|
uri="",
|
||||||
|
name=signature_id + "-ref0")
|
||||||
|
xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
|
||||||
|
sp = xmlsig.template.add_reference(
|
||||||
|
signature,
|
||||||
|
xmlsig.constants.TransformSha512,
|
||||||
|
uri="#" + signature_id + "-signedprops",
|
||||||
|
uri_type="http://uri.etsi.org/01903#SignedProperties")
|
||||||
|
xmlsig.template.add_transform(sp, xmlsig.constants.TransformInclC14N)
|
||||||
|
xmlsig.template.add_reference(
|
||||||
|
signature,
|
||||||
|
xmlsig.constants.TransformSha512,
|
||||||
|
uri="#" + signature_id + "-keyinfo")
|
||||||
|
ki = xmlsig.template.ensure_key_info(signature, name=signature_id + "-keyinfo")
|
||||||
|
data = xmlsig.template.add_x509_data(ki)
|
||||||
|
xmlsig.template.x509_data_add_certificate(data)
|
||||||
|
xmlsig.template.x509_data_add_subject_name(data)
|
||||||
|
serial = xmlsig.template.x509_data_add_issuer_serial(data)
|
||||||
|
xmlsig.template.x509_issuer_serial_add_issuer_name(serial)
|
||||||
|
xmlsig.template.x509_issuer_serial_add_serial_number(serial)
|
||||||
|
xmlsig.template.add_key_value(ki)
|
||||||
|
qualifying = template.create_qualifying_properties(signature)
|
||||||
|
props = template.create_signed_properties(
|
||||||
|
qualifying, name=signature_id + "-signedprops")
|
||||||
|
template.add_claimed_role(props, "supplier")
|
||||||
|
policy = PolicyId()
|
||||||
|
policy.id = signature_policy_url
|
||||||
|
policy.name = signature_policy_description
|
||||||
|
policy.remote = b64decode(signature_policy_file)
|
||||||
|
policy.hash_method = xmlsig.constants.TransformSha512
|
||||||
|
ctx = XAdESContext(policy)
|
||||||
|
ctx.load_pkcs12(get_pkcs12(certificate_file, certificate_password))
|
||||||
|
root.append(signature)
|
||||||
|
ctx.sign(signature)
|
||||||
|
ctx.verify(signature)
|
||||||
|
root.remove(signature)
|
||||||
|
position = 0
|
||||||
|
|
||||||
|
for element in root.iter("{%s}ExtensionContent" % ext):
|
||||||
|
if position == 1:
|
||||||
|
element.append(signature)
|
||||||
|
|
||||||
|
position += 1
|
||||||
|
|
||||||
|
return get_xml_with_c14n(root)
|
||||||
|
|
||||||
|
|
||||||
|
def get_xml_soap_values(certificate_file, certificate_password):
|
||||||
|
Created = datetime.utcnow()
|
||||||
|
Expires = Created + timedelta(seconds=60000)
|
||||||
|
Created = Created.strftime("%Y-%m-%dT%H:%M:%S.001Z")
|
||||||
|
Expires = Expires.strftime("%Y-%m-%dT%H:%M:%S.001Z")
|
||||||
|
# https://github.com/mit-dig/idm/blob/master/idm_query_functions.py#L151
|
||||||
|
pkcs12 = get_pkcs12(certificate_file, certificate_password)
|
||||||
|
cert = pkcs12.get_certificate()
|
||||||
|
der = b64encode(crypto.dump_certificate(crypto.FILETYPE_ASN1, cert)).decode('utf-8')
|
||||||
|
|
||||||
|
return {
|
||||||
|
'Created': Created,
|
||||||
|
'Expires': Expires,
|
||||||
|
'Id': uuid4(),
|
||||||
|
'BinarySecurityToken': der}
|
||||||
|
|
||||||
|
|
||||||
|
def get_xml_soap_with_signature(
|
||||||
|
xml_soap_without_signature, Id, certificate_file, certificate_password):
|
||||||
|
wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
||||||
|
wsu = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||||
|
X509v3 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
|
||||||
|
parser = etree.XMLParser(remove_comments=True, remove_blank_text=True)
|
||||||
|
root = etree.fromstring(xml_soap_without_signature, parser=parser)
|
||||||
|
signature_id = "{}".format(Id)
|
||||||
|
signature = xmlsig.template.create(
|
||||||
|
xmlsig.constants.TransformExclC14N,
|
||||||
|
xmlsig.constants.TransformRsaSha256, # solo me ha funcionado con esta
|
||||||
|
"SIG-" + signature_id)
|
||||||
|
ref = xmlsig.template.add_reference(
|
||||||
|
signature, xmlsig.constants.TransformSha256, uri="#id-" + signature_id)
|
||||||
|
xmlsig.template.add_transform(ref, xmlsig.constants.TransformExclC14N)
|
||||||
|
ki = xmlsig.template.ensure_key_info(signature, name="KI-" + signature_id)
|
||||||
|
ctx = xmlsig.SignatureContext()
|
||||||
|
ctx.load_pkcs12(get_pkcs12(certificate_file, certificate_password))
|
||||||
|
|
||||||
|
for element in root.iter("{%s}Security" % wsse):
|
||||||
|
element.append(signature)
|
||||||
|
|
||||||
|
ki_str = etree.SubElement(ki, "{%s}SecurityTokenReference" % wsse)
|
||||||
|
ki_str.attrib["{%s}Id" % wsu] = "STR-" + signature_id
|
||||||
|
ki_str_reference = etree.SubElement(ki_str, "{%s}Reference" % wsse)
|
||||||
|
ki_str_reference.attrib['URI'] = "#X509-" + signature_id
|
||||||
|
ki_str_reference.attrib['ValueType'] = X509v3
|
||||||
|
ctx.sign(signature)
|
||||||
|
ctx.verify(signature)
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def get_qr_image(data):
|
||||||
|
qr_code = QRCode(
|
||||||
|
version=1,
|
||||||
|
error_correction=constants.ERROR_CORRECT_L,
|
||||||
|
box_size=20,
|
||||||
|
border=4)
|
||||||
|
qr_code.add_data(data)
|
||||||
|
qr_code.make(fit=True)
|
||||||
|
image = qr_code.make_image()
|
||||||
|
temp = BytesIO()
|
||||||
|
image.save(temp, format="PNG")
|
||||||
|
qr_image = b64encode(temp.getvalue()).decode('utf-8')
|
||||||
|
temp.close()
|
||||||
|
|
||||||
|
return qr_image
|
||||||
17
l10n_co_account_e_invoicing/models/ir_sequence.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class IrSequence(models.Model):
|
||||||
|
_inherit = 'ir.sequence'
|
||||||
|
|
||||||
|
dian_type = fields.Selection(
|
||||||
|
selection_add=[
|
||||||
|
('e-invoicing', _('E-Invoicing')),
|
||||||
|
('e-credit_note', _('E-Credit Note')),
|
||||||
|
('e-debit_note', _('E-Debit Note')),
|
||||||
|
('e-support_document', _('E-Support Document')),
|
||||||
|
('contingency_checkbook_e-invoicing', _('Contingency Checkbook E-Invoicing'))])
|
||||||
15
l10n_co_account_e_invoicing/models/ir_sequence_date_range.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class IrSequenceDateRange(models.Model):
|
||||||
|
_inherit = 'ir.sequence.date_range'
|
||||||
|
|
||||||
|
dian_type = fields.Selection(
|
||||||
|
string='DIAN Type',
|
||||||
|
related='sequence_id.dian_type',
|
||||||
|
store=False)
|
||||||
|
technical_key = fields.Char(string="Technical Key")
|
||||||
14
l10n_co_account_e_invoicing/models/product_scheme.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class ProductScheme(models.Model):
|
||||||
|
_name = 'product.scheme'
|
||||||
|
_description = 'Product Schemes'
|
||||||
|
|
||||||
|
code = fields.Char(string='schemeID')
|
||||||
|
name = fields.Char(string='schemeName')
|
||||||
|
scheme_agency_id = fields.Char(string='schemeAgencyID')
|
||||||
27
l10n_co_account_e_invoicing/models/product_template.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class ProductTemplate(models.Model):
|
||||||
|
_inherit = 'product.template'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_product_scheme(self):
|
||||||
|
return self.env['product.scheme'].search([('code', '=', '999')]).id
|
||||||
|
|
||||||
|
margin_percentage = fields.Float(
|
||||||
|
string='Margin Percentage',
|
||||||
|
help='The cost price + this percentage will be the reference price',
|
||||||
|
digits='Discount',
|
||||||
|
default=10)
|
||||||
|
reference_price = fields.Float(
|
||||||
|
string='Reference Price',
|
||||||
|
help='Use this field if the reference price does not depend on the cost price',
|
||||||
|
digits='Product Price')
|
||||||
|
product_scheme_id = fields.Many2one(
|
||||||
|
comodel_name='product.scheme',
|
||||||
|
string='Product Scheme',
|
||||||
|
default=_default_product_scheme)
|
||||||
208
l10n_co_account_e_invoicing/models/res_company.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from urllib import request
|
||||||
|
from requests import post, exceptions
|
||||||
|
from lxml import etree
|
||||||
|
import ssl
|
||||||
|
from . import global_functions
|
||||||
|
from odoo import api, models, fields, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
ssl._create_default_https_context = ssl._create_unverified_context
|
||||||
|
|
||||||
|
|
||||||
|
class ResCompany(models.Model):
|
||||||
|
_inherit = "res.company"
|
||||||
|
|
||||||
|
einvoicing_enabled = fields.Boolean(string='E-Invoicing Enabled')
|
||||||
|
automatic_delivery_datetime = fields.Boolean(
|
||||||
|
string='Automatic Delivery Datetime?')
|
||||||
|
additional_hours_delivery_datetime = fields.Float(
|
||||||
|
string='Additional Hours',
|
||||||
|
help='Additional hours to invoice date for delivery date',
|
||||||
|
digits=(12, 4),
|
||||||
|
default=False)
|
||||||
|
send_invoice_to_dian = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
('0', 'Immediately'),
|
||||||
|
('1', 'After 1 Day'),
|
||||||
|
('2', 'After 2 Days')],
|
||||||
|
string='Send Invoice to DIAN?',
|
||||||
|
default='0')
|
||||||
|
operation_type = fields.Selection(
|
||||||
|
selection=[('09', 'AIU'), ('10', 'Standard')],
|
||||||
|
string='Operation Type',
|
||||||
|
default=False,
|
||||||
|
copy=False)
|
||||||
|
invoice_type_code = fields.Selection(
|
||||||
|
selection=[('01', 'E-invoice of sale')],
|
||||||
|
string='Invoice Type',
|
||||||
|
default=False,
|
||||||
|
copy=False)
|
||||||
|
force_send_mail = fields.Boolean(string='Force Send Mail?')
|
||||||
|
#send_pdf_of_invoice = fields.Boolean(string='Send PDF of the Invoice?')
|
||||||
|
#send_attachments_of_invoice = fields.Boolean(string='Send Attachments of the Invoice?')
|
||||||
|
profile_execution_id = fields.Selection(
|
||||||
|
selection=[('1', 'Production'), ('2', 'Test')],
|
||||||
|
string='Destination Environment of Document',
|
||||||
|
default='2',
|
||||||
|
required=True)
|
||||||
|
have_technological_provider = fields.Boolean(
|
||||||
|
string='Do you have a technological provider?')
|
||||||
|
technological_provider_id = fields.Many2one(
|
||||||
|
string='Technological Provider',
|
||||||
|
comodel_name='res.partner')
|
||||||
|
assignment_code = fields.Char(string='Assignment Code', size=3)
|
||||||
|
test_set_id = fields.Char(string='Test Set ID')
|
||||||
|
software_id = fields.Char(string='Software ID')
|
||||||
|
software_pin = fields.Char(string='Software PIN')
|
||||||
|
certificate_filename = fields.Char(string='Certificate Filename')
|
||||||
|
certificate_file = fields.Binary(string='Certificate File')
|
||||||
|
certificate_password = fields.Char(string='Certificate Password')
|
||||||
|
certificate_date = fields.Date(string='Certificate Date Validity')
|
||||||
|
certificate_remaining_days = fields.Integer(
|
||||||
|
string='Certificate Remaining Days',
|
||||||
|
default=False)
|
||||||
|
signature_policy_url = fields.Char(
|
||||||
|
string='Signature Policy URL',
|
||||||
|
default='https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf')
|
||||||
|
signature_policy_filename = fields.Char(string='Signature Policy Filename')
|
||||||
|
signature_policy_file = fields.Binary(string='Signature Policy File')
|
||||||
|
signature_policy_description = fields.Char(
|
||||||
|
string='Signature Policy Description',
|
||||||
|
default='Política de firma para facturas electrónicas de la República de Colombia.')
|
||||||
|
einvoicing_email = fields.Char(
|
||||||
|
string='E-Invoice Email, From:',
|
||||||
|
help="Enter the e-invoice sender's email.")
|
||||||
|
validate_einvoicing_email = fields.Boolean(
|
||||||
|
string='Validate E-Invoicing Email?',
|
||||||
|
default=True)
|
||||||
|
einvoicing_partner_no_email = fields.Char(
|
||||||
|
string='Failed Emails, To:',
|
||||||
|
help='Enter the email where the invoice will be sent when the customer does not have an email.')
|
||||||
|
einvoicing_receives_all_emails = fields.Char(
|
||||||
|
string='Email that receives all emails')
|
||||||
|
report_template_id = fields.Many2one(
|
||||||
|
string='Report Template',
|
||||||
|
comodel_name='ir.actions.report')
|
||||||
|
send_failure_mail = fields.Boolean(
|
||||||
|
string='Send Failure Mail?',
|
||||||
|
default=False)
|
||||||
|
notification_group_ids = fields.One2many(
|
||||||
|
comodel_name='einvoice.notification.group',
|
||||||
|
inverse_name='company_id',
|
||||||
|
string='Notification Group')
|
||||||
|
get_numbering_range_response = fields.Text(string='GetNumberingRange Response')
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
msg = _('Invalid URL.')
|
||||||
|
|
||||||
|
if vals.get('signature_policy_url'):
|
||||||
|
try:
|
||||||
|
for company in self:
|
||||||
|
response = request.urlopen(
|
||||||
|
vals.get('signature_policy_url'), timeout=2)
|
||||||
|
|
||||||
|
if response.getcode() != 200:
|
||||||
|
raise ValidationError(msg)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValidationError(msg % e)
|
||||||
|
|
||||||
|
rec = super(ResCompany, self).write(vals)
|
||||||
|
|
||||||
|
if vals.get('certificate_file') or vals.get('certificate_password'):
|
||||||
|
for company in self:
|
||||||
|
pkcs12 = global_functions.get_pkcs12(
|
||||||
|
company.certificate_file, company.certificate_password)
|
||||||
|
x509 = pkcs12.get_certificate()
|
||||||
|
date = x509.get_notAfter()
|
||||||
|
company.certificate_date = datetime.strptime(
|
||||||
|
date.decode("utf-8"), '%Y%m%d%H%M%SZ').date()
|
||||||
|
|
||||||
|
return rec
|
||||||
|
|
||||||
|
def _get_GetNumberingRange_values(self):
|
||||||
|
xml_soap_values = global_functions.get_xml_soap_values(
|
||||||
|
self.certificate_file, self.certificate_password)
|
||||||
|
xml_soap_values['accountCode'] = self.partner_id.ref_num
|
||||||
|
xml_soap_values['accountCodeT'] = self.partner_id.ref_num
|
||||||
|
xml_soap_values['softwareCode'] = self.software_id
|
||||||
|
|
||||||
|
if self.have_technological_provider:
|
||||||
|
xml_soap_values['accountCodeT'] = self.technological_provider_id.ref_num
|
||||||
|
|
||||||
|
return xml_soap_values
|
||||||
|
|
||||||
|
def action_GetNumberingRange(self):
|
||||||
|
msg1 = _("Unknown Error,\nStatus Code: %s,\nReason: %s.")
|
||||||
|
msg2 = _("Unknown Error: %s\n.")
|
||||||
|
wsdl = 'https://vpfe.dian.gov.co/WcfDianCustomerServices.svc?wsdl'
|
||||||
|
s = "http://www.w3.org/2003/05/soap-envelope"
|
||||||
|
GetNumberingRange_values = self._get_GetNumberingRange_values()
|
||||||
|
GetNumberingRange_values['To'] = wsdl.replace('?wsdl', '')
|
||||||
|
xml_soap_with_signature = global_functions.get_xml_soap_with_signature(
|
||||||
|
global_functions.get_template_xml(
|
||||||
|
GetNumberingRange_values, 'GetNumberingRange'),
|
||||||
|
GetNumberingRange_values['Id'],
|
||||||
|
self.certificate_file,
|
||||||
|
self.certificate_password)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = post(
|
||||||
|
wsdl,
|
||||||
|
headers={'content-type': 'application/soap+xml;charset=utf-8'},
|
||||||
|
data=etree.tostring(xml_soap_with_signature))
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
root = etree.fromstring(response.text)
|
||||||
|
response = ''
|
||||||
|
|
||||||
|
for element in root.iter("{%s}Body" % s):
|
||||||
|
response = etree.tostring(element, pretty_print=True)
|
||||||
|
|
||||||
|
if response == '':
|
||||||
|
response = etree.tostring(root, pretty_print=True)
|
||||||
|
|
||||||
|
self.write({'get_numbering_range_response': response})
|
||||||
|
else:
|
||||||
|
raise ValidationError(msg1 % (response.status_code, response.reason))
|
||||||
|
|
||||||
|
except exceptions.RequestException as e:
|
||||||
|
raise ValidationError(msg2 % (e))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action_process_dian_documents(self):
|
||||||
|
for company in self:
|
||||||
|
count = 0
|
||||||
|
dian_documents = self.env['account.move.dian.document'].search(
|
||||||
|
[('state', 'in', ('draft', 'sent')), ('company_id', '=', company.id)],
|
||||||
|
order='zipped_filename asc')
|
||||||
|
|
||||||
|
for dian_document in dian_documents:
|
||||||
|
today = fields.Date.context_today(self)
|
||||||
|
date_from = dian_document.invoice_id.invoice_date
|
||||||
|
days = (today - date_from).days
|
||||||
|
|
||||||
|
if int(dian_document.invoice_id.send_invoice_to_dian) - 1 <= days:
|
||||||
|
try:
|
||||||
|
dian_document.action_process()
|
||||||
|
except:
|
||||||
|
count -= 1
|
||||||
|
|
||||||
|
if dian_document.state == 'done':
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
# 10 -> 50 -JEG
|
||||||
|
if count == 50:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def cron_process_dian_documents(self):
|
||||||
|
for company_id in self.search([]):
|
||||||
|
company_id.action_process_dian_documents()
|
||||||
248
l10n_co_account_e_invoicing/models/res_partner.py
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2021 Joan Marín <Github@JoanMarin>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
import re
|
||||||
|
from odoo import api, models, fields, _
|
||||||
|
from odoo.exceptions import UserError, ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = "res.partner"
|
||||||
|
|
||||||
|
is_einvoicing_agent = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
('yes', 'Yes'),
|
||||||
|
('no_but', 'No, but has email'),
|
||||||
|
('no', 'No'),
|
||||||
|
('unknown', 'Unknown')],
|
||||||
|
string='Is an E-Invoicing Agent?',
|
||||||
|
default=False)
|
||||||
|
einvoicing_email = fields.Char(string='E-Invoicing Email')
|
||||||
|
validate_einvoicing_email = fields.Boolean(
|
||||||
|
string='Validate E-Invoicing Email?',
|
||||||
|
related='company_id.validate_einvoicing_email')
|
||||||
|
view_einvoicing_email_field = fields.Boolean(
|
||||||
|
string="View 'E-Invoicing Email' Fields",
|
||||||
|
compute='_get_view_einvoicing_email_field',
|
||||||
|
store=False)
|
||||||
|
edit_is_einvoicing_agent_field = fields.Boolean(
|
||||||
|
string="Edit 'Is an E-Invoicing Agent?' Field",
|
||||||
|
compute='_get_edit_is_einvoicing_agent_field',
|
||||||
|
store=False)
|
||||||
|
|
||||||
|
@api.onchange('person_type')
|
||||||
|
def onchange_person_type(self):
|
||||||
|
if self.person_type == '1':
|
||||||
|
self.is_einvoicing_agent = 'yes'
|
||||||
|
|
||||||
|
self.property_account_position_id = False
|
||||||
|
|
||||||
|
def _get_view_einvoicing_email_field(self):
|
||||||
|
user = self.env['res.users'].search([('id', '=', self.env.user.id)])
|
||||||
|
view_einvoicing_email_field = False
|
||||||
|
|
||||||
|
if user.has_group(
|
||||||
|
'l10n_co_account_e_invoicing.group_view_einvoicing_email_fields'):
|
||||||
|
view_einvoicing_email_field = True
|
||||||
|
|
||||||
|
for partner in self:
|
||||||
|
partner.view_einvoicing_email_field = view_einvoicing_email_field
|
||||||
|
|
||||||
|
def _get_edit_is_einvoicing_agent_field(self):
|
||||||
|
user = self.env['res.users'].search([('id', '=', self.env.user.id)])
|
||||||
|
edit_is_einvoicing_agent_field = False
|
||||||
|
|
||||||
|
if user.has_group(
|
||||||
|
'l10n_co_account_e_invoicing.group_edit_is_einvoicing_agent_field'):
|
||||||
|
edit_is_einvoicing_agent_field = True
|
||||||
|
|
||||||
|
for partner in self:
|
||||||
|
partner.edit_is_einvoicing_agent_field = edit_is_einvoicing_agent_field
|
||||||
|
|
||||||
|
@api.constrains('einvoicing_email')
|
||||||
|
@api.onchange('einvoicing_email')
|
||||||
|
def validate_mail(self):
|
||||||
|
if self.einvoicing_email:
|
||||||
|
for email in self.einvoicing_email.replace(' ', '').split(","):
|
||||||
|
match = re.match(
|
||||||
|
r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", email)
|
||||||
|
|
||||||
|
if match is None:
|
||||||
|
raise ValidationError(_("The field 'E-Invoicing Email' is not "
|
||||||
|
"correctly filled.\n\n"
|
||||||
|
"Please add @ and dot (.)"))
|
||||||
|
|
||||||
|
def _get_accounting_partner_party_values(self):
|
||||||
|
msg1 = _("'%s' does not have a person type established.")
|
||||||
|
msg2 = _("'%s' does not have a city established.")
|
||||||
|
msg3 = _("'%s' does not have a state established.")
|
||||||
|
msg4 = _("'%s' does not have a country established.")
|
||||||
|
msg5 = _("'%s' does not have a verification digit established.")
|
||||||
|
msg6 = _("'%s' does not have a DIAN document type established.")
|
||||||
|
msg7 = _("The document type of '%s' does not seem to correspond with the "
|
||||||
|
"person type.")
|
||||||
|
msg8 = _("'%s' does not have a identification document established.")
|
||||||
|
msg9 = _("'%s' does not have a fiscal position correctly configured.")
|
||||||
|
msg10 = _("'%s' does not have a fiscal position established.")
|
||||||
|
msg11 = _("E-Invoicing Agent: '%s' does not have a E-Invoicing Email.")
|
||||||
|
name = self.name
|
||||||
|
zip_code = False
|
||||||
|
identification_document = self.ref_num
|
||||||
|
telephone = False
|
||||||
|
|
||||||
|
if not self.person_type:
|
||||||
|
raise UserError(msg1 % self.name)
|
||||||
|
|
||||||
|
if self.country_id:
|
||||||
|
if self.country_id.code == 'CO':
|
||||||
|
if not self.city_id:
|
||||||
|
raise UserError(msg2 % self.name)
|
||||||
|
elif not self.state_id:
|
||||||
|
raise UserError(msg3 % self.name)
|
||||||
|
else:
|
||||||
|
raise UserError(msg4 % self.name)
|
||||||
|
|
||||||
|
if self.ref_type_id:
|
||||||
|
document_type_code = self.ref_type_id.code_dian
|
||||||
|
|
||||||
|
if (document_type_code == '31'
|
||||||
|
and not self.verification_code
|
||||||
|
and str(self.verification_code) != '0'):
|
||||||
|
raise UserError(msg5 % self.name)
|
||||||
|
|
||||||
|
# Punto 13.2.1. Documento de identificación (Tipo de Identificador Fiscal):
|
||||||
|
# cbc:CompanyID.@schemeName; sts:ProviderID.@schemeName
|
||||||
|
# Factura electrónica del anexo tecnico version 1.8
|
||||||
|
if document_type_code not in (
|
||||||
|
'11', '12', '13', '21', '22', '31', '41', '42', '47', '50', '91'):
|
||||||
|
if self.person_type == '1':
|
||||||
|
raise UserError(msg6 % self.name)
|
||||||
|
else:
|
||||||
|
name = 'consumidor final'
|
||||||
|
document_type_code = '13'
|
||||||
|
identification_document = '222222222222'
|
||||||
|
else:
|
||||||
|
raise UserError(msg6 % self.name)
|
||||||
|
|
||||||
|
if self.person_type == '1' and document_type_code not in ('31', '50'):
|
||||||
|
raise UserError(msg7 % self.name)
|
||||||
|
|
||||||
|
if not identification_document:
|
||||||
|
raise UserError(msg8 % self.name)
|
||||||
|
|
||||||
|
if self.property_account_position_id:
|
||||||
|
if (not self.property_account_position_id.tax_level_code_ids
|
||||||
|
or not self.property_account_position_id.party_tax_scheme_id
|
||||||
|
or not self.property_account_position_id.listname):
|
||||||
|
raise UserError(msg9 % self.name)
|
||||||
|
|
||||||
|
tax_level_codes = ''
|
||||||
|
tax_scheme_code = self.property_account_position_id.party_tax_scheme_id.code
|
||||||
|
tax_scheme_name = self.property_account_position_id.party_tax_scheme_id.name
|
||||||
|
else:
|
||||||
|
raise UserError(msg10 % self.name)
|
||||||
|
|
||||||
|
if (self.validate_einvoicing_email
|
||||||
|
and (not self.is_einvoicing_agent
|
||||||
|
or self.is_einvoicing_agent in ('yes', 'not_but'))
|
||||||
|
and not self.einvoicing_email):
|
||||||
|
raise UserError(msg11 % self.name)
|
||||||
|
|
||||||
|
if self.zip_id:
|
||||||
|
zip_code = self.zip_id.name
|
||||||
|
|
||||||
|
for tax_level_code_id in self.property_account_position_id.tax_level_code_ids:
|
||||||
|
if tax_level_codes == '':
|
||||||
|
tax_level_codes = tax_level_code_id.code
|
||||||
|
else:
|
||||||
|
tax_level_codes += ';' + tax_level_code_id.code
|
||||||
|
|
||||||
|
if self.phone and self.mobile:
|
||||||
|
telephone = self.phone + " / " + self.mobile
|
||||||
|
elif self.phone:
|
||||||
|
telephone = self.phone
|
||||||
|
elif self.mobile:
|
||||||
|
telephone = self.mobile
|
||||||
|
|
||||||
|
if identification_document == '222222222222':
|
||||||
|
tax_level_codes = 'R-99-PN'
|
||||||
|
tax_scheme_code = 'ZZ'
|
||||||
|
tax_scheme_name = 'No causa'
|
||||||
|
|
||||||
|
if self.property_account_position_id.listname != '49':
|
||||||
|
raise UserError(msg8 % self.name)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'AdditionalAccountID': self.person_type,
|
||||||
|
'PartyName': self.commercial_name,
|
||||||
|
'Name': name,
|
||||||
|
'AddressID': (self.state_id.numeric_code or '') + (self.city_id.code or ''),
|
||||||
|
'AddressCityName': self.city_id.name or '',
|
||||||
|
'AddressPostalZone': zip_code,
|
||||||
|
'AddressCountrySubentity': self.state_id.name or '',
|
||||||
|
'AddressCountrySubentityCode': self.state_id.numeric_code or '',
|
||||||
|
'AddressLine': self.street or '',
|
||||||
|
'CompanyIDschemeID': self.verification_code,
|
||||||
|
'CompanyIDschemeName': document_type_code,
|
||||||
|
'CompanyID': identification_document,
|
||||||
|
'listName': self.property_account_position_id.listname,
|
||||||
|
'TaxLevelCode': tax_level_codes,
|
||||||
|
'TaxSchemeID': tax_scheme_code,
|
||||||
|
'TaxSchemeName': tax_scheme_name,
|
||||||
|
'CorporateRegistrationSchemeName': self.coc_registration_number,
|
||||||
|
'CountryIdentificationCode': self.country_id.code,
|
||||||
|
'CountryName': self.country_id.name,
|
||||||
|
'Telephone': telephone,
|
||||||
|
'Telefax': self.fax,
|
||||||
|
'ElectronicMail': self.einvoicing_email or self.email
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_delivery_values(self):
|
||||||
|
msg1 = _("'%s' does not have a city established.")
|
||||||
|
msg2 = _("'%s' does not have a state established.")
|
||||||
|
msg3 = _("'%s' does not have a country established.")
|
||||||
|
zip_code = False
|
||||||
|
|
||||||
|
if self.country_id:
|
||||||
|
if self.country_id.code == 'CO':
|
||||||
|
if not self.city_id:
|
||||||
|
raise UserError(msg1 % self.name)
|
||||||
|
elif not self.state_id:
|
||||||
|
raise UserError(msg2 % self.name)
|
||||||
|
else:
|
||||||
|
raise UserError(msg3 % self.name)
|
||||||
|
|
||||||
|
if self.zip_id:
|
||||||
|
zip_code = self.zip_id.name
|
||||||
|
|
||||||
|
return {
|
||||||
|
'AddressID': (self.state_id.numeric_code or '') + (self.city_id.code or ''),
|
||||||
|
'AddressCityName': self.city_id.name or '',
|
||||||
|
'AddressPostalZone': zip_code,
|
||||||
|
'AddressCountrySubentity': self.state_id.name or '',
|
||||||
|
'AddressCountrySubentityCode': self.state_id.numeric_code or '',
|
||||||
|
'AddressLine': self.street or '',
|
||||||
|
'CountryIdentificationCode': self.country_id.code,
|
||||||
|
'CountryName': self.country_id.name
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_tax_representative_party_values(self):
|
||||||
|
msg1 = _("'%s' does not have a verification digit established.")
|
||||||
|
msg2 = _("'%s' does not have a document type established.")
|
||||||
|
msg3 = _("'%s' does not have a identification document established.")
|
||||||
|
|
||||||
|
if self.ref_type_id:
|
||||||
|
if self.ref_type_id.code_dian == '31' and not self.verification_code:
|
||||||
|
raise UserError(msg1 % self.name)
|
||||||
|
else:
|
||||||
|
raise UserError(msg2 % self.name)
|
||||||
|
|
||||||
|
if not self.ref_num:
|
||||||
|
raise UserError(msg3 % self.name)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'IDschemeID': self.verification_code,
|
||||||
|
'IDschemeName': self.ref_type_id.code_dian,
|
||||||
|
'ID': self.ref_num
|
||||||
|
}
|
||||||
12
l10n_co_account_e_invoicing/report/report.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<report id="einvoice_report"
|
||||||
|
model="account.move"
|
||||||
|
string="Factura Electrónica DIAN"
|
||||||
|
report_type="qweb-pdf"
|
||||||
|
name="l10n_co_account_e_invoicing.report_einvoice"
|
||||||
|
file="l10n_co_account_e_invoicing.report_einvoice"
|
||||||
|
attachment="(object.state == 'posted') and ((object.name or 'INV').replace('/','_')+'.pdf')"
|
||||||
|
print_report_name="(object._get_report_base_filename())"
|
||||||
|
groups="l10n_co_account_e_invoicing.group_view_dian_documents"/>
|
||||||
|
</odoo>
|
||||||
459
l10n_co_account_e_invoicing/report/template_report.xml
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="customer_accept_invoice">
|
||||||
|
<title>Factura Aceptada</title>
|
||||||
|
|
||||||
|
<div style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
|
||||||
|
<div style="height:auto;text-align: center;font-size : 30px;color: #3b9c17;">
|
||||||
|
<strong>Factura Aceptada</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="height: 50px;text-align: center;font-size : 10px;border-collapse: separate;margin-top:10px">
|
||||||
|
<p style="margin-left:12px">
|
||||||
|
Por Avancys SAS
|
||||||
|
<a href="http://www.avancys.com">http://www.avancys.com</a>
|
||||||
|
. Proveedor tecnologico.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="customer_reject_invoice">
|
||||||
|
<title>Factura Rechazada</title>
|
||||||
|
|
||||||
|
<div style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
|
||||||
|
<div style="height:auto;text-align: center;font-size : 30px;color: #d66058;">
|
||||||
|
<strong>Factura Rechazada</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="height: 50px;text-align: center;font-size : 10px;border-collapse: separate;margin-top:10px">
|
||||||
|
<p style="margin-left:12px">
|
||||||
|
Por Avancys SAS
|
||||||
|
<a href="http://www.avancys.com">http://www.avancys.com</a>
|
||||||
|
. Proveedor tecnologico.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="report_einvoice_document">
|
||||||
|
<t t-call="web.basic_layout">
|
||||||
|
<style>
|
||||||
|
.infolabel {
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
div.infolabel {
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
div.infolabelbig {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
span.infodata{
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
span.infodatabottom{
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
strong.infodatabottom{
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 0px
|
||||||
|
}
|
||||||
|
tr.inforow {
|
||||||
|
line-height: 25px;
|
||||||
|
min-height: 25px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
.borderless td, .borderless tr {
|
||||||
|
border: none;
|
||||||
|
line-height: 15px;
|
||||||
|
min-height: 15px;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3">
|
||||||
|
<img t-if="o.company_id.logo" t-att-src="image_data_uri(o.company_id.logo)" width="130" height="auto" />
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.partner_id.name" />
|
||||||
|
<span style="font-size:10px">NIT </span><span style="font-size:10px" t-field="o.company_id.partner_id.ref_num" />
|
||||||
|
<br/>
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.street" />
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.street2" />
|
||||||
|
<br/>
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.partner_id.city_id.name" />
|
||||||
|
,
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.partner_id.country_id.name" />
|
||||||
|
<br/>
|
||||||
|
<span style="font-size:10px">TELEFONO </span><span style="font-size:10px" t-field="o.company_id.phone" />
|
||||||
|
<span style="font-size:10px" t-if="o.company_id.partner_id.mobile">/</span>
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.partner_id.mobile" />
|
||||||
|
<br/>
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.email" />
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
</div>
|
||||||
|
<div t-if="o.dian_document_ids" class="col-3" style="width: 120px; height: 120px; text-align: right;">
|
||||||
|
<t t-foreach="o.dian_document_ids" t-as="dd">
|
||||||
|
<t t-if="dd.qr_image and dd.state != 'cancel'">
|
||||||
|
<img t-att-src="image_data_uri(dd.qr_image)" style="width:120px;height:120px"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6 card card-body">
|
||||||
|
<table class="table borderless">
|
||||||
|
<tr class="inforow">
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
CLIENTE
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.name" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
NIT
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.ref_num" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
DIRECCIÓN
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.street" />
|
||||||
|
<span class="infodata" t-field="o.partner_id.street2" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
CIUDAD
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.city_id.name" />
|
||||||
|
-
|
||||||
|
<span class="infodata" t-field="o.partner_id.country_id.name" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
TELEFONO
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.phone" />
|
||||||
|
<span class="infodata" t-if="o.partner_id.mobile">/</span>
|
||||||
|
<span class="infodata" t-field="o.partner_id.mobile" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
EMAIL
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.partner_id.email" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
REFERENCIA
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.ref" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 card">
|
||||||
|
<div class="infolabelbig card-header">
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and not o.refund_type and o.dian_document_ids">
|
||||||
|
FACTURA ELECTRÓNICA DE VENTA
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and not o.refund_type and not o.dian_document_ids">
|
||||||
|
FACTURA DE VENTA
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type == 'out_refund' and o.refund_type == 'credit' and o.dian_document_ids">
|
||||||
|
NOTA CRÉDITO ELECTRONICA
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type == 'out_refund' and o.refund_type == 'credit' and not o.dian_document_ids">
|
||||||
|
NOTA CRÉDITO
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and o.refund_type == 'debit' and o.dian_document_ids">
|
||||||
|
NOTA DÉBITO ELECTRONICA
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and o.refund_type == 'debit' and not o.dian_document_ids">
|
||||||
|
NOTA DÉBITO
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type in ('in_invoice', 'in_refund') and o.dian_document_ids">
|
||||||
|
DOCUMENTO DE SOPORTE
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type == 'in_invoice' and not o.dian_document_ids">
|
||||||
|
FACTURA PROVEEDOR
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type == 'in_refund' and not o.dian_document_ids">
|
||||||
|
NOTA DÉBITO PROVEEDOR
|
||||||
|
</span>
|
||||||
|
<span t-if="o.move_type == 'out_invoice' and o.state == 'cancel'">CANCELADA</span>
|
||||||
|
<span t-if="o.name != '/'" t-field="o.name"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table borderless">
|
||||||
|
<tr class="inforow">
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
FECHA DE EMISION
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.invoice_datetime" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
FECHA DE VENCIMIENTO
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.invoice_date_due" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr t-if="o.invoice_payment_term_id">
|
||||||
|
<td>
|
||||||
|
<span class="w-15 infolabel">
|
||||||
|
TERMINO DE PAGO
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="infodata" t-field="o.invoice_payment_term_id.name" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<t t-foreach="o.dian_document_ids" t-as="dd">
|
||||||
|
<t t-if="dd.cufe_cude and o.move_type == 'out_invoice' and not o.refund_type and dd.state != 'cancel'">
|
||||||
|
<div class="col-12" style="font-size:8px; float:left; text-align:left; white-space: pre;"><strong>CUFE : </strong><span t-field="dd.cufe_cude"/></div>
|
||||||
|
</t>
|
||||||
|
<t t-if="dd.cufe_cude and dd.state != 'cancel' and (o.refund_type or o.move_type == 'in_invoice')">
|
||||||
|
<div class="col-12" style="font-size:8px; float:left; text-align:left; white-space: pre;"><strong>CUDE : </strong><span t-field="dd.cufe_cude"/></div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
|
||||||
|
<t t-set="display_discount" t-value="any(l.discount for l in o.invoice_line_ids)"/>
|
||||||
|
|
||||||
|
<table class="table table-sm o_main_table" name="invoice_line_table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th name="th_defaultcode" class="text-left"><span class="infolabel">N°</span></th>
|
||||||
|
<th name="th_defaultcode" class="text-left"><span class="infolabel">REFERENCIA</span></th>
|
||||||
|
<th name="th_description" class="text-left"><span class="infolabel">DESCRIPCIÓN</span></th>
|
||||||
|
<th name="th_quantity" class="text-right"><span class="infolabel">CANTIDAD</span></th>
|
||||||
|
<th name="th_priceunit" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span class="infolabel">PRECIO UNIT.</span></th>
|
||||||
|
<th name="th_price_unit" t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||||
|
<span class="infolabel">DESC.%</span>
|
||||||
|
</th>
|
||||||
|
<th name="th_taxes" t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span class="infolabel">IMPUESTOS</span></th>
|
||||||
|
<th name="th_subtotal" class="text-right">
|
||||||
|
<span groups="account.group_show_line_subtotals_tax_excluded" class="infolabel">VALOR</span>
|
||||||
|
<span groups="account.group_show_line_subtotals_tax_included" class="infolabel">PRECIO TOTAL</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="invoice_tbody">
|
||||||
|
<t t-set="current_subtotal" t-value="0"/>
|
||||||
|
<t t-set="lines" t-value="o.invoice_line_ids.sorted(key=lambda l: (-l.sequence, l.date, l.move_name, -l.id), reverse=True)"/>
|
||||||
|
<t t-set="n_item" t-value="0"/>
|
||||||
|
<t t-foreach="lines" t-as="line">
|
||||||
|
<t t-set="n_item" t-value="n_item+1"/>
|
||||||
|
<t t-set="current_subtotal" t-value="current_subtotal + line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||||
|
<t t-set="current_subtotal" t-value="current_subtotal + line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
|
||||||
|
|
||||||
|
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''">
|
||||||
|
<t t-if="not line.display_type" name="account_invoice_line_accountable">
|
||||||
|
<td name="account_invoice_line_number"><span class="infodatabottom" t-esc="n_item"/></td>
|
||||||
|
<td name="account_invoice_default_code"><span class="infodatabottom" t-field="line.product_id.default_code" t-options="{'widget': 'text'}"/></td>
|
||||||
|
<td name="account_invoice_line_name"><span class="infodatabottom" t-field="line.name" t-options="{'widget': 'text'}"/></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span class="infodatabottom" t-field="line.quantity"/>
|
||||||
|
<span class="infodatabottom" t-field="line.product_uom_id" groups="uom.group_uom"/>
|
||||||
|
</td>
|
||||||
|
<td t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="line.price_unit"/>
|
||||||
|
</td>
|
||||||
|
<td t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="line.discount"/>
|
||||||
|
</td>
|
||||||
|
<td t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">
|
||||||
|
<span class="infodatabottom" t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_ids))" id="line_tax_ids"/>
|
||||||
|
</td>
|
||||||
|
<td class="text-right o_price_total">
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="line.price_subtotal" groups="account.group_show_line_subtotals_tax_excluded"/>
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="line.price_total" groups="account.group_show_line_subtotals_tax_included"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
<t t-if="line.display_type == 'line_section'">
|
||||||
|
<td colspan="99">
|
||||||
|
<span t-field="line.name" t-options="{'widget': 'text'}"/>
|
||||||
|
</td>
|
||||||
|
<t t-set="current_section" t-value="line"/>
|
||||||
|
<t t-set="current_subtotal" t-value="0"/>
|
||||||
|
</t>
|
||||||
|
<t t-if="line.display_type == 'line_note'">
|
||||||
|
<td colspan="99">
|
||||||
|
<span t-field="line.name" t-options="{'widget': 'text'}"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<t t-if="current_section and (line_last or lines[line_index+1].display_type == 'line_section')">
|
||||||
|
<tr class="is-subtotal text-right">
|
||||||
|
<td colspan="99">
|
||||||
|
<strong class="mr16">SUBTOTAL</strong>
|
||||||
|
<span t-esc="current_subtotal" t-options="{"widget": "monetary", "display_currency": o.currency_id}"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div>
|
||||||
|
<span class="infodata" t-esc="n_item"></span> <span class="infodata" >Items</span>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div class="clearfix">
|
||||||
|
<div id="total" class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<t t-if="o.journal_id.sequence_id.description">
|
||||||
|
<div class="col-12" style="font-size:10px; float:left; text-align:left;"><span t-field="o.journal_id.sequence_id.description"/></div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-6">
|
||||||
|
<table class="table table-sm" style="page-break-inside: avoid;">
|
||||||
|
<tr class="border-black o_subtotal" style="">
|
||||||
|
<td><strong class="infodatabottom">SUBTOTAL</strong></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span class="infodatabottom" t-field="o.amount_untaxed"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<t t-foreach="o.amount_by_group" t-as="amount_by_group">
|
||||||
|
<tr style="">
|
||||||
|
<t t-if="len(o.line_ids.filtered(lambda line: line.tax_line_id)) in [0, 1] and o.amount_untaxed == amount_by_group[2]">
|
||||||
|
<td><span class="text-nowrap infodatabottom" t-esc="amount_by_group[0]"/></td>
|
||||||
|
<td class="text-right o_price_total">
|
||||||
|
<span class="text-nowrap infodatabottom" t-esc="amount_by_group[3]"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<td>
|
||||||
|
<span class="infodatabottom" t-esc="amount_by_group[0]"/>
|
||||||
|
<span class="text-nowrap infodatabottom"> sobre
|
||||||
|
<t t-esc="amount_by_group[4]"/>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right o_price_total">
|
||||||
|
<span class="text-nowrap infodatabottom" t-esc="amount_by_group[3]"/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
<tr class="border-black o_total">
|
||||||
|
<td><strong class="infodatabottom">TOTAL</strong></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span class="text-nowrap infodatabottom" t-field="o.amount_total"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p t-if="o.narration" name="comment">
|
||||||
|
<span class="infodata" t-field="o.narration"/>
|
||||||
|
</p>
|
||||||
|
<p t-if="o.fiscal_position_id.note" name="note">
|
||||||
|
<span class="infodata" t-field="o.fiscal_position_id.note"/>
|
||||||
|
</p>
|
||||||
|
<p t-if="o.invoice_incoterm_id" name="incoterm">
|
||||||
|
<strong class="infodatabottom">Incoterm: </strong><span class="infodata" t-field="o.invoice_incoterm_id.code"/> - <span class="infodata" t-field="o.invoice_incoterm_id.name"/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<t t-foreach="o.dian_document_ids" t-as="dd">
|
||||||
|
<t t-if="dd.state != 'cancel'">
|
||||||
|
<span style="font-size:9px"><b>FECHA DE VALIDACION: </b></span><span style="font-size:9px" t-esc="dd.validation_datetime"/><br/>
|
||||||
|
<span style="font-size:9px"><b>INFORMACIÓN: </b></span><a style="font-size:9px"><span t-esc="dd.invoice_url"/></a>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<div class="text-center border-top" >
|
||||||
|
<small>
|
||||||
|
<strong style="font-size:10px">
|
||||||
|
<span style="font-size:10px" t-field="o.company_id.website" />
|
||||||
|
</strong>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-left">
|
||||||
|
<p style="font-size:9px; margin-bottom: 10px">AvancysERP, por Avancys SAS, NIT 900297700</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="report_einvoice">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<t t-set="lang" t-value="o.invoice_user_id.sudo().lang if o.move_type in ('in_invoice', 'in_refund') else o.partner_id.lang"/>
|
||||||
|
<t t-call="l10n_co_account_e_invoicing.report_einvoice_document" t-lang="lang"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
10
l10n_co_account_e_invoicing/security/ir.model.access.csv
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_account_fiscal_position_party_tax_scheme_manager,account_fiscal_position_party_tax_scheme_manager,model_account_fiscal_position_party_tax_scheme,account.group_account_manager,1,1,1,1
|
||||||
|
access_account_fiscal_position_party_tax_scheme_users,account_fiscal_position_party_tax_scheme_users,model_account_fiscal_position_party_tax_scheme,,1,0,0,0
|
||||||
|
access_account_fiscal_position_tax_level_code_manager,account_fiscal_position_tax_level_code_manager,model_account_fiscal_position_tax_level_code,account.group_account_manager,1,1,1,1
|
||||||
|
access_account_fiscal_position_tax_level_code_users,account_fiscal_position_tax_level_code_users,model_account_fiscal_position_tax_level_code,,1,0,0,0
|
||||||
|
access_account_move_dian_document_line_user,account_move_dian_document_line_user,model_account_move_dian_document_line,,1,1,1,0
|
||||||
|
access_account_move_dian_document_user,account_move_dian_document_user,model_account_move_dian_document,,1,1,1,0
|
||||||
|
access_account_move_summary_line_user,account_move_summary_line_user,model_account_move_summary_line,,1,1,1,0
|
||||||
|
access_einvoice_notification_group_user,einvoice_notification_group_user,model_einvoice_notification_group,,1,1,1,1
|
||||||
|
access_product_scheme_user,product_scheme_user,model_product_scheme,,1,0,0,0
|
||||||
|
BIN
l10n_co_account_e_invoicing/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 229 B |
BIN
l10n_co_account_e_invoicing/static/images/account_invoice.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
l10n_co_account_e_invoicing/static/images/dian_document.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
l10n_co_account_e_invoicing/static/images/ir_sequence.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
l10n_co_account_e_invoicing/static/images/res_company.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
70
l10n_co_account_e_invoicing/templates/AttachedDocument.xml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<AttachedDocument xmlns="urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2"
|
||||||
|
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||||
|
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
|
||||||
|
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
|
||||||
|
xmlns:ccts="urn:un:unece:uncefact:data:specification:CoreComponentTypeSchemaModule:2"
|
||||||
|
xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
|
||||||
|
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
|
||||||
|
xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#">
|
||||||
|
<cbc:UBLVersionID>UBL 2.1</cbc:UBLVersionID>
|
||||||
|
<cbc:CustomizationID>Documentos adjuntos</cbc:CustomizationID>
|
||||||
|
<cbc:ProfileID>DIAN 2.1</cbc:ProfileID>
|
||||||
|
<cbc:ProfileExecutionID>{{ProfileExecutionID}}</cbc:ProfileExecutionID>
|
||||||
|
<cbc:ID>{{ID}}</cbc:ID>
|
||||||
|
<cbc:IssueDate>{{IssueDate}}</cbc:IssueDate>
|
||||||
|
<cbc:IssueTime>{{IssueTime}}</cbc:IssueTime>
|
||||||
|
<cbc:DocumentType>Contenedor de Factura Electrónica</cbc:DocumentType>
|
||||||
|
<cbc:ParentDocumentID>{{ParentDocumentID}}</cbc:ParentDocumentID>
|
||||||
|
<cac:SenderParty>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>{{SenderParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195" schemeID="{{SenderParty.CompanyIDschemeID}}" schemeName="{{SenderParty.CompanyIDschemeName}}">{{SenderParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<cbc:TaxLevelCode listName="{{SenderParty.listName}}">{{SenderParty.TaxLevelCode}}</cbc:TaxLevelCode>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{SenderParty.TaxSchemeID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{SenderParty.TaxSchemeName}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
</cac:SenderParty>
|
||||||
|
<cac:ReceiverParty>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>{{ReceiverParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195" schemeID="{{ReceiverParty.CompanyIDschemeID}}" schemeName="{{ReceiverParty.CompanyIDschemeName}}">{{ReceiverParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<cbc:TaxLevelCode listName="{{ReceiverParty.listName}}">{{ReceiverParty.TaxLevelCode}}</cbc:TaxLevelCode>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{ReceiverParty.TaxSchemeID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{ReceiverParty.TaxSchemeName}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
</cac:ReceiverParty>
|
||||||
|
<cac:Attachment>
|
||||||
|
<cac:ExternalReference>
|
||||||
|
<cbc:MimeCode>text/xml</cbc:MimeCode>
|
||||||
|
<cbc:EncodingCode>UTF-8</cbc:EncodingCode>
|
||||||
|
<cbc:Description><![CDATA[{{InvoiceCNDN}}]]></cbc:Description>
|
||||||
|
</cac:ExternalReference>
|
||||||
|
</cac:Attachment>
|
||||||
|
<cac:ParentDocumentLineReference>
|
||||||
|
<cbc:LineID>1</cbc:LineID>
|
||||||
|
<cac:DocumentReference>
|
||||||
|
<cbc:ID>{{ParentDocumentID}}</cbc:ID>
|
||||||
|
<cbc:UUID schemeName="CUFE-SHA384">{{UUID}}</cbc:UUID>
|
||||||
|
<cbc:IssueDate>{{ValidationDate}}</cbc:IssueDate>
|
||||||
|
<cbc:DocumentType>ApplicationResponse</cbc:DocumentType>
|
||||||
|
<cac:Attachment>
|
||||||
|
<cac:ExternalReference>
|
||||||
|
<cbc:MimeCode>text/xml</cbc:MimeCode>
|
||||||
|
<cbc:EncodingCode>UTF-8</cbc:EncodingCode>
|
||||||
|
<cbc:Description><![CDATA[{{ApplicationResponse}}]]></cbc:Description>
|
||||||
|
</cac:ExternalReference>
|
||||||
|
</cac:Attachment>
|
||||||
|
<cac:ResultOfVerification>
|
||||||
|
<cbc:ValidatorID>Unidad Especial Dirección de Impuestos y Aduanas Nacionales</cbc:ValidatorID>
|
||||||
|
<cbc:ValidationResultCode>02</cbc:ValidationResultCode>
|
||||||
|
<cbc:ValidationDate>{{ValidationDate}}</cbc:ValidationDate>
|
||||||
|
<cbc:ValidationTime>{{ValidationTime}}</cbc:ValidationTime>
|
||||||
|
</cac:ResultOfVerification>
|
||||||
|
</cac:DocumentReference>
|
||||||
|
</cac:ParentDocumentLineReference>
|
||||||
|
</AttachedDocument>
|
||||||
636
l10n_co_account_e_invoicing/templates/CreditNote.xml
Normal file
@ -0,0 +1,636 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<CreditNote xmlns="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"
|
||||||
|
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
|
||||||
|
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
|
||||||
|
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||||
|
xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
|
||||||
|
xmlns:sts="http://www.dian.gov.co/contratos/facturaelectronica/v1/Structures"
|
||||||
|
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
|
||||||
|
xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-CreditNote-2.1.xsd">
|
||||||
|
<ext:UBLExtensions>
|
||||||
|
<ext:UBLExtension>
|
||||||
|
<ext:ExtensionContent>
|
||||||
|
<sts:DianExtensions>
|
||||||
|
<sts:InvoiceSource>
|
||||||
|
<cbc:IdentificationCode listAgencyID="6"
|
||||||
|
listAgencyName="United Nations Economic Commission for Europe"
|
||||||
|
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1">CO</cbc:IdentificationCode>
|
||||||
|
</sts:InvoiceSource>
|
||||||
|
<sts:SoftwareProvider>
|
||||||
|
<sts:ProviderID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{ProviderIDschemeID}}"
|
||||||
|
schemeName="{{ProviderIDschemeName}}">{{ProviderID}}</sts:ProviderID>
|
||||||
|
<sts:SoftwareID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)">{{SoftwareID}}</sts:SoftwareID>
|
||||||
|
</sts:SoftwareProvider>
|
||||||
|
<sts:SoftwareSecurityCode schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)">{{SoftwareSecurityCode}}</sts:SoftwareSecurityCode>
|
||||||
|
<sts:AuthorizationProvider>
|
||||||
|
<sts:AuthorizationProviderID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="4"
|
||||||
|
schemeName="31">800197268</sts:AuthorizationProviderID>
|
||||||
|
</sts:AuthorizationProvider>
|
||||||
|
<sts:QRCode>NumFac: {{ID}}
|
||||||
|
FecFac: {{IssueDate}}
|
||||||
|
HorFac: {{IssueTime}}
|
||||||
|
NitFac: {{NitFac}}
|
||||||
|
DocAdq: {{DocAdq}}
|
||||||
|
ValFac: {{LineExtensionAmount}}
|
||||||
|
ValIva: {{ValIva}}
|
||||||
|
ValOtroIm: {{ValOtroIm}}
|
||||||
|
ValTolFac: {{PayableAmount}}
|
||||||
|
CUDE: {{UUID}}
|
||||||
|
{{QRCodeURL}}</sts:QRCode>
|
||||||
|
</sts:DianExtensions>
|
||||||
|
</ext:ExtensionContent>
|
||||||
|
</ext:UBLExtension>
|
||||||
|
<ext:UBLExtension>
|
||||||
|
<ext:ExtensionContent/>
|
||||||
|
</ext:UBLExtension>
|
||||||
|
</ext:UBLExtensions>
|
||||||
|
<cbc:UBLVersionID>UBL 2.1</cbc:UBLVersionID>
|
||||||
|
<cbc:CustomizationID>{{CustomizationID}}</cbc:CustomizationID>
|
||||||
|
<cbc:ProfileID>DIAN 2.1: Nota Crédito de Factura Electrónica de Venta</cbc:ProfileID>
|
||||||
|
<cbc:ProfileExecutionID>{{ProfileExecutionID}}</cbc:ProfileExecutionID>
|
||||||
|
<cbc:ID>{{ID}}</cbc:ID>
|
||||||
|
<cbc:UUID schemeID="{{ProfileExecutionID}}" schemeName="CUDE-SHA384">{{UUID}}</cbc:UUID>
|
||||||
|
<cbc:IssueDate>{{IssueDate}}</cbc:IssueDate>
|
||||||
|
<cbc:IssueTime>{{IssueTime}}</cbc:IssueTime>
|
||||||
|
<cbc:CreditNoteTypeCode>{{CreditNoteTypeCode}}</cbc:CreditNoteTypeCode>
|
||||||
|
<!--TODO 1.0: Que nota colocar?-->
|
||||||
|
<cbc:Note>{{Note}}</cbc:Note>
|
||||||
|
<cbc:DocumentCurrencyCode listAgencyID="6"
|
||||||
|
listAgencyName="United Nations Economic Commission for Europe"
|
||||||
|
listID="ISO 4217 Alpha">{{DocumentCurrencyCode}}</cbc:DocumentCurrencyCode>
|
||||||
|
<cbc:LineCountNumeric>{{LineCountNumeric}}</cbc:LineCountNumeric>
|
||||||
|
<!--TODO 3.0, Grupo de campos relativos al Periodo de Facturación: Intervalo de fechas la las que
|
||||||
|
referencia la factura por ejemplo en servicios públicos>
|
||||||
|
<cac:InvoicePeriod>
|
||||||
|
<cbc:StartDate>{{InvoicePeriodStartDate}}</cbc:StartDate>
|
||||||
|
<cbc:EndDate>{{InvoicePeriodEndDate}}</cbc:EndDate>
|
||||||
|
</cac:InvoicePeriod-->
|
||||||
|
<cac:DiscrepancyResponse>
|
||||||
|
<cbc:ReferenceID>{{DiscrepancyReferenceID}}</cbc:ReferenceID>
|
||||||
|
<cbc:ResponseCode>{{DiscrepancyResponseCode}}</cbc:ResponseCode>
|
||||||
|
<cbc:Description>{{DiscrepancyDescription}}</cbc:Description>
|
||||||
|
</cac:DiscrepancyResponse>
|
||||||
|
<!--TODO 1.0 TODAS LAS FACTURAS DEBEN SER DEL MISMO ADQUIRIENTE-->
|
||||||
|
<!--Si CreditNoteTypeCode igual a 20 es obligatorio-->
|
||||||
|
{% if BillingReference.ID %}
|
||||||
|
<cac:BillingReference>
|
||||||
|
<cac:InvoiceDocumentReference>
|
||||||
|
<cbc:ID>{{BillingReference.ID}}</cbc:ID>
|
||||||
|
<cbc:UUID schemeName="CUFE-SHA384">{{BillingReference.UUID}}</cbc:UUID>
|
||||||
|
<cbc:IssueDate>{{BillingReference.IssueDate}}</cbc:IssueDate>
|
||||||
|
</cac:InvoiceDocumentReference>
|
||||||
|
</cac:BillingReference>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: Si se habilita sale Error Regla ZB01>
|
||||||
|
<cac:OrderReference>
|
||||||
|
<cbc:ID>{{OrderReferenceID}}</cbc:ID>
|
||||||
|
<cbc:IssueDate>{{OrderReferenceIssueDate}}</cbc:IssueDate>
|
||||||
|
</cac:OrderReference-->
|
||||||
|
<!--TODO 3.0: opcional, solo interés mercantil, para referenciar uno o más documentos de despacho asociado>
|
||||||
|
<cac:DespatchDocumentReference>
|
||||||
|
<cbc:ID>8124167214 DA</cbc:ID>
|
||||||
|
<cbc:IssueDate>2019-12-12</cbc:IssueDate>
|
||||||
|
</cac:DespatchDocumentReference-->
|
||||||
|
<!--TODO 3.0: opcional, solo interés mercantil, para referenciar uno o más documentos de recepción asociado>
|
||||||
|
<cac:ReceiptDocumentReference>
|
||||||
|
<cbc:ID>12314129 GR</cbc:ID>
|
||||||
|
<cbc:IssueDate>2019-12-12</cbc:IssueDate>
|
||||||
|
</cac:ReceiptDocumentReference-->
|
||||||
|
<!--TODO 3.0: opcional, Referencia a documentos adicionales que hacen parte de la NC.
|
||||||
|
Especifación de este grupo igual a la del documento Invoice>
|
||||||
|
<cac:AdditionalDocumentReference>
|
||||||
|
<cbc:ID>12314129 GR</cbc:ID>
|
||||||
|
<cbc:IssueDate>2019-12-12</cbc:IssueDate>
|
||||||
|
<cbc:DocumentTypeCode>Punto 13.1.4. Referencia a otros documentos. (la tabla existe en el anexo 1.7 punto 6.1.4) del anexo tecnico version 1.8</cbc:DocumentTypeCode>
|
||||||
|
</cac:AdditionalDocumentReference-->
|
||||||
|
<cac:AccountingSupplierParty>
|
||||||
|
<cbc:AdditionalAccountID>{{AccountingSupplierParty.AdditionalAccountID}}</cbc:AdditionalAccountID>
|
||||||
|
<cac:Party>
|
||||||
|
{% if IndustryClassificationCode %}
|
||||||
|
<cbc:IndustryClassificationCode>{{IndustryClassificationCode}}</cbc:IndustryClassificationCode>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingSupplierParty.PartyName %}
|
||||||
|
<cac:PartyName>
|
||||||
|
<cbc:Name>{{AccountingSupplierParty.PartyName}}</cbc:Name>
|
||||||
|
</cac:PartyName>
|
||||||
|
{% endif %}
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>{{AccountingSupplierParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingSupplierParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingSupplierParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingSupplierParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingSupplierParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingSupplierParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingSupplierParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingSupplierParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingSupplierParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>{{AccountingSupplierParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{AccountingSupplierParty.CompanyIDschemeID}}"
|
||||||
|
schemeName="{{AccountingSupplierParty.CompanyIDschemeName}}">{{AccountingSupplierParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<!--TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49-->
|
||||||
|
<cbc:TaxLevelCode listName="{{AccountingSupplierParty.listName}}">{{AccountingSupplierParty.TaxLevelCode}}</cbc:TaxLevelCode>
|
||||||
|
<cac:RegistrationAddress>
|
||||||
|
<cbc:ID>{{AccountingSupplierParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingSupplierParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingSupplierParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingSupplierParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingSupplierParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingSupplierParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingSupplierParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingSupplierParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingCustomerParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:RegistrationAddress>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{AccountingSupplierParty.TaxSchemeID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{AccountingSupplierParty.TaxSchemeName}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
<cac:PartyLegalEntity>
|
||||||
|
<cbc:RegistrationName>{{AccountingSupplierParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{AccountingSupplierParty.CompanyIDschemeID}}"
|
||||||
|
schemeName="{{AccountingSupplierParty.CompanyIDschemeName}}">{{AccountingSupplierParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
{% if AccountingSupplierParty.CorporateRegistrationSchemeName %}
|
||||||
|
<cac:CorporateRegistrationScheme>
|
||||||
|
<cbc:Name>{{AccountingSupplierParty.CorporateRegistrationSchemeName}}</cbc:Name>
|
||||||
|
</cac:CorporateRegistrationScheme>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 3.0 Si se va a opera bajo modalidad de Consorcio, entonces este grupo de información debe ser informada.
|
||||||
|
De debe completar un grupo de elementos por cada participante del consorcio.>
|
||||||
|
<cac:ShareholderParty>
|
||||||
|
<cbc:PartecipationPercent>10.00</cbc:PartecipationPercent>
|
||||||
|
<cac:Party>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName></cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID=""
|
||||||
|
schemeName=""></cbc:CompanyID>
|
||||||
|
<TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49>
|
||||||
|
<cbc:TaxLevelCode listName=""></cbc:TaxLevelCode>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID></cbc:ID>
|
||||||
|
<cbc:Name></cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
</cac:Party>
|
||||||
|
</cac:ShareholderParty-->
|
||||||
|
</cac:PartyLegalEntity>
|
||||||
|
{% if AccountingSupplierParty.Telephone or AccountingSupplierParty.Telefax or AccountingSupplierParty.ElectronicMail %}
|
||||||
|
<cac:Contact>
|
||||||
|
<!--TODO 3.0: opcional, de momento se manda info de la empresa sin nombre de contacto>
|
||||||
|
<cbc:Name>Diana Cruz</cbc:Name-->
|
||||||
|
{% if AccountingSupplierParty.Telephone %}
|
||||||
|
<cbc:Telephone>{{AccountingSupplierParty.Telephone}}</cbc:Telephone>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingSupplierParty.Telefax %}
|
||||||
|
<cbc:Telefax>{{AccountingSupplierParty.Telefax}}</cbc:Telefax>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingSupplierParty.ElectronicMail %}
|
||||||
|
<cbc:ElectronicMail>{{AccountingSupplierParty.ElectronicMail}}</cbc:ElectronicMail>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: opcional, de momento se manda info de la empresa no se sabe que mandar aca de momento>
|
||||||
|
<cbc:Note></cbc:Note-->
|
||||||
|
</cac:Contact>
|
||||||
|
{% endif %}
|
||||||
|
</cac:Party>
|
||||||
|
</cac:AccountingSupplierParty>
|
||||||
|
<cac:AccountingCustomerParty>
|
||||||
|
<cbc:AdditionalAccountID>{{AccountingCustomerParty.AdditionalAccountID}}</cbc:AdditionalAccountID>
|
||||||
|
<cac:Party>
|
||||||
|
<cac:PartyIdentification>
|
||||||
|
<cbc:ID {% if AccountingCustomerParty.CompanyIDschemeName == '31' %}schemeID="{{AccountingCustomerParty.CompanyIDschemeID}}"{% endif %}
|
||||||
|
schemeName="{{AccountingCustomerParty.CompanyIDschemeName}}">{{AccountingCustomerParty.CompanyID}}</cbc:ID>
|
||||||
|
</cac:PartyIdentification>
|
||||||
|
{% if AccountingCustomerParty.PartyName %}
|
||||||
|
<cac:PartyName>
|
||||||
|
<cbc:Name>{{AccountingCustomerParty.PartyName}}</cbc:Name>
|
||||||
|
</cac:PartyName>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>{{AccountingCustomerParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingCustomerParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingCustomerParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingCustomerParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingCustomerParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingCustomerParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingCustomerParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingCustomerParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingCustomerParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
{% endif %}
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>{{AccountingCustomerParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
{% if AccountingCustomerParty.CompanyIDschemeName == '31' %}schemeID="{{AccountingCustomerParty.CompanyIDschemeID}}"{% endif %}
|
||||||
|
schemeName="{{AccountingCustomerParty.CompanyIDschemeName}}">{{AccountingCustomerParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<!--TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49-->
|
||||||
|
<cbc:TaxLevelCode listName="{{AccountingCustomerParty.listName}}">{{AccountingCustomerParty.TaxLevelCode}}</cbc:TaxLevelCode>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
<cac:RegistrationAddress>
|
||||||
|
<cbc:ID>{{AccountingCustomerParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingCustomerParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingCustomerParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingCustomerParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingCustomerParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingCustomerParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingCustomerParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingCustomerParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingCustomerParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:RegistrationAddress>
|
||||||
|
{% endif %}
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{AccountingCustomerParty.TaxSchemeID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{AccountingCustomerParty.TaxSchemeName}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
<cac:PartyLegalEntity>
|
||||||
|
<cbc:RegistrationName>{{AccountingCustomerParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
{% if AccountingCustomerParty.CompanyIDschemeName == '31' %}schemeID="{{AccountingCustomerParty.CompanyIDschemeID}}"{% endif %}
|
||||||
|
schemeName="{{AccountingCustomerParty.CompanyIDschemeName}}">{{AccountingCustomerParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
{% if AccountingCustomerParty.CorporateRegistrationSchemeName %}
|
||||||
|
<cac:CorporateRegistrationScheme>
|
||||||
|
<cbc:Name>{{AccountingCustomerParty.CorporateRegistrationSchemeName}}</cbc:Name>
|
||||||
|
</cac:CorporateRegistrationScheme>
|
||||||
|
{% endif %}
|
||||||
|
</cac:PartyLegalEntity>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' and (AccountingCustomerParty.Telephone or AccountingCustomerParty.Telefax or AccountingCustomerParty.ElectronicMail) %}
|
||||||
|
<cac:Contact>
|
||||||
|
<!--TODO 3.0: opcional, de momento se manda info de la empresa sin nombre de contacto>
|
||||||
|
<cbc:Name>Diana Cruz</cbc:Name-->
|
||||||
|
{% if AccountingCustomerParty.Telephone %}
|
||||||
|
<cbc:Telephone>{{AccountingCustomerParty.Telephone}}</cbc:Telephone>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.Telefax %}
|
||||||
|
<cbc:Telefax>{{AccountingCustomerParty.Telefax}}</cbc:Telefax>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.ElectronicMail %}
|
||||||
|
<cbc:ElectronicMail>{{AccountingCustomerParty.ElectronicMail}}</cbc:ElectronicMail>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: opcional, de momento se manda info de la empresa no se sabe que mandar aca de momento>
|
||||||
|
<cbc:Note></cbc:Note-->
|
||||||
|
</cac:Contact>
|
||||||
|
{% endif %}
|
||||||
|
</cac:Party>
|
||||||
|
</cac:AccountingCustomerParty>
|
||||||
|
<!--TODO 2.0 Grupo de información de la Persona autorizada para descargar documentos-->
|
||||||
|
<!--cac:TaxRepresentativeParty>
|
||||||
|
<cac:PartyIdentification>
|
||||||
|
<cbc:ID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID=""
|
||||||
|
schemeName=""></cbc:ID>
|
||||||
|
</cac:PartyIdentification>
|
||||||
|
</cac:TaxRepresentativeParty-->
|
||||||
|
<cac:Delivery>
|
||||||
|
<cbc:ActualDeliveryDate>{{ActualDeliveryDate}}</cbc:ActualDeliveryDate>
|
||||||
|
<cbc:ActualDeliveryTime>{{ActualDeliveryTime}}</cbc:ActualDeliveryTime>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
<cac:DeliveryAddress>
|
||||||
|
<cbc:ID>{{Delivery.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{Delivery.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if Delivery.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{Delivery.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{Delivery.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{Delivery.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{Delivery.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{Delivery.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{Delivery.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:DeliveryAddress>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 3.0, Grupo de datos con información sobre la empresa de transporte Desarrollo futuro>
|
||||||
|
<cac:DeliveryParty>
|
||||||
|
<cac:PartyName>
|
||||||
|
<cbc:Name></cbc:Name>
|
||||||
|
</cac:PartyName>
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>11001</cbc:ID>
|
||||||
|
<cbc:CityName>Bogotá, D.c. </cbc:CityName>
|
||||||
|
<cbc:PostalZone>Bogotá, D.c. </cbc:PostalZone>
|
||||||
|
<cbc:CountrySubentity>Bogotá</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>11</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>Av. #17 - 193</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>CO</cbc:IdentificationCode>
|
||||||
|
<TODO 2.0: languageID podria variar, siguiente version revisar esto>
|
||||||
|
<cbc:Name languageID="es">Colombia</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>Empresa de transporte</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="1"
|
||||||
|
schemeName="31">981223983</cbc:CompanyID>
|
||||||
|
<TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49>
|
||||||
|
<cbc:TaxLevelCode listName="">O-99</cbc:TaxLevelCode>
|
||||||
|
<cac:RegistrationAddress>
|
||||||
|
<cbc:ID>11001</cbc:ID>
|
||||||
|
<cbc:CityName>Bogotá, D.c. </cbc:CityName>
|
||||||
|
<cbc:PostalZone>Bogotá, D.c. </cbc:PostalZone>
|
||||||
|
<cbc:CountrySubentity>Bogotá</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>11</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>Av. #17 - 193</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>CO</cbc:IdentificationCode>
|
||||||
|
<TODO 2.0: languageID podria variar, siguiente version revisar esto>
|
||||||
|
<cbc:Name languageID="es">Colombia</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:RegistrationAddress>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>01</cbc:ID>
|
||||||
|
<cbc:Name>IVA</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
<cac:PartyLegalEntity>
|
||||||
|
<cbc:RegistrationName>Empresa de transporte</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="1"
|
||||||
|
schemeName="31">981223983</cbc:CompanyID>
|
||||||
|
<cac:CorporateRegistrationScheme>
|
||||||
|
<cbc:Name>75433</cbc:Name>
|
||||||
|
</cac:CorporateRegistrationScheme>
|
||||||
|
</cac:PartyLegalEntity>
|
||||||
|
<cac:Contact>
|
||||||
|
<cbc:Name>Eric Van Boxsom</cbc:Name>
|
||||||
|
<cbc:Telephone>9712311</cbc:Telephone>
|
||||||
|
<cbc:Telefax>12431241</cbc:Telefax>
|
||||||
|
<cbc:ElectronicMail>eric.vanboxsom@gosocket.net</cbc:ElectronicMail>
|
||||||
|
<cbc:Note>Test descripcion contacto</cbc:Note>
|
||||||
|
</cac:Contact>
|
||||||
|
</cac:DeliveryParty-->
|
||||||
|
</cac:Delivery>
|
||||||
|
{% if DeliveryTerms.LossRiskResponsibilityCode %}
|
||||||
|
<cac:DeliveryTerms>
|
||||||
|
<cbc:ID>1</cbc:ID>
|
||||||
|
<!--TODO 2.0: Con otro modulo complementario se puede resolver el texto libre-->
|
||||||
|
<!--cbc:SpecialTerms>Portes Pagados</cbc:SpecialTerms-->
|
||||||
|
<cbc:LossRiskResponsibilityCode>{{DeliveryTerms.LossRiskResponsibilityCode}}</cbc:LossRiskResponsibilityCode>
|
||||||
|
<cbc:LossRisk>{{DeliveryTerms.LossRisk}}</cbc:LossRisk>
|
||||||
|
</cac:DeliveryTerms>
|
||||||
|
{% endif %}
|
||||||
|
<cac:PaymentMeans>
|
||||||
|
<cbc:ID>{{PaymentMeansID}}</cbc:ID>
|
||||||
|
<cbc:PaymentMeansCode>{{PaymentMeansCode}}</cbc:PaymentMeansCode>
|
||||||
|
<cbc:PaymentDueDate>{{PaymentDueDate}}</cbc:PaymentDueDate>
|
||||||
|
<!--TODO 2.0: Identificador del pago, pueden ir de cero a varios PaymentID-->
|
||||||
|
<!--cbc:PaymentID>{{PaymentID}}</cbc:PaymentID-->
|
||||||
|
</cac:PaymentMeans>
|
||||||
|
{% if DocumentCurrencyCode != 'COP' %}
|
||||||
|
<cac:PaymentExchangeRate>
|
||||||
|
<cbc:SourceCurrencyCode>{{PaymentExchangeRate.SourceCurrencyCode}}</cbc:SourceCurrencyCode>
|
||||||
|
<cbc:SourceCurrencyBaseRate>1.00</cbc:SourceCurrencyBaseRate>
|
||||||
|
<cbc:TargetCurrencyCode>{{PaymentExchangeRate.TargetCurrencyCode}}</cbc:TargetCurrencyCode>
|
||||||
|
<cbc:TargetCurrencyBaseRate>1.00</cbc:TargetCurrencyBaseRate>
|
||||||
|
<cbc:CalculationRate>{{'{:.2f}'.format(PaymentExchangeRate.CalculationRate)}}</cbc:CalculationRate>
|
||||||
|
<cbc:Date>{{PaymentExchangeRate.Date}}</cbc:Date>
|
||||||
|
</cac:PaymentExchangeRate>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 3.0: Grupo de campos para información relacionadas con un cargo o un descuento>
|
||||||
|
<cac:AllowanceCharge>
|
||||||
|
<cbc:ID>SFR3123856</cbc:ID>
|
||||||
|
<cbc:ChargeIndicator></cbc:ChargeIndicator>
|
||||||
|
<cbc:AllowanceChargeReasonCode></cbc:AllowanceChargeReasonCode>
|
||||||
|
<cbc:AllowanceChargeReason></cbc:AllowanceChargeReason>
|
||||||
|
<cbc:MultiplierFactorNumeric>Prepago recibido</cbc:MultiplierFactorNumeric>
|
||||||
|
<cbc:Amount currencyID="{{DocumentCurrencyCode}}">1000.00</cbc:Amount>
|
||||||
|
<cbc:BaseAmount currencyID="{{DocumentCurrencyCode}}">1000.00</cbc:BaseAmount>
|
||||||
|
</cac:AllowanceCharge-->
|
||||||
|
{% for TaxTotalID, TaxTotal in TaxesTotal.items() %}
|
||||||
|
{% if TaxTotal.total != 0 %}
|
||||||
|
<cac:TaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 2.0: Se manda 0.00 por ahora mientras se evalua la necesidad de calculos-->
|
||||||
|
<cbc:RoundingAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:RoundingAmount>
|
||||||
|
{% for Percent, TaxSubtotal in TaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 1.0: Usado en el caso de que el tributo es un valor fijo>
|
||||||
|
<cbc:BaseUnitMeasure></cbc:BaseUnitMeasure>
|
||||||
|
<cbc:unitCode></cbc:unitCode>
|
||||||
|
<cbc:PerUnitAmount currencyID="{{DocumentCurrencyCode}}"></cbc:TaxAmount-->
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{TaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{TaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:TaxTotal>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for WithholdingTaxTotalID, WithholdingTaxTotal in WithholdingTaxesTotal.items() %}
|
||||||
|
<cac:WithholdingTaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(WithholdingTaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
{% for Percent, TaxSubtotal in WithholdingTaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{WithholdingTaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{WithholdingTaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:WithholdingTaxTotal>
|
||||||
|
{% endfor %}
|
||||||
|
<cac:LegalMonetaryTotal>
|
||||||
|
<cbc:LineExtensionAmount currencyID="{{DocumentCurrencyCode}}">{{LineExtensionAmount}}</cbc:LineExtensionAmount>
|
||||||
|
<cbc:TaxExclusiveAmount currencyID="{{DocumentCurrencyCode}}">{{TaxExclusiveAmount}}</cbc:TaxExclusiveAmount>
|
||||||
|
<cbc:TaxInclusiveAmount currencyID="{{DocumentCurrencyCode}}">{{TaxInclusiveAmount}}</cbc:TaxInclusiveAmount>
|
||||||
|
<!--TODO 3.0: los prepagos son los anticipos, no hay soporte aun en el odoo para identificar esto
|
||||||
|
PrepaidAmount == 0 mientras tanto, PrepaidAmount = suma de PrepaidPayment
|
||||||
|
AllowanceTotalAmount == 0 y ChargeTotalAmount == 0 mientras tanto, suma de AllowanceCharge
|
||||||
|
TaxInclusiveAmount == PayableAmount mientras tanto-->
|
||||||
|
<cbc:AllowanceTotalAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:AllowanceTotalAmount>
|
||||||
|
<cbc:ChargeTotalAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:ChargeTotalAmount>
|
||||||
|
<cbc:PrepaidAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:PrepaidAmount>
|
||||||
|
<cbc:PayableAmount currencyID="{{DocumentCurrencyCode}}">{{PayableAmount}}</cbc:PayableAmount>
|
||||||
|
</cac:LegalMonetaryTotal>
|
||||||
|
{% for CreditNoteLineID, CreditNoteLine in CreditNoteLines.items() %}
|
||||||
|
<cac:CreditNoteLine>
|
||||||
|
<cbc:ID>{{CreditNoteLineID}}</cbc:ID>
|
||||||
|
{% if BillingReference.CustomizationID == '09' %}
|
||||||
|
<cbc:Note>Contrato de servicios AIU por concepto de: {{CreditNoteLine.ItemDescription}}</cbc:Note>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CreditedQuantity unitCode="{{CreditNoteLine.unitCode}}">{{CreditNoteLine.Quantity}}</cbc:CreditedQuantity>
|
||||||
|
<cbc:LineExtensionAmount currencyID="{{DocumentCurrencyCode}}">{{CreditNoteLine.LineExtensionAmount}}</cbc:LineExtensionAmount>
|
||||||
|
{% if CreditNoteLine.LineExtensionAmount == '0.00' %}
|
||||||
|
<cac:PricingReference>
|
||||||
|
<cac:AlternativeConditionPrice>
|
||||||
|
<cbc:PriceAmount currencyID="{{DocumentCurrencyCode}}">{{CreditNoteLine.PricingReferencePriceAmount}}</cbc:PriceAmount>
|
||||||
|
<cbc:PriceTypeCode>01</cbc:PriceTypeCode>
|
||||||
|
</cac:AlternativeConditionPrice>
|
||||||
|
</cac:PricingReference>
|
||||||
|
{% endif %}
|
||||||
|
{% for TaxTotalID, TaxTotal in CreditNoteLine.TaxesTotal.items() %}
|
||||||
|
{% if TaxTotal.total != 0 %}
|
||||||
|
<cac:TaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 2.0: Se manda 0.00 por ahora mientras se evalua la necesidad de calculos-->
|
||||||
|
<cbc:RoundingAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:RoundingAmount>
|
||||||
|
{% for Percent, TaxSubtotal in TaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 1.0: Usado en el caso de que el tributo es un valor fijo>
|
||||||
|
<cbc:BaseUnitMeasure></cbc:BaseUnitMeasure>
|
||||||
|
<cbc:unitCode></cbc:unitCode>
|
||||||
|
<cbc:PerUnitAmount currencyID="{{DocumentCurrencyCode}}"></cbc:TaxAmount-->
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{TaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{TaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:TaxTotal>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for WithholdingTaxTotalID, WithholdingTaxTotal in CreditNoteLine.WithholdingTaxesTotal.items() %}
|
||||||
|
<cac:WithholdingTaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(WithholdingTaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
{% for Percent, TaxSubtotal in WithholdingTaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{WithholdingTaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{WithholdingTaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:WithholdingTaxTotal>
|
||||||
|
{% endfor %}
|
||||||
|
<!--TODO 2.0, puede iterar en el campo ID aumenta segun la cantidad de descuentos o cargos por lineas
|
||||||
|
ChargeIndicator true seria un cargo-->
|
||||||
|
<cac:AllowanceCharge>
|
||||||
|
<cbc:ID>1</cbc:ID>
|
||||||
|
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||||
|
<cbc:AllowanceChargeReason>Descuento</cbc:AllowanceChargeReason>
|
||||||
|
<cbc:MultiplierFactorNumeric>{{CreditNoteLine.MultiplierFactorNumeric}}</cbc:MultiplierFactorNumeric>
|
||||||
|
<cbc:Amount currencyID="{{DocumentCurrencyCode}}">{{CreditNoteLine.AllowanceChargeAmount}}</cbc:Amount>
|
||||||
|
<cbc:BaseAmount currencyID="{{DocumentCurrencyCode}}">{{CreditNoteLine.AllowanceChargeBaseAmount}}</cbc:BaseAmount>
|
||||||
|
</cac:AllowanceCharge>
|
||||||
|
<cac:Item>
|
||||||
|
<cbc:Description>{{CreditNoteLine.ItemDescription}}</cbc:Description>
|
||||||
|
<!--TODO 2.0, Cantidad de unidad de este artículo por empaque>
|
||||||
|
<cbc:PackSizeNumeric>{{CreditNoteLine.PackSizeNumeric}}</cbc:PackSizeNumeric-->
|
||||||
|
{% if CreditNoteLine.BrandName %}
|
||||||
|
<cbc:BrandName>{{CreditNoteLine.BrandName}}</cbc:BrandName>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0, Mandaremos la referencia del fabricante por ahora, hay que definir luego si esto cambia-->
|
||||||
|
{% if CreditNoteLine.ModelName %}
|
||||||
|
<cbc:ModelName>{{CreditNoteLine.ModelName}}</cbc:ModelName>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0, Grupo de datos de identificación del artículo o servicio de acuerdo con el vendedor>
|
||||||
|
<cac:SellersItemIdentification>
|
||||||
|
<cbc:ID>AOHV84-225</cbc:ID>
|
||||||
|
<cbc:ExtendedID>AOHV84-225</cbc:ExtendedID>
|
||||||
|
</cac:SellersItemIdentification-->
|
||||||
|
<!--TODO 2.0, Grupo de datos de identificación del artículo o servicio de acuerdo con un estándar-->
|
||||||
|
<!--OPCIONAL schemeName="Estándar de adopción del contribuyente" schemeAgencyID="" schemeAgencyName=""-->
|
||||||
|
<cac:StandardItemIdentification>
|
||||||
|
<cbc:ID schemeID="999">{{CreditNoteLine.StandardItemIdentification}}</cbc:ID>
|
||||||
|
</cac:StandardItemIdentification>
|
||||||
|
<!--TODO 2.0, opcional, Grupo de información para adicionar información específica del ítem que puede
|
||||||
|
ser solicitada por autoridades o entidades diferentes a la DIAN>
|
||||||
|
<cac:AdditionalItemProperty>
|
||||||
|
<cbc:Name></cbc:Name>
|
||||||
|
<cbc:Value></cbc:Value>
|
||||||
|
<cbc:ValueQuantity unitCode=""></cbc:ValueQuantity>
|
||||||
|
</cac:AdditionalItemProperty-->
|
||||||
|
{% if BillingReference.CustomizationID == '11' %}
|
||||||
|
<cac:InformationContentProviderParty>
|
||||||
|
<cac:PowerOfAttorney>
|
||||||
|
<cac:AgentParty>
|
||||||
|
<cac:PartyIdentification>
|
||||||
|
<cbc:ID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{CreditNoteLine.InformationContentProviderParty.IDschemeID}}"
|
||||||
|
schemeName="{{CreditNoteLine.InformationContentProviderParty.IDschemeName}}">{{CreditNoteLine.InformationContentProviderParty.ID}}</cbc:ID>
|
||||||
|
</cac:PartyIdentification>
|
||||||
|
</cac:AgentParty>
|
||||||
|
</cac:PowerOfAttorney>
|
||||||
|
</cac:InformationContentProviderParty>
|
||||||
|
{% endif %}
|
||||||
|
</cac:Item>
|
||||||
|
<cac:Price>
|
||||||
|
<cbc:PriceAmount currencyID="{{DocumentCurrencyCode}}">{{CreditNoteLine.PriceAmount}}</cbc:PriceAmount>
|
||||||
|
<cbc:BaseQuantity unitCode="{{CreditNoteLine.unitCode}}">{{CreditNoteLine.Quantity}}</cbc:BaseQuantity>
|
||||||
|
</cac:Price>
|
||||||
|
</cac:CreditNoteLine>
|
||||||
|
{% endfor %}
|
||||||
|
</CreditNote>
|
||||||
635
l10n_co_account_e_invoicing/templates/DebitNote.xml
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<DebitNote xmlns="urn:oasis:names:specification:ubl:schema:xsd:DebitNote-2"
|
||||||
|
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
|
||||||
|
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
|
||||||
|
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||||
|
xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
|
||||||
|
xmlns:sts="http://www.dian.gov.co/contratos/facturaelectronica/v1/Structures"
|
||||||
|
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
|
||||||
|
xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:DebitNote-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-DebitNote-2.1.xsd">
|
||||||
|
<ext:UBLExtensions>
|
||||||
|
<ext:UBLExtension>
|
||||||
|
<ext:ExtensionContent>
|
||||||
|
<sts:DianExtensions>
|
||||||
|
<sts:InvoiceSource>
|
||||||
|
<cbc:IdentificationCode listAgencyID="6"
|
||||||
|
listAgencyName="United Nations Economic Commission for Europe"
|
||||||
|
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1">CO</cbc:IdentificationCode>
|
||||||
|
</sts:InvoiceSource>
|
||||||
|
<sts:SoftwareProvider>
|
||||||
|
<sts:ProviderID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{ProviderIDschemeID}}"
|
||||||
|
schemeName="{{ProviderIDschemeName}}">{{ProviderID}}</sts:ProviderID>
|
||||||
|
<sts:SoftwareID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)">{{SoftwareID}}</sts:SoftwareID>
|
||||||
|
</sts:SoftwareProvider>
|
||||||
|
<sts:SoftwareSecurityCode schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)">{{SoftwareSecurityCode}}</sts:SoftwareSecurityCode>
|
||||||
|
<sts:AuthorizationProvider>
|
||||||
|
<sts:AuthorizationProviderID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="4"
|
||||||
|
schemeName="31">800197268</sts:AuthorizationProviderID>
|
||||||
|
</sts:AuthorizationProvider>
|
||||||
|
<sts:QRCode>NumFac: {{ID}}
|
||||||
|
FecFac: {{IssueDate}}
|
||||||
|
HorFac: {{IssueTime}}
|
||||||
|
NitFac: {{NitFac}}
|
||||||
|
DocAdq: {{DocAdq}}
|
||||||
|
ValFac: {{LineExtensionAmount}}
|
||||||
|
ValIva: {{ValIva}}
|
||||||
|
ValOtroIm: {{ValOtroIm}}
|
||||||
|
ValTolFac: {{PayableAmount}}
|
||||||
|
CUDE: {{UUID}}
|
||||||
|
{{QRCodeURL}}</sts:QRCode>
|
||||||
|
</sts:DianExtensions>
|
||||||
|
</ext:ExtensionContent>
|
||||||
|
</ext:UBLExtension>
|
||||||
|
<ext:UBLExtension>
|
||||||
|
<ext:ExtensionContent/>
|
||||||
|
</ext:UBLExtension>
|
||||||
|
</ext:UBLExtensions>
|
||||||
|
<cbc:UBLVersionID>UBL 2.1</cbc:UBLVersionID>
|
||||||
|
<cbc:CustomizationID>{{CustomizationID}}</cbc:CustomizationID>
|
||||||
|
<cbc:ProfileID>DIAN 2.1: Nota Débito de Factura Electrónica de Venta</cbc:ProfileID>
|
||||||
|
<cbc:ProfileExecutionID>{{ProfileExecutionID}}</cbc:ProfileExecutionID>
|
||||||
|
<cbc:ID>{{ID}}</cbc:ID>
|
||||||
|
<cbc:UUID schemeID="{{ProfileExecutionID}}" schemeName="CUDE-SHA384">{{UUID}}</cbc:UUID>
|
||||||
|
<cbc:IssueDate>{{IssueDate}}</cbc:IssueDate>
|
||||||
|
<cbc:IssueTime>{{IssueTime}}</cbc:IssueTime>
|
||||||
|
<!--TODO 1.0: Establecer opcional-->
|
||||||
|
<cbc:Note>{{Note}}</cbc:Note>
|
||||||
|
<cbc:DocumentCurrencyCode listAgencyID="6"
|
||||||
|
listAgencyName="United Nations Economic Commission for Europe"
|
||||||
|
listID="ISO 4217 Alpha">{{DocumentCurrencyCode}}</cbc:DocumentCurrencyCode>
|
||||||
|
<cbc:LineCountNumeric>{{LineCountNumeric}}</cbc:LineCountNumeric>
|
||||||
|
<!--TODO 3.0, Grupo de campos relativos al Periodo de Facturación: Intervalo de fechas la las que
|
||||||
|
referencia la factura por ejemplo en servicios públicos>
|
||||||
|
<cac:InvoicePeriod>
|
||||||
|
<cbc:StartDate>{{InvoicePeriodStartDate}}</cbc:StartDate>
|
||||||
|
<cbc:EndDate>{{InvoicePeriodEndDate}}</cbc:EndDate>
|
||||||
|
</cac:InvoicePeriod-->
|
||||||
|
<cac:DiscrepancyResponse>
|
||||||
|
<cbc:ReferenceID>{{DiscrepancyReferenceID}}</cbc:ReferenceID>
|
||||||
|
<cbc:ResponseCode>{{DiscrepancyResponseCode}}</cbc:ResponseCode>
|
||||||
|
<cbc:Description>{{DiscrepancyDescription}}</cbc:Description>
|
||||||
|
</cac:DiscrepancyResponse>
|
||||||
|
<!--TODO 1.0 TODAS LAS FACTURAS DEBEN SER DEL MISMO ADQUIRIENTE-->
|
||||||
|
<!--Si DebitNoteTypeCode igual a 30 es obligatorio-->
|
||||||
|
{% if BillingReference.ID %}
|
||||||
|
<cac:BillingReference>
|
||||||
|
<cac:InvoiceDocumentReference>
|
||||||
|
<cbc:ID>{{BillingReference.ID}}</cbc:ID>
|
||||||
|
<cbc:UUID schemeName="CUFE-SHA384">{{BillingReference.UUID}}</cbc:UUID>
|
||||||
|
<cbc:IssueDate>{{BillingReference.IssueDate}}</cbc:IssueDate>
|
||||||
|
</cac:InvoiceDocumentReference>
|
||||||
|
</cac:BillingReference>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: Si se habilita sale Error Regla ZB01>
|
||||||
|
<cac:OrderReference>
|
||||||
|
<cbc:ID>{{OrderReferenceID}}</cbc:ID>
|
||||||
|
<cbc:IssueDate>{{OrderReferenceIssueDate}}</cbc:IssueDate>
|
||||||
|
</cac:OrderReference-->
|
||||||
|
<!--TODO 3.0: opcional, solo interés mercantil, para referenciar uno o más documentos de despacho asociado>
|
||||||
|
<cac:DespatchDocumentReference>
|
||||||
|
<cbc:ID>8124167214 DA</cbc:ID>
|
||||||
|
<cbc:IssueDate>2019-12-12</cbc:IssueDate>
|
||||||
|
</cac:DespatchDocumentReference-->
|
||||||
|
<!--TODO 3.0: opcional, solo interés mercantil, para referenciar uno o más documentos de recepción asociado>
|
||||||
|
<cac:ReceiptDocumentReference>
|
||||||
|
<cbc:ID>12314129 GR</cbc:ID>
|
||||||
|
<cbc:IssueDate>2019-12-12</cbc:IssueDate>
|
||||||
|
</cac:ReceiptDocumentReference-->
|
||||||
|
<!--TODO 3.0: opcional, Referencia a documentos adicionales que hacen parte de la NC.
|
||||||
|
Especifación de este grupo igual a la del documento Invoice>
|
||||||
|
<cac:AdditionalDocumentReference>
|
||||||
|
<cbc:ID>12314129 GR</cbc:ID>
|
||||||
|
<cbc:IssueDate>2019-12-12</cbc:IssueDate>
|
||||||
|
<cbc:DocumentTypeCode>Punto 13.1.4. Referencia a otros documentos. (la tabla existe en el anexo 1.7 punto 6.1.4) del anexo tecnico version 1.8</cbc:DocumentTypeCode>
|
||||||
|
</cac:AdditionalDocumentReference-->
|
||||||
|
<cac:AccountingSupplierParty>
|
||||||
|
<cbc:AdditionalAccountID>{{AccountingSupplierParty.AdditionalAccountID}}</cbc:AdditionalAccountID>
|
||||||
|
<cac:Party>
|
||||||
|
{% if IndustryClassificationCode %}
|
||||||
|
<cbc:IndustryClassificationCode>{{IndustryClassificationCode}}</cbc:IndustryClassificationCode>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingSupplierParty.PartyName %}
|
||||||
|
<cac:PartyName>
|
||||||
|
<cbc:Name>{{AccountingSupplierParty.PartyName}}</cbc:Name>
|
||||||
|
</cac:PartyName>
|
||||||
|
{% endif %}
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>{{AccountingSupplierParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingSupplierParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingSupplierParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingSupplierParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingSupplierParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingSupplierParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingSupplierParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingSupplierParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingSupplierParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>{{AccountingSupplierParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{AccountingSupplierParty.CompanyIDschemeID}}"
|
||||||
|
schemeName="{{AccountingSupplierParty.CompanyIDschemeName}}">{{AccountingSupplierParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<!--TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49-->
|
||||||
|
<cbc:TaxLevelCode listName="{{AccountingSupplierParty.listName}}">{{AccountingSupplierParty.TaxLevelCode}}</cbc:TaxLevelCode>
|
||||||
|
<cac:RegistrationAddress>
|
||||||
|
<cbc:ID>{{AccountingSupplierParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingSupplierParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingSupplierParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingSupplierParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingSupplierParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingSupplierParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingSupplierParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingSupplierParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingCustomerParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:RegistrationAddress>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{AccountingSupplierParty.TaxSchemeID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{AccountingSupplierParty.TaxSchemeName}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
<cac:PartyLegalEntity>
|
||||||
|
<cbc:RegistrationName>{{AccountingSupplierParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{AccountingSupplierParty.CompanyIDschemeID}}"
|
||||||
|
schemeName="{{AccountingSupplierParty.CompanyIDschemeName}}">{{AccountingSupplierParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
{% if AccountingSupplierParty.CorporateRegistrationSchemeName %}
|
||||||
|
<cac:CorporateRegistrationScheme>
|
||||||
|
<cbc:Name>{{AccountingSupplierParty.CorporateRegistrationSchemeName}}</cbc:Name>
|
||||||
|
</cac:CorporateRegistrationScheme>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 3.0 Si se va a opera bajo modalidad de Consorcio, entonces este grupo de información debe ser informada.
|
||||||
|
De debe completar un grupo de elementos por cada participante del consorcio.>
|
||||||
|
<cac:ShareholderParty>
|
||||||
|
<cbc:PartecipationPercent>10.00</cbc:PartecipationPercent>
|
||||||
|
<cac:Party>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName></cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID=""
|
||||||
|
schemeName=""></cbc:CompanyID>
|
||||||
|
<TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49>
|
||||||
|
<cbc:TaxLevelCode listName=""></cbc:TaxLevelCode>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID></cbc:ID>
|
||||||
|
<cbc:Name></cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
</cac:Party>
|
||||||
|
</cac:ShareholderParty-->
|
||||||
|
</cac:PartyLegalEntity>
|
||||||
|
{% if AccountingSupplierParty.Telephone or AccountingSupplierParty.Telefax or AccountingSupplierParty.ElectronicMail %}
|
||||||
|
<cac:Contact>
|
||||||
|
<!--TODO 3.0: opcional, de momento se manda info de la empresa sin nombre de contacto>
|
||||||
|
<cbc:Name>Diana Cruz</cbc:Name-->
|
||||||
|
{% if AccountingSupplierParty.Telephone %}
|
||||||
|
<cbc:Telephone>{{AccountingSupplierParty.Telephone}}</cbc:Telephone>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingSupplierParty.Telefax %}
|
||||||
|
<cbc:Telefax>{{AccountingSupplierParty.Telefax}}</cbc:Telefax>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingSupplierParty.ElectronicMail %}
|
||||||
|
<cbc:ElectronicMail>{{AccountingSupplierParty.ElectronicMail}}</cbc:ElectronicMail>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: opcional, de momento se manda info de la empresa no se sabe que mandar aca de momento>
|
||||||
|
<cbc:Note></cbc:Note-->
|
||||||
|
</cac:Contact>
|
||||||
|
{% endif %}
|
||||||
|
</cac:Party>
|
||||||
|
</cac:AccountingSupplierParty>
|
||||||
|
<cac:AccountingCustomerParty>
|
||||||
|
<cbc:AdditionalAccountID>{{AccountingCustomerParty.AdditionalAccountID}}</cbc:AdditionalAccountID>
|
||||||
|
<cac:Party>
|
||||||
|
<cac:PartyIdentification>
|
||||||
|
<cbc:ID {% if AccountingCustomerParty.CompanyIDschemeName == '31' %}schemeID="{{AccountingCustomerParty.CompanyIDschemeID}}"{% endif %}
|
||||||
|
schemeName="{{AccountingCustomerParty.CompanyIDschemeName}}">{{AccountingCustomerParty.CompanyID}}</cbc:ID>
|
||||||
|
</cac:PartyIdentification>
|
||||||
|
{% if AccountingCustomerParty.PartyName%}
|
||||||
|
<cac:PartyName>
|
||||||
|
<cbc:Name>{{AccountingCustomerParty.PartyName}}</cbc:Name>
|
||||||
|
</cac:PartyName>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>{{AccountingCustomerParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingCustomerParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingCustomerParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingCustomerParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingCustomerParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingCustomerParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingCustomerParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingCustomerParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingCustomerParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
{% endif %}
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>{{AccountingCustomerParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
{% if AccountingCustomerParty.CompanyIDschemeName == '31' %}schemeID="{{AccountingCustomerParty.CompanyIDschemeID}}"{% endif %}
|
||||||
|
schemeName="{{AccountingCustomerParty.CompanyIDschemeName}}">{{AccountingCustomerParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<!--TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49-->
|
||||||
|
<cbc:TaxLevelCode listName="{{AccountingCustomerParty.listName}}">{{AccountingCustomerParty.TaxLevelCode}}</cbc:TaxLevelCode>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
<cac:RegistrationAddress>
|
||||||
|
<cbc:ID>{{AccountingCustomerParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingCustomerParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingCustomerParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingCustomerParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingCustomerParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingCustomerParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingCustomerParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingCustomerParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingCustomerParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:RegistrationAddress>
|
||||||
|
{% endif %}
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{AccountingCustomerParty.TaxSchemeID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{AccountingCustomerParty.TaxSchemeName}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
<cac:PartyLegalEntity>
|
||||||
|
<cbc:RegistrationName>{{AccountingCustomerParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
{% if AccountingCustomerParty.CompanyIDschemeName == '31' %}schemeID="{{AccountingCustomerParty.CompanyIDschemeID}}"{% endif %}
|
||||||
|
schemeName="{{AccountingCustomerParty.CompanyIDschemeName}}">{{AccountingCustomerParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
{% if AccountingCustomerParty.CorporateRegistrationSchemeName %}
|
||||||
|
<cac:CorporateRegistrationScheme>
|
||||||
|
<cbc:Name>{{AccountingCustomerParty.CorporateRegistrationSchemeName}}</cbc:Name>
|
||||||
|
</cac:CorporateRegistrationScheme>
|
||||||
|
{% endif %}
|
||||||
|
</cac:PartyLegalEntity>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' and (AccountingCustomerParty.Telephone or AccountingCustomerParty.Telefax or AccountingCustomerParty.ElectronicMail) %}
|
||||||
|
<cac:Contact>
|
||||||
|
<!--TODO 3.0: opcional, de momento se manda info de la empresa sin nombre de contacto>
|
||||||
|
<cbc:Name>Diana Cruz</cbc:Name-->
|
||||||
|
{% if AccountingCustomerParty.Telephone %}
|
||||||
|
<cbc:Telephone>{{AccountingCustomerParty.Telephone}}</cbc:Telephone>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.Telefax %}
|
||||||
|
<cbc:Telefax>{{AccountingCustomerParty.Telefax}}</cbc:Telefax>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.ElectronicMail %}
|
||||||
|
<cbc:ElectronicMail>{{AccountingCustomerParty.ElectronicMail}}</cbc:ElectronicMail>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: opcional, de momento se manda info de la empresa no se sabe que mandar aca de momento>
|
||||||
|
<cbc:Note></cbc:Note-->
|
||||||
|
</cac:Contact>
|
||||||
|
{% endif %}
|
||||||
|
</cac:Party>
|
||||||
|
</cac:AccountingCustomerParty>
|
||||||
|
<!--TODO 2.0 Grupo de información de la Persona autorizada para descargar documentos-->
|
||||||
|
<!--cac:TaxRepresentativeParty>
|
||||||
|
<cac:PartyIdentification>
|
||||||
|
<cbc:ID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID=""
|
||||||
|
schemeName=""></cbc:ID>
|
||||||
|
</cac:PartyIdentification>
|
||||||
|
</cac:TaxRepresentativeParty-->
|
||||||
|
<!--TODO 3.0: Grupo de campos para información relacionadas con un cargo o un descuento>
|
||||||
|
<cac:AllowanceCharge>
|
||||||
|
<cbc:ID>SFR3123856</cbc:ID>
|
||||||
|
<cbc:ChargeIndicator></cbc:ChargeIndicator>
|
||||||
|
<cbc:AllowanceChargeReasonCode></cbc:AllowanceChargeReasonCode>
|
||||||
|
<cbc:AllowanceChargeReason></cbc:AllowanceChargeReason>
|
||||||
|
<cbc:MultiplierFactorNumeric>Prepago recibido</cbc:MultiplierFactorNumeric>
|
||||||
|
<cbc:Amount currencyID="{{DocumentCurrencyCode}}">1000.00</cbc:Amount>
|
||||||
|
<cbc:BaseAmount currencyID="{{DocumentCurrencyCode}}">1000.00</cbc:BaseAmount>
|
||||||
|
</cac:AllowanceCharge-->
|
||||||
|
<cac:Delivery>
|
||||||
|
<cbc:ActualDeliveryDate>{{ActualDeliveryDate}}</cbc:ActualDeliveryDate>
|
||||||
|
<cbc:ActualDeliveryTime>{{ActualDeliveryTime}}</cbc:ActualDeliveryTime>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
<cac:DeliveryAddress>
|
||||||
|
<cbc:ID>{{Delivery.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{Delivery.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if Delivery.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{Delivery.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{Delivery.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{Delivery.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{Delivery.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{Delivery.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{Delivery.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:DeliveryAddress>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 3.0, Grupo de datos con información sobre la empresa de transporte Desarrollo futuro>
|
||||||
|
<cac:DeliveryParty>
|
||||||
|
<cac:PartyName>
|
||||||
|
<cbc:Name></cbc:Name>
|
||||||
|
</cac:PartyName>
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>11001</cbc:ID>
|
||||||
|
<cbc:CityName>Bogotá, D.c. </cbc:CityName>
|
||||||
|
<cbc:PostalZone>Bogotá, D.c. </cbc:PostalZone>
|
||||||
|
<cbc:CountrySubentity>Bogotá</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>11</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>Av. #17 - 193</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>CO</cbc:IdentificationCode>
|
||||||
|
<TODO 2.0: languageID podria variar, siguiente version revisar esto>
|
||||||
|
<cbc:Name languageID="es">Colombia</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>Empresa de transporte</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="1"
|
||||||
|
schemeName="31">981223983</cbc:CompanyID>
|
||||||
|
<TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49>
|
||||||
|
<cbc:TaxLevelCode listName="">O-99</cbc:TaxLevelCode>
|
||||||
|
<cac:RegistrationAddress>
|
||||||
|
<cbc:ID>11001</cbc:ID>
|
||||||
|
<cbc:CityName>Bogotá, D.c. </cbc:CityName>
|
||||||
|
<cbc:PostalZone>Bogotá, D.c. </cbc:PostalZone>
|
||||||
|
<cbc:CountrySubentity>Bogotá</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>11</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>Av. #17 - 193</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>CO</cbc:IdentificationCode>
|
||||||
|
<TODO 2.0: languageID podria variar, siguiente version revisar esto>
|
||||||
|
<cbc:Name languageID="es">Colombia</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:RegistrationAddress>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>01</cbc:ID>
|
||||||
|
<cbc:Name>IVA</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
<cac:PartyLegalEntity>
|
||||||
|
<cbc:RegistrationName>Empresa de transporte</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="1"
|
||||||
|
schemeName="31">981223983</cbc:CompanyID>
|
||||||
|
<cac:CorporateRegistrationScheme>
|
||||||
|
<cbc:Name>75433</cbc:Name>
|
||||||
|
</cac:CorporateRegistrationScheme>
|
||||||
|
</cac:PartyLegalEntity>
|
||||||
|
<cac:Contact>
|
||||||
|
<cbc:Name>Eric Van Boxsom</cbc:Name>
|
||||||
|
<cbc:Telephone>9712311</cbc:Telephone>
|
||||||
|
<cbc:Telefax>12431241</cbc:Telefax>
|
||||||
|
<cbc:ElectronicMail>eric.vanboxsom@gosocket.net</cbc:ElectronicMail>
|
||||||
|
<cbc:Note>Test descripcion contacto</cbc:Note>
|
||||||
|
</cac:Contact>
|
||||||
|
</cac:DeliveryParty-->
|
||||||
|
</cac:Delivery>
|
||||||
|
{% if DeliveryTerms.LossRiskResponsibilityCode %}
|
||||||
|
<cac:DeliveryTerms>
|
||||||
|
<cbc:ID>1</cbc:ID>
|
||||||
|
<!--TODO 2.0: Con otro modulo complementario se puede resolver el texto libre-->
|
||||||
|
<!--cbc:SpecialTerms>Portes Pagados</cbc:SpecialTerms-->
|
||||||
|
<cbc:LossRiskResponsibilityCode>{{DeliveryTerms.LossRiskResponsibilityCode}}</cbc:LossRiskResponsibilityCode>
|
||||||
|
<cbc:LossRisk>{{DeliveryTerms.LossRisk}}</cbc:LossRisk>
|
||||||
|
</cac:DeliveryTerms>
|
||||||
|
{% endif %}
|
||||||
|
<cac:PaymentMeans>
|
||||||
|
<cbc:ID>{{PaymentMeansID}}</cbc:ID>
|
||||||
|
<cbc:PaymentMeansCode>{{PaymentMeansCode}}</cbc:PaymentMeansCode>
|
||||||
|
<cbc:PaymentDueDate>{{PaymentDueDate}}</cbc:PaymentDueDate>
|
||||||
|
<!--TODO 2.0: Identificador del pago, pueden ir de cero a varios PaymentID-->
|
||||||
|
<!--cbc:PaymentID></cbc:PaymentID-->
|
||||||
|
</cac:PaymentMeans>
|
||||||
|
{% if DocumentCurrencyCode != 'COP' %}
|
||||||
|
<cac:PaymentExchangeRate>
|
||||||
|
<cbc:SourceCurrencyCode>{{PaymentExchangeRate.SourceCurrencyCode}}</cbc:SourceCurrencyCode>
|
||||||
|
<cbc:SourceCurrencyBaseRate>1.00</cbc:SourceCurrencyBaseRate>
|
||||||
|
<cbc:TargetCurrencyCode>{{PaymentExchangeRate.TargetCurrencyCode}}</cbc:TargetCurrencyCode>
|
||||||
|
<cbc:TargetCurrencyBaseRate>1.00</cbc:TargetCurrencyBaseRate>
|
||||||
|
<cbc:CalculationRate>{{'{:.2f}'.format(PaymentExchangeRate.CalculationRate)}}</cbc:CalculationRate>
|
||||||
|
<cbc:Date>{{PaymentExchangeRate.Date}}</cbc:Date>
|
||||||
|
</cac:PaymentExchangeRate>
|
||||||
|
{% endif %}
|
||||||
|
{% for TaxTotalID, TaxTotal in TaxesTotal.items() %}
|
||||||
|
{% if TaxTotal.total != 0 %}
|
||||||
|
<cac:TaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 2.0: Se manda 0.00 por ahora mientras se evalua la necesidad de calculos-->
|
||||||
|
<cbc:RoundingAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:RoundingAmount>
|
||||||
|
{% for Percent, TaxSubtotal in TaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 1.0: Usado en el caso de que el tributo es un valor fijo>
|
||||||
|
<cbc:BaseUnitMeasure></cbc:BaseUnitMeasure>
|
||||||
|
<cbc:unitCode></cbc:unitCode>
|
||||||
|
<cbc:PerUnitAmount currencyID="{{DocumentCurrencyCode}}"></cbc:TaxAmount-->
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{TaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{TaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:TaxTotal>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for WithholdingTaxTotalID, WithholdingTaxTotal in WithholdingTaxesTotal.items() %}
|
||||||
|
<cac:WithholdingTaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(WithholdingTaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
{% for Percent, TaxSubtotal in WithholdingTaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{WithholdingTaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{WithholdingTaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:WithholdingTaxTotal>
|
||||||
|
{% endfor %}
|
||||||
|
<cac:RequestedMonetaryTotal>
|
||||||
|
<cbc:LineExtensionAmount currencyID="{{DocumentCurrencyCode}}">{{LineExtensionAmount}}</cbc:LineExtensionAmount>
|
||||||
|
<cbc:TaxExclusiveAmount currencyID="{{DocumentCurrencyCode}}">{{TaxExclusiveAmount}}</cbc:TaxExclusiveAmount>
|
||||||
|
<cbc:TaxInclusiveAmount currencyID="{{DocumentCurrencyCode}}">{{TaxInclusiveAmount}}</cbc:TaxInclusiveAmount>
|
||||||
|
<!--TODO 3.0: los prepagos son los anticipos, no hay soporte aun en el odoo para identificar esto
|
||||||
|
PrepaidAmount == 0 mientras tanto, PrepaidAmount = suma de PrepaidPayment
|
||||||
|
AllowanceTotalAmount == 0 y ChargeTotalAmount == 0 mientras tanto, suma de AllowanceCharge
|
||||||
|
TaxInclusiveAmount == PayableAmount mientras tanto-->
|
||||||
|
<cbc:AllowanceTotalAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:AllowanceTotalAmount>
|
||||||
|
<cbc:ChargeTotalAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:ChargeTotalAmount>
|
||||||
|
<cbc:PrepaidAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:PrepaidAmount>
|
||||||
|
<cbc:PayableAmount currencyID="{{DocumentCurrencyCode}}">{{PayableAmount}}</cbc:PayableAmount>
|
||||||
|
</cac:RequestedMonetaryTotal>
|
||||||
|
{% for DebitNoteLineID, DebitNoteLine in DebitNoteLines.items() %}
|
||||||
|
<cac:DebitNoteLine>
|
||||||
|
<cbc:ID>{{DebitNoteLineID}}</cbc:ID>
|
||||||
|
{% if BillingReference.CustomizationID == '09' %}
|
||||||
|
<cbc:Note>Contrato de servicios AIU por concepto de: {{DebitNoteLine.ItemDescription}}</cbc:Note>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:DebitedQuantity unitCode="{{DebitNoteLine.unitCode}}">{{DebitNoteLine.Quantity}}</cbc:DebitedQuantity>
|
||||||
|
<cbc:LineExtensionAmount currencyID="{{DocumentCurrencyCode}}">{{DebitNoteLine.LineExtensionAmount}}</cbc:LineExtensionAmount>
|
||||||
|
{% if DebitNoteLine.LineExtensionAmount == '0.00' %}
|
||||||
|
<cac:PricingReference>
|
||||||
|
<cac:AlternativeConditionPrice>
|
||||||
|
<cbc:PriceAmount currencyID="{{DocumentCurrencyCode}}">{{DebitNoteLine.PricingReferencePriceAmount}}</cbc:PriceAmount>
|
||||||
|
<cbc:PriceTypeCode>01</cbc:PriceTypeCode>
|
||||||
|
</cac:AlternativeConditionPrice>
|
||||||
|
</cac:PricingReference>
|
||||||
|
{% endif %}
|
||||||
|
{% for TaxTotalID, TaxTotal in DebitNoteLine.TaxesTotal.items() %}
|
||||||
|
{% if TaxTotal.total != 0 %}
|
||||||
|
<cac:TaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 2.0: Se manda 0.00 por ahora mientras se evalua la necesidad de calculos-->
|
||||||
|
<cbc:RoundingAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:RoundingAmount>
|
||||||
|
{% for Percent, TaxSubtotal in TaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 1.0: Usado en el caso de que el tributo es un valor fijo>
|
||||||
|
<cbc:BaseUnitMeasure></cbc:BaseUnitMeasure>
|
||||||
|
<cbc:unitCode></cbc:unitCode>
|
||||||
|
<cbc:PerUnitAmount currencyID="{{DocumentCurrencyCode}}"></cbc:TaxAmount-->
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{TaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{TaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:TaxTotal>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for WithholdingTaxTotalID, WithholdingTaxTotal in DebitNoteLine.WithholdingTaxesTotal.items() %}
|
||||||
|
<cac:WithholdingTaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(WithholdingTaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
{% for Percent, TaxSubtotal in WithholdingTaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{WithholdingTaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{WithholdingTaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:WithholdingTaxTotal>
|
||||||
|
{% endfor %}
|
||||||
|
<!--TODO 2.0, puede iterar en el campo ID aumenta segun la cantidad de descuentos o cargos por lineas
|
||||||
|
ChargeIndicator true seria un cargo-->
|
||||||
|
<cac:AllowanceCharge>
|
||||||
|
<cbc:ID>1</cbc:ID>
|
||||||
|
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||||
|
<cbc:AllowanceChargeReason>Descuento</cbc:AllowanceChargeReason>
|
||||||
|
<cbc:MultiplierFactorNumeric>{{DebitNoteLine.MultiplierFactorNumeric}}</cbc:MultiplierFactorNumeric>
|
||||||
|
<cbc:Amount currencyID="{{DocumentCurrencyCode}}">{{DebitNoteLine.AllowanceChargeAmount}}</cbc:Amount>
|
||||||
|
<cbc:BaseAmount currencyID="{{DocumentCurrencyCode}}">{{DebitNoteLine.AllowanceChargeBaseAmount}}</cbc:BaseAmount>
|
||||||
|
</cac:AllowanceCharge>
|
||||||
|
<cac:Item>
|
||||||
|
<cbc:Description>{{DebitNoteLine.ItemDescription}}</cbc:Description>
|
||||||
|
<!--TODO 2.0, Cantidad de unidad de este artículo por empaque>
|
||||||
|
<cbc:PackSizeNumeric>{{DebitNoteLine.PackSizeNumeric}}</cbc:PackSizeNumeric-->
|
||||||
|
{% if DebitNoteLine.BrandName %}
|
||||||
|
<cbc:BrandName>{{DebitNoteLine.BrandName}}</cbc:BrandName>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0, Mandaremos la referencia del fabricante por ahora, hay que definir luego si esto cambia-->
|
||||||
|
{% if DebitNoteLine.ModelName %}
|
||||||
|
<cbc:ModelName>{{DebitNoteLine.ModelName}}</cbc:ModelName>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0, Grupo de datos de identificación del artículo o servicio de acuerdo con el vendedor>
|
||||||
|
<cac:SellersItemIdentification>
|
||||||
|
<cbc:ID>AOHV84-225</cbc:ID>
|
||||||
|
<cbc:ExtendedID>AOHV84-225</cbc:ExtendedID>
|
||||||
|
</cac:SellersItemIdentification-->
|
||||||
|
<!--TODO 2.0, Grupo de datos de identificación del artículo o servicio de acuerdo con un estándar-->
|
||||||
|
<!--OPCIONAL schemeName="Estándar de adopción del contribuyente" schemeAgencyID="" schemeAgencyName=""-->
|
||||||
|
<cac:StandardItemIdentification>
|
||||||
|
<cbc:ID schemeID="999">{{DebitNoteLine.StandardItemIdentification}}</cbc:ID>
|
||||||
|
</cac:StandardItemIdentification>
|
||||||
|
<!--TODO 2.0, opcional, Grupo de información para adicionar información específica del ítem que puede
|
||||||
|
ser solicitada por autoridades o entidades diferentes a la DIAN>
|
||||||
|
<cac:AdditionalItemProperty>
|
||||||
|
<cbc:Name></cbc:Name>
|
||||||
|
<cbc:Value></cbc:Value>
|
||||||
|
<cbc:ValueQuantity unitCode=""></cbc:ValueQuantity>
|
||||||
|
</cac:AdditionalItemProperty-->
|
||||||
|
{% if BillingReference.CustomizationID == '11' %}
|
||||||
|
<cac:InformationContentProviderParty>
|
||||||
|
<cac:PowerOfAttorney>
|
||||||
|
<cac:AgentParty>
|
||||||
|
<cac:PartyIdentification>
|
||||||
|
<cbc:ID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{DebitNoteLine.InformationContentProviderParty.IDschemeID}}"
|
||||||
|
schemeName="{{DebitNoteLine.InformationContentProviderParty.IDschemeName}}">{{DebitNoteLine.InformationContentProviderParty.ID}}</cbc:ID>
|
||||||
|
</cac:PartyIdentification>
|
||||||
|
</cac:AgentParty>
|
||||||
|
</cac:PowerOfAttorney>
|
||||||
|
</cac:InformationContentProviderParty>
|
||||||
|
{% endif %}
|
||||||
|
</cac:Item>
|
||||||
|
<cac:Price>
|
||||||
|
<cbc:PriceAmount currencyID="{{DocumentCurrencyCode}}">{{DebitNoteLine.PriceAmount}}</cbc:PriceAmount>
|
||||||
|
<cbc:BaseQuantity unitCode="{{DebitNoteLine.unitCode}}">{{DebitNoteLine.Quantity}}</cbc:BaseQuantity>
|
||||||
|
</cac:Price>
|
||||||
|
</cac:DebitNoteLine>
|
||||||
|
{% endfor %}
|
||||||
|
</DebitNote>
|
||||||
574
l10n_co_account_e_invoicing/templates/DocumentoSoporte.xml
Normal file
@ -0,0 +1,574 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
|
||||||
|
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
|
||||||
|
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
|
||||||
|
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||||
|
xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
|
||||||
|
xmlns:sts="dian:gov:co:facturaelectronica:Structures-2-1"
|
||||||
|
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
|
||||||
|
xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd">
|
||||||
|
<ext:UBLExtensions>
|
||||||
|
<ext:UBLExtension>
|
||||||
|
<ext:ExtensionContent>
|
||||||
|
<sts:DianExtensions>
|
||||||
|
<sts:InvoiceControl>
|
||||||
|
<sts:InvoiceAuthorization>{{InvoiceControl.InvoiceAuthorization}}</sts:InvoiceAuthorization>
|
||||||
|
<sts:AuthorizationPeriod>
|
||||||
|
<cbc:StartDate>{{InvoiceControl.StartDate}}</cbc:StartDate>
|
||||||
|
<cbc:EndDate>{{InvoiceControl.EndDate}}</cbc:EndDate>
|
||||||
|
</sts:AuthorizationPeriod>
|
||||||
|
<sts:AuthorizedInvoices>
|
||||||
|
{% if InvoiceControl.Prefix %}
|
||||||
|
<sts:Prefix>{{InvoiceControl.Prefix}}</sts:Prefix>
|
||||||
|
{% endif %}
|
||||||
|
<sts:From>{{InvoiceControl.From}}</sts:From>
|
||||||
|
<sts:To>{{InvoiceControl.To}}</sts:To>
|
||||||
|
</sts:AuthorizedInvoices>
|
||||||
|
</sts:InvoiceControl>
|
||||||
|
<sts:InvoiceSource>
|
||||||
|
<cbc:IdentificationCode listAgencyID="6"
|
||||||
|
listAgencyName="United Nations Economic Commission for Europe"
|
||||||
|
listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1">CO</cbc:IdentificationCode>
|
||||||
|
</sts:InvoiceSource>
|
||||||
|
<sts:SoftwareProvider>
|
||||||
|
<sts:ProviderID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{ProviderIDschemeID}}"
|
||||||
|
schemeName="{{ProviderIDschemeName}}">{{ProviderID}}</sts:ProviderID>
|
||||||
|
<sts:SoftwareID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)">{{SoftwareID}}</sts:SoftwareID>
|
||||||
|
</sts:SoftwareProvider>
|
||||||
|
<sts:SoftwareSecurityCode schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)">{{SoftwareSecurityCode}}</sts:SoftwareSecurityCode>
|
||||||
|
<sts:AuthorizationProvider>
|
||||||
|
<sts:AuthorizationProviderID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="4"
|
||||||
|
schemeName="31">800197268</sts:AuthorizationProviderID>
|
||||||
|
</sts:AuthorizationProvider>
|
||||||
|
<sts:QRCode>NumFac: {{ID}}
|
||||||
|
FecFac: {{IssueDate}}
|
||||||
|
HorFac: {{IssueTime}}
|
||||||
|
NitFac: {{NitFac}}
|
||||||
|
DocAdq: {{DocAdq}}
|
||||||
|
ValFac: {{LineExtensionAmount}}
|
||||||
|
ValIva: {{ValIva}}
|
||||||
|
ValOtroIm: {{ValOtroIm}}
|
||||||
|
ValTolFac: {{PayableAmount}}
|
||||||
|
CUDE: {{UUID}}
|
||||||
|
{{QRCodeURL}}</sts:QRCode>
|
||||||
|
</sts:DianExtensions>
|
||||||
|
</ext:ExtensionContent>
|
||||||
|
</ext:UBLExtension>
|
||||||
|
<ext:UBLExtension>
|
||||||
|
<ext:ExtensionContent/>
|
||||||
|
</ext:UBLExtension>
|
||||||
|
</ext:UBLExtensions>
|
||||||
|
<cbc:UBLVersionID>UBL 2.1</cbc:UBLVersionID>
|
||||||
|
<cbc:CustomizationID>{{CustomizationID}}</cbc:CustomizationID>
|
||||||
|
<cbc:ProfileID>DIAN 2.1: documento soporte en adquisiciones efectuadas a no obligados a facturar.</cbc:ProfileID>
|
||||||
|
<cbc:ProfileExecutionID>{{ProfileExecutionID}}</cbc:ProfileExecutionID>
|
||||||
|
<cbc:ID>{{ID}}</cbc:ID>
|
||||||
|
<cbc:UUID schemeID="{{ProfileExecutionID}}" schemeName="CUDE-SHA384">{{UUID}}</cbc:UUID>
|
||||||
|
<cbc:IssueDate>{{IssueDate}}</cbc:IssueDate>
|
||||||
|
<cbc:IssueTime>{{IssueTime}}</cbc:IssueTime>
|
||||||
|
<cbc:DueDate>{{DueDate}}</cbc:DueDate>
|
||||||
|
<cbc:InvoiceTypeCode>{{InvoiceTypeCode}}</cbc:InvoiceTypeCode>
|
||||||
|
<!--TODO 1.0: Que nota colocar?-->
|
||||||
|
<cbc:Note>{{Note}}</cbc:Note>
|
||||||
|
<cbc:DocumentCurrencyCode listAgencyID="6"
|
||||||
|
listAgencyName="United Nations Economic Commission for Europe"
|
||||||
|
listID="ISO 4217 Alpha">{{DocumentCurrencyCode}}</cbc:DocumentCurrencyCode>
|
||||||
|
<cbc:LineCountNumeric>{{LineCountNumeric}}</cbc:LineCountNumeric>
|
||||||
|
<!--TODO 3.0: Grupo de campos relativos al Periodo de Facturación: Intervalo de fechas la las que
|
||||||
|
referencia la factura por ejemplo en servicios públicos>
|
||||||
|
<cac:InvoicePeriod>
|
||||||
|
<cbc:StartDate>{{InvoicePeriodStartDate}}</cbc:StartDate>
|
||||||
|
<cbc:EndDate>{{InvoicePeriodEndDate}}</cbc:EndDate>
|
||||||
|
</cac:InvoicePeriod-->
|
||||||
|
{% if OrderReferenceID %}
|
||||||
|
<cac:OrderReference>
|
||||||
|
<cbc:ID>{{OrderReferenceID}}</cbc:ID>
|
||||||
|
<!--TODO 2.0: No hay un campo validado aun, para la fecha de la orden del cliente>
|
||||||
|
<cbc:IssueDate>{{OrderReferenceIssueDate}}</cbc:IssueDate-->
|
||||||
|
</cac:OrderReference>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: Se debe diligenciar únicamente cuando la FE se origina a partir de la corrección o
|
||||||
|
ajuste que se da mediante un Nota Crédito, ES POSIBLE PERO NO HAY PROBLEMA SI NO ESTA,
|
||||||
|
odoo esto es poco probable y no esta la relacion directa, desarrollo para cumplir con esto>
|
||||||
|
<cac:BillingReference>
|
||||||
|
<cac:CreditNoteDocumentReference>
|
||||||
|
<cbc:ID>SFR3123856</cbc:ID>
|
||||||
|
<cbc:UUID schemeName="CUFE-SHA1">a675432fecc1d537361dcdbdfbd08d6e5283f2bc</cbc:UUID>
|
||||||
|
<cbc:IssueDate>2018-09-29</cbc:IssueDate>
|
||||||
|
</cac:InvoiceDocumentReference>
|
||||||
|
</cac:BillingReference-->
|
||||||
|
<!--TODO 2.0: Se debe diligenciar únicamente cuando la FE se origina a partir de la corrección o
|
||||||
|
ajuste que se da mediante un Nota Débito, CREO QUE ESTO ES IMPOSIBLE DESDE EL ODOO, Y TAMPOCO TIENE SENTIDO
|
||||||
|
GENERAR UNA NUEVA FACTURA, SABIENDO QUE LA NOTA DEBITO PRECISAMENTE ES LA CORRECCION PARA NO GENERAR UNA NOTA
|
||||||
|
CREDITO Y UNA FACTURA DE NUEVO EN EL CASO DE QUE LA FACTURA ORIGINAL ESTE ERRADA POR FALTA DE UN ITEM O VALOR>
|
||||||
|
<cac:BillingReference>
|
||||||
|
<cac:DebitNoteDocumentReference>
|
||||||
|
<cbc:ID>SETP990000101</cbc:ID>
|
||||||
|
<cbc:UUID schemeName="CUFE-SHA384">1dc661228f152332d876e1f1cd2042ecdea1804ed0da78f84dc9ee0938d69f17037dc53f97778ed2721d65c1fc3c73ac</cbc:UUID>
|
||||||
|
<cbc:IssueDate>2018-09-29</cbc:IssueDate>
|
||||||
|
</cac:InvoiceDocumentReference>
|
||||||
|
</cac:BillingReference-->
|
||||||
|
<!--TODO 3.0: opcional, solo interés mercantil, para referenciar uno o más documentos de despacho asociado>
|
||||||
|
<cac:DespatchDocumentReference>
|
||||||
|
<cbc:ID>8124167214 DA</cbc:ID>
|
||||||
|
<cbc:IssueDate>2019-12-12</cbc:IssueDate>
|
||||||
|
</cac:DespatchDocumentReference-->
|
||||||
|
<!--TODO 3.0: opcional, solo interés mercantil, para referenciar uno o más documentos de recepción asociado-->
|
||||||
|
{% if ReceiptDocumentReferenceID %}
|
||||||
|
<cac:ReceiptDocumentReference>
|
||||||
|
<cbc:ID>{{ReceiptDocumentReferenceID}}</cbc:ID>
|
||||||
|
<!--TODO 2.0: No hay un campo validado aun, para la fecha de la orden del cliente>
|
||||||
|
<cbc:IssueDate>{{ReceiptDocumentReferenceDate}}</cbc:IssueDate-->
|
||||||
|
</cac:ReceiptDocumentReference>
|
||||||
|
{% endif %}
|
||||||
|
{% if InvoiceTypeCode == '03' %}
|
||||||
|
<cac:AdditionalDocumentReference>
|
||||||
|
<cbc:ID>{{ID}}</cbc:ID>
|
||||||
|
<cbc:IssueDate>{{IssueDate}}</cbc:IssueDate>
|
||||||
|
<!--TODO 2.0: Opcional, Corresponde a una codificación propia de la empresa.
|
||||||
|
<cbc:DocumentTypeCode></cbc:DocumentTypeCode-->
|
||||||
|
</cac:AdditionalDocumentReference>
|
||||||
|
{% endif %}
|
||||||
|
<cac:AccountingSupplierParty>
|
||||||
|
<cbc:AdditionalAccountID schemeID="01">{{AccountingSupplierParty.AdditionalAccountID}}</cbc:AdditionalAccountID>
|
||||||
|
<cac:Party>
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>{{AccountingSupplierParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingSupplierParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingSupplierParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingSupplierParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingSupplierParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingSupplierParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingSupplierParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingSupplierParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingSupplierParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>{{AccountingSupplierParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="{{AccountingSupplierParty.CompanyIDschemeID}}"
|
||||||
|
schemeName="{{AccountingSupplierParty.CompanyIDschemeName}}">{{AccountingSupplierParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<!--TODO 2.0: listName el anexo dice que eliminar o valor No aplica, pero para consumidor final dice informar 49-->
|
||||||
|
<cbc:TaxLevelCode listName="{{AccountingSupplierParty.listName}}">{{AccountingSupplierParty.TaxLevelCode}}</cbc:TaxLevelCode>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{AccountingSupplierParty.TaxSchemeID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{AccountingSupplierParty.TaxSchemeName}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
<!--TODO 2.0: opciona>
|
||||||
|
<cac:PartyLegalEntity>
|
||||||
|
<cbc:RegistrationName>{{AccountingSupplierParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195" schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)" schemeID="{{AccountingSupplierParty.CompanyIDschemeID}}" schemeName="{{AccountingSupplierParty.CompanyIDschemeName}}">{{AccountingSupplierParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<TODO 3.0: Si se va a opera bajo modalidad de Consorcio, entonces este grupo de información
|
||||||
|
debe ser informada. Se debe completar un grupo de elementos por cada participante del consorcio.>
|
||||||
|
<cac:ShareholderParty>
|
||||||
|
<cbc:PartecipationPercent>10.00</cbc:PartecipationPercent>
|
||||||
|
<cac:Party>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName></cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195" schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)" schemeID="" schemeName=""></cbc:CompanyID>
|
||||||
|
<TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49>
|
||||||
|
<cbc:TaxLevelCode listName=""></cbc:TaxLevelCode>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID></cbc:ID>
|
||||||
|
<cbc:Name></cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
</cac:Party>
|
||||||
|
</cac:ShareholderParty>
|
||||||
|
</cac:PartyLegalEntity-->
|
||||||
|
{% if AccountingSupplierParty.Telephone or AccountingSupplierParty.Telefax or AccountingSupplierParty.ElectronicMail %}
|
||||||
|
<cac:Contact>
|
||||||
|
<!--TODO 3.0: opcional, de momento se manda info de la empresa sin nombre de contacto>
|
||||||
|
<cbc:Name>Diana Cruz</cbc:Name-->
|
||||||
|
{% if AccountingSupplierParty.Telephone %}
|
||||||
|
<cbc:Telephone>{{AccountingSupplierParty.Telephone}}</cbc:Telephone>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingSupplierParty.Telefax %}
|
||||||
|
<cbc:Telefax>{{AccountingSupplierParty.Telefax}}</cbc:Telefax>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingSupplierParty.ElectronicMail %}
|
||||||
|
<cbc:ElectronicMail>{{AccountingSupplierParty.ElectronicMail}}</cbc:ElectronicMail>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: opcional, de momento se manda info de la empresa no se sabe que mandar aca de momento>
|
||||||
|
<cbc:Note></cbc:Note-->
|
||||||
|
</cac:Contact>
|
||||||
|
{% endif %}
|
||||||
|
</cac:Party>
|
||||||
|
</cac:AccountingSupplierParty>
|
||||||
|
<cac:AccountingCustomerParty>
|
||||||
|
<cbc:AdditionalAccountID>{{AccountingCustomerParty.AdditionalAccountID}}</cbc:AdditionalAccountID>
|
||||||
|
<cac:Party>
|
||||||
|
{% if IndustryClassificationCode %}
|
||||||
|
<cbc:IndustryClassificationCode>{{IndustryClassificationCode}}</cbc:IndustryClassificationCode>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>{{AccountingCustomerParty.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{AccountingCustomerParty.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if AccountingCustomerParty.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{AccountingCustomerParty.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{AccountingCustomerParty.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{AccountingCustomerParty.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{AccountingCustomerParty.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{AccountingCustomerParty.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{AccountingCustomerParty.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
{% endif %}
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>{{AccountingCustomerParty.Name}}</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
{% if AccountingCustomerParty.CompanyIDschemeName == '31' %}schemeID="{{AccountingCustomerParty.CompanyIDschemeID}}"{% endif %}
|
||||||
|
schemeName="{{AccountingCustomerParty.CompanyIDschemeName}}">{{AccountingCustomerParty.CompanyID}}</cbc:CompanyID>
|
||||||
|
<!--TODO 2.0: listName el anexo dice que eliminar o valor No aplica, pero para consumidor final dice informar 49-->
|
||||||
|
<cbc:TaxLevelCode listName="{{AccountingCustomerParty.listName}}">{{AccountingCustomerParty.TaxLevelCode}}</cbc:TaxLevelCode>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
{% endif %}
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{AccountingCustomerParty.TaxSchemeID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{AccountingCustomerParty.TaxSchemeName}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' and (AccountingCustomerParty.Telephone or AccountingCustomerParty.Telefax or AccountingCustomerParty.ElectronicMail) %}
|
||||||
|
<cac:Contact>
|
||||||
|
<!--TODO 2.0: opcional, de momento se manda info de la empresa sin nombre de contacto>
|
||||||
|
<cbc:Name>Diana Cruz</cbc:Name-->
|
||||||
|
{% if AccountingCustomerParty.Telephone %}
|
||||||
|
<cbc:Telephone>{{AccountingCustomerParty.Telephone}}</cbc:Telephone>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.Telefax %}
|
||||||
|
<cbc:Telefax>{{AccountingCustomerParty.Telefax}}</cbc:Telefax>
|
||||||
|
{% endif %}
|
||||||
|
{% if AccountingCustomerParty.ElectronicMail %}
|
||||||
|
<cbc:ElectronicMail>{{AccountingCustomerParty.ElectronicMail}}</cbc:ElectronicMail>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0: opcional, de momento se manda info de la empresa no se sabe que mandar aca de momento>
|
||||||
|
<cbc:Note></cbc:Note-->
|
||||||
|
</cac:Contact>
|
||||||
|
{% endif %}
|
||||||
|
</cac:Party>
|
||||||
|
</cac:AccountingCustomerParty>
|
||||||
|
<cac:Delivery>
|
||||||
|
<cbc:ActualDeliveryDate>{{ActualDeliveryDate}}</cbc:ActualDeliveryDate>
|
||||||
|
<cbc:ActualDeliveryTime>{{ActualDeliveryTime}}</cbc:ActualDeliveryTime>
|
||||||
|
{% if AccountingCustomerParty.CompanyID != '222222222222' %}
|
||||||
|
<cac:DeliveryAddress>
|
||||||
|
<cbc:ID>{{Delivery.AddressID}}</cbc:ID>
|
||||||
|
<cbc:CityName>{{Delivery.AddressCityName}}</cbc:CityName>
|
||||||
|
{% if Delivery.AddressPostalZone %}
|
||||||
|
<cbc:PostalZone>{{Delivery.AddressPostalZone}}</cbc:PostalZone>
|
||||||
|
{% endif %}
|
||||||
|
<cbc:CountrySubentity>{{Delivery.AddressCountrySubentity}}</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>{{Delivery.AddressCountrySubentityCode}}</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>{{Delivery.AddressLine}}</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>{{Delivery.CountryIdentificationCode}}</cbc:IdentificationCode>
|
||||||
|
<!--TODO 2.0: languageID podria variar, siguiente version revisar esto-->
|
||||||
|
<cbc:Name languageID="es">{{Delivery.CountryName}}</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:DeliveryAddress>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 3.0, Grupo de datos con información sobre la empresa de transporte, desarrollo futuro>
|
||||||
|
<cac:DeliveryParty>
|
||||||
|
<cac:PartyName>
|
||||||
|
<cbc:Name></cbc:Name>
|
||||||
|
</cac:PartyName>
|
||||||
|
<cac:PhysicalLocation>
|
||||||
|
<cac:Address>
|
||||||
|
<cbc:ID>11001</cbc:ID>
|
||||||
|
<cbc:CityName>Bogotá, D.c. </cbc:CityName>
|
||||||
|
<cbc:PostalZone>Bogotá, D.c. </cbc:PostalZone>
|
||||||
|
<cbc:CountrySubentity>Bogotá</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>11</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>Av. #17 - 193</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>CO</cbc:IdentificationCode>
|
||||||
|
<TODO 2.0: languageID podria variar, siguiente version revisar esto>
|
||||||
|
<cbc:Name languageID="es">Colombia</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:Address>
|
||||||
|
</cac:PhysicalLocation>
|
||||||
|
<cac:PartyTaxScheme>
|
||||||
|
<cbc:RegistrationName>Empresa de transporte</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="1"
|
||||||
|
schemeName="31">981223983</cbc:CompanyID>
|
||||||
|
<TODO 2.0: listName el anexo dice que eliminar o valor No aplica,
|
||||||
|
pero para consumidor final dice informar 49>
|
||||||
|
<cbc:TaxLevelCode listName="">O-99</cbc:TaxLevelCode>
|
||||||
|
<cac:RegistrationAddress>
|
||||||
|
<cbc:ID>11001</cbc:ID>
|
||||||
|
<cbc:CityName>Bogotá, D.c. </cbc:CityName>
|
||||||
|
<cbc:PostalZone>Bogotá, D.c. </cbc:PostalZone>
|
||||||
|
<cbc:CountrySubentity>Bogotá</cbc:CountrySubentity>
|
||||||
|
<cbc:CountrySubentityCode>11</cbc:CountrySubentityCode>
|
||||||
|
<cac:AddressLine>
|
||||||
|
<cbc:Line>Av. #17 - 193</cbc:Line>
|
||||||
|
</cac:AddressLine>
|
||||||
|
<cac:Country>
|
||||||
|
<cbc:IdentificationCode>CO</cbc:IdentificationCode>
|
||||||
|
<TODO 2.0: languageID podria variar, siguiente version revisar esto>
|
||||||
|
<cbc:Name languageID="es">Colombia</cbc:Name>
|
||||||
|
</cac:Country>
|
||||||
|
</cac:RegistrationAddress>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>01</cbc:ID>
|
||||||
|
<cbc:Name>IVA</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:PartyTaxScheme>
|
||||||
|
<cac:PartyLegalEntity>
|
||||||
|
<cbc:RegistrationName>Empresa de transporte</cbc:RegistrationName>
|
||||||
|
<cbc:CompanyID schemeAgencyID="195"
|
||||||
|
schemeAgencyName="CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)"
|
||||||
|
schemeID="1"
|
||||||
|
schemeName="31">981223983</cbc:CompanyID>
|
||||||
|
<cac:CorporateRegistrationScheme>
|
||||||
|
<cbc:Name>75433</cbc:Name>
|
||||||
|
</cac:CorporateRegistrationScheme>
|
||||||
|
</cac:PartyLegalEntity>
|
||||||
|
<cac:Contact>
|
||||||
|
<cbc:Name>Eric Van Boxsom</cbc:Name>
|
||||||
|
<cbc:Telephone>9712311</cbc:Telephone>
|
||||||
|
<cbc:Telefax>12431241</cbc:Telefax>
|
||||||
|
<cbc:ElectronicMail>eric.vanboxsom@gosocket.net</cbc:ElectronicMail>
|
||||||
|
<cbc:Note>Test descripcion contacto</cbc:Note>
|
||||||
|
</cac:Contact>
|
||||||
|
</cac:DeliveryParty-->
|
||||||
|
</cac:Delivery>
|
||||||
|
{% if DeliveryTerms.LossRiskResponsibilityCode %}
|
||||||
|
<cac:DeliveryTerms>
|
||||||
|
<cbc:ID>1</cbc:ID>
|
||||||
|
<!--TODO 2.0: Con otro modulo complementario se puede resolver el texto libre-->
|
||||||
|
<!--cbc:SpecialTerms>Portes Pagados</cbc:SpecialTerms-->
|
||||||
|
<cbc:LossRiskResponsibilityCode>{{DeliveryTerms.LossRiskResponsibilityCode}}</cbc:LossRiskResponsibilityCode>
|
||||||
|
<cbc:LossRisk>{{DeliveryTerms.LossRisk}}</cbc:LossRisk>
|
||||||
|
</cac:DeliveryTerms>
|
||||||
|
{% endif %}
|
||||||
|
<cac:PaymentMeans>
|
||||||
|
<cbc:ID>{{PaymentMeansID}}</cbc:ID>
|
||||||
|
<cbc:PaymentMeansCode>{{PaymentMeansCode}}</cbc:PaymentMeansCode>
|
||||||
|
<cbc:PaymentDueDate>{{PaymentDueDate}}</cbc:PaymentDueDate>
|
||||||
|
<!--TODO 2.0: Identificador del pago, pueden ir de cero a varios PaymentID-->
|
||||||
|
<!--cbc:PaymentID></cbc:PaymentID-->
|
||||||
|
<!--TODO 2.0: En el anexo esta pero solo en facturas y no hay informacion sobre el campo-->
|
||||||
|
<!--cbc:PaymentTerms></cbc:PaymentTerms-->
|
||||||
|
</cac:PaymentMeans>
|
||||||
|
<!--TODO 3.0: Grupo de campos para informaciónrelacionadas con un anticipo -->
|
||||||
|
<!--cac:PrepaidPayment>
|
||||||
|
<cbc:ID>SFR3123856</cbc:ID>
|
||||||
|
<cbc:PaidAmount currencyID="{{DocumentCurrencyCode}}">1000.00</cbc:PaidAmount>
|
||||||
|
<cbc:ReceivedDate>2018-09-29</cbc:ReceivedDate>
|
||||||
|
<cbc:PaidDate>2018-09-29</cbc:PaidDate>
|
||||||
|
<cbc:PaidTime>2018-09-29</cbc:PaidDate>
|
||||||
|
<cbc:InstructionID>Prepago recibido</cbc:InstructionID>
|
||||||
|
</cac:PrepaidPayment-->
|
||||||
|
<!--TODO 3.0: Grupo de campos para información relacionadas con un cargo o un descuento>
|
||||||
|
<cac:AllowanceCharge>
|
||||||
|
<cbc:ID>SFR3123856</cbc:ID>
|
||||||
|
<cbc:ChargeIndicator></cbc:ChargeIndicator>
|
||||||
|
<cbc:AllowanceChargeReasonCode></cbc:AllowanceChargeReasonCode>
|
||||||
|
<cbc:AllowanceChargeReason></cbc:AllowanceChargeReason>
|
||||||
|
<cbc:MultiplierFactorNumeric>Prepago recibido</cbc:MultiplierFactorNumeric>
|
||||||
|
<cbc:Amount currencyID="{{DocumentCurrencyCode}}">1000.00</cbc:Amount>
|
||||||
|
<cbc:BaseAmount currencyID="{{DocumentCurrencyCode}}">1000.00</cbc:BaseAmount>
|
||||||
|
</cac:AllowanceCharge-->
|
||||||
|
{% if DocumentCurrencyCode != 'COP' %}
|
||||||
|
<cac:PaymentExchangeRate>
|
||||||
|
<cbc:SourceCurrencyCode>{{PaymentExchangeRate.SourceCurrencyCode}}</cbc:SourceCurrencyCode>
|
||||||
|
<cbc:SourceCurrencyBaseRate>1.00</cbc:SourceCurrencyBaseRate>
|
||||||
|
<cbc:TargetCurrencyCode>{{PaymentExchangeRate.TargetCurrencyCode}}</cbc:TargetCurrencyCode>
|
||||||
|
<cbc:TargetCurrencyBaseRate>1.00</cbc:TargetCurrencyBaseRate>
|
||||||
|
<cbc:CalculationRate>{{'{:.2f}'.format(PaymentExchangeRate.CalculationRate)}}</cbc:CalculationRate>
|
||||||
|
<cbc:Date>{{PaymentExchangeRate.Date}}</cbc:Date>
|
||||||
|
</cac:PaymentExchangeRate>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 3.0, Utilizado como metodo alternativo para infomar conversiones a otras divisas.>
|
||||||
|
<cac:PaymentAlternativeExchangeRate>
|
||||||
|
<cbc:SourceCurrencyCode>USD</cbc:SourceCurrencyCode>
|
||||||
|
<cbc:SourceCurrencyBaseRate>1.00</cbc:SourceCurrencyBaseRate>
|
||||||
|
<cbc:TargetCurrencyCode>COP</cbc:TargetCurrencyCode>
|
||||||
|
<cbc:TargetCurrencyBaseRate>1.00</cbc:TargetCurrencyBaseRate>
|
||||||
|
<cbc:CalculationRate>3100</cbc:CalculationRate>
|
||||||
|
<cbc:Date>2019-06-21</cbc:Date>
|
||||||
|
</cac:PaymentAlternativeExchangeRate-->
|
||||||
|
{% for TaxTotalID, TaxTotal in TaxesTotal.items() %}
|
||||||
|
<cac:TaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 2.0: Se manda 0.00 por ahora mientras se evalua la necesidad de calculos-->
|
||||||
|
<cbc:RoundingAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:RoundingAmount>
|
||||||
|
{% for Percent, TaxSubtotal in TaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 1.0: Usado en el caso de que el tributo es un valor fijo>
|
||||||
|
<cbc:BaseUnitMeasure></cbc:BaseUnitMeasure>
|
||||||
|
<cbc:unitCode></cbc:unitCode>
|
||||||
|
<cbc:PerUnitAmount currencyID="{{DocumentCurrencyCode}}"></cbc:TaxAmount-->
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{TaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{TaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:TaxTotal>
|
||||||
|
{% endfor %}
|
||||||
|
{% for WithholdingTaxTotalID, WithholdingTaxTotal in WithholdingTaxesTotal.items() %}
|
||||||
|
<cac:WithholdingTaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(WithholdingTaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
{% for Percent, TaxSubtotal in WithholdingTaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{WithholdingTaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{WithholdingTaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:WithholdingTaxTotal>
|
||||||
|
{% endfor %}
|
||||||
|
<cac:LegalMonetaryTotal>
|
||||||
|
<cbc:LineExtensionAmount currencyID="{{DocumentCurrencyCode}}">{{LineExtensionAmount}}</cbc:LineExtensionAmount>
|
||||||
|
<cbc:TaxExclusiveAmount currencyID="{{DocumentCurrencyCode}}">{{TaxExclusiveAmount}}</cbc:TaxExclusiveAmount>
|
||||||
|
<cbc:TaxInclusiveAmount currencyID="{{DocumentCurrencyCode}}">{{TaxInclusiveAmount}}</cbc:TaxInclusiveAmount>
|
||||||
|
<!--TODO 3.0: los prepagos son los anticipos, no hay soporte aun en el odoo para identificar esto
|
||||||
|
PrepaidAmount == 0 mientras tanto, PrepaidAmount = suma de PrepaidPayment
|
||||||
|
AllowanceTotalAmount == 0 y ChargeTotalAmount == 0 mientras tanto, suma de AllowanceCharge
|
||||||
|
TaxInclusiveAmount == PayableAmount mientras tanto-->
|
||||||
|
<cbc:AllowanceTotalAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:AllowanceTotalAmount>
|
||||||
|
<cbc:ChargeTotalAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:ChargeTotalAmount>
|
||||||
|
<cbc:PrepaidAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:PrepaidAmount>
|
||||||
|
<cbc:PayableAmount currencyID="{{DocumentCurrencyCode}}">{{PayableAmount}}</cbc:PayableAmount>
|
||||||
|
</cac:LegalMonetaryTotal>
|
||||||
|
{% for InvoiceLineID, InvoiceLine in InvoiceLines.items() %}
|
||||||
|
<cac:InvoiceLine>
|
||||||
|
<cbc:ID>{{InvoiceLineID}}</cbc:ID>
|
||||||
|
<cbc:InvoicedQuantity unitCode="{{InvoiceLine.unitCode}}">{{InvoiceLine.Quantity}}</cbc:InvoicedQuantity>
|
||||||
|
<cbc:LineExtensionAmount currencyID="{{DocumentCurrencyCode}}">{{InvoiceLine.LineExtensionAmount}}</cbc:LineExtensionAmount>
|
||||||
|
{% if InvoiceLine.LineExtensionAmount == '0.00' %}
|
||||||
|
<cac:PricingReference>
|
||||||
|
<cac:AlternativeConditionPrice>
|
||||||
|
<cbc:PriceAmount currencyID="{{DocumentCurrencyCode}}">{{InvoiceLine.PricingReferencePriceAmount}}</cbc:PriceAmount>
|
||||||
|
<cbc:PriceTypeCode>01</cbc:PriceTypeCode>
|
||||||
|
</cac:AlternativeConditionPrice>
|
||||||
|
</cac:PricingReference>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0, puede iterar en el campo ID aumenta segun la cantidad de descuentos o cargos por lineas
|
||||||
|
ChargeIndicator true seria un cargo-->
|
||||||
|
<cac:AllowanceCharge>
|
||||||
|
<cbc:ID>1</cbc:ID>
|
||||||
|
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||||
|
<cbc:AllowanceChargeReason>Descuento</cbc:AllowanceChargeReason>
|
||||||
|
<cbc:MultiplierFactorNumeric>{{InvoiceLine.MultiplierFactorNumeric}}</cbc:MultiplierFactorNumeric>
|
||||||
|
<cbc:Amount currencyID="{{DocumentCurrencyCode}}">{{InvoiceLine.AllowanceChargeAmount}}</cbc:Amount>
|
||||||
|
<cbc:BaseAmount currencyID="{{DocumentCurrencyCode}}">{{InvoiceLine.AllowanceChargeBaseAmount}}</cbc:BaseAmount>
|
||||||
|
</cac:AllowanceCharge>
|
||||||
|
<!--TODO 1.0, revisar que impuestos se deben y no informar?-->
|
||||||
|
{% for TaxTotalID, TaxTotal in InvoiceLine.TaxesTotal.items() %}
|
||||||
|
<cac:TaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 2.0: Se manda 0.00 por ahora mientras se evalua la necesidad de calculos-->
|
||||||
|
<cbc:RoundingAmount currencyID="{{DocumentCurrencyCode}}">0.00</cbc:RoundingAmount>
|
||||||
|
{% for Percent, TaxSubtotal in TaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<!--TODO 1.0: Usado en el caso de que el tributo es un valor fijo>
|
||||||
|
<cbc:BaseUnitMeasure></cbc:BaseUnitMeasure>
|
||||||
|
<cbc:unitCode></cbc:unitCode>
|
||||||
|
<cbc:PerUnitAmount currencyID="{{DocumentCurrencyCode}}"></cbc:TaxAmount-->
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{TaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{TaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:TaxTotal>
|
||||||
|
{% endfor %}
|
||||||
|
{% for WithholdingTaxTotalID, WithholdingTaxTotal in InvoiceLine.WithholdingTaxesTotal.items() %}
|
||||||
|
<cac:WithholdingTaxTotal>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(WithholdingTaxTotal.total)}}</cbc:TaxAmount>
|
||||||
|
{% for Percent, TaxSubtotal in WithholdingTaxTotal.taxes.items() %}
|
||||||
|
<cac:TaxSubtotal>
|
||||||
|
<cbc:TaxableAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.base)}}</cbc:TaxableAmount>
|
||||||
|
<cbc:TaxAmount currencyID="{{DocumentCurrencyCode}}">{{'{:.2f}'.format(TaxSubtotal.amount)}}</cbc:TaxAmount>
|
||||||
|
<cac:TaxCategory>
|
||||||
|
<cbc:Percent>{{Percent}}</cbc:Percent>
|
||||||
|
<cac:TaxScheme>
|
||||||
|
<cbc:ID>{{WithholdingTaxTotalID}}</cbc:ID>
|
||||||
|
<cbc:Name>{{WithholdingTaxTotal.name}}</cbc:Name>
|
||||||
|
</cac:TaxScheme>
|
||||||
|
</cac:TaxCategory>
|
||||||
|
</cac:TaxSubtotal>
|
||||||
|
{% endfor %}
|
||||||
|
</cac:WithholdingTaxTotal>
|
||||||
|
{% endfor %}
|
||||||
|
<cac:Item>
|
||||||
|
<cbc:Description>{{InvoiceLine.ItemDescription}}</cbc:Description>
|
||||||
|
<!--TODO 2.0, Cantidad de unidad de este artículo por empaque>
|
||||||
|
<cbc:PackSizeNumeric>{{InvoiceLine.PackSizeNumeric}}</cbc:PackSizeNumeric-->
|
||||||
|
{% if InvoiceLine.BrandName %}
|
||||||
|
<cbc:BrandName>{{InvoiceLine.BrandName}}</cbc:BrandName>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0, Mandaremos la referencia del fabricante por ahora, hay que definir luego si esto cambia-->
|
||||||
|
{% if InvoiceLine.ModelName %}
|
||||||
|
<cbc:ModelName>{{InvoiceLine.ModelName}}</cbc:ModelName>
|
||||||
|
{% endif %}
|
||||||
|
<!--TODO 2.0, Grupo de datos de identificación del artículo o servicio de acuerdo con el vendedor>
|
||||||
|
<cac:SellersItemIdentification>
|
||||||
|
<cbc:ID>AOHV84-225</cbc:ID>
|
||||||
|
<cbc:ExtendedID>AOHV84-225</cbc:ExtendedID>
|
||||||
|
</cac:SellersItemIdentification-->
|
||||||
|
<!--TODO 2.0, Grupo de datos de identificación del artículo o servicio de acuerdo con un estándar-->
|
||||||
|
<!--OPCIONAL schemeName="Estándar de adopción del contribuyente" schemeAgencyID="" schemeAgencyName=""-->
|
||||||
|
<cac:StandardItemIdentification>
|
||||||
|
<cbc:ID schemeID="999">{{InvoiceLine.StandardItemIdentification}}</cbc:ID>
|
||||||
|
</cac:StandardItemIdentification>
|
||||||
|
</cac:Item>
|
||||||
|
<cac:Price>
|
||||||
|
<cbc:PriceAmount currencyID="{{DocumentCurrencyCode}}">{{InvoiceLine.PriceAmount}}</cbc:PriceAmount>
|
||||||
|
<cbc:BaseQuantity unitCode="{{InvoiceLine.unitCode}}">{{InvoiceLine.Quantity}}</cbc:BaseQuantity>
|
||||||
|
</cac:Price>
|
||||||
|
</cac:InvoiceLine>
|
||||||
|
{% endfor %}
|
||||||
|
</Invoice>
|
||||||