implementacion de envio de factura para habilitacion
This commit is contained in:
parent
558f09b6a7
commit
80ec627f98
|
@ -2,7 +2,6 @@ from trytond.pool import Pool
|
||||||
from . import configuration
|
from . import configuration
|
||||||
|
|
||||||
from . import invoice
|
from . import invoice
|
||||||
from . import party
|
|
||||||
from . import facho
|
from . import facho
|
||||||
from . import dian
|
from . import dian
|
||||||
|
|
||||||
|
@ -18,6 +17,7 @@ def register():
|
||||||
invoice.InvoiceLine,
|
invoice.InvoiceLine,
|
||||||
invoice.Product,
|
invoice.Product,
|
||||||
invoice.Party,
|
invoice.Party,
|
||||||
|
invoice.Cron,
|
||||||
dian.RangoFacturacion,
|
dian.RangoFacturacion,
|
||||||
module='account_invoice_facho', type_='model')
|
module='account_invoice_facho', type_='model')
|
||||||
Pool.register(
|
Pool.register(
|
||||||
|
|
|
@ -13,10 +13,10 @@ class Configuration(
|
||||||
dian_fe_numeracion_username = fields.MultiValue(fields.Char('Username'))
|
dian_fe_numeracion_username = fields.MultiValue(fields.Char('Username'))
|
||||||
dian_fe_numeracion_password = fields.MultiValue(fields.Char('Password'))
|
dian_fe_numeracion_password = fields.MultiValue(fields.Char('Password'))
|
||||||
dian_fe_numeracion_key = fields.MultiValue(fields.Char('Dian FE Numeracion Key'))
|
dian_fe_numeracion_key = fields.MultiValue(fields.Char('Dian FE Numeracion Key'))
|
||||||
dian_fe_llave_publica = fields.MultiValue(fields.Binary('Privada PCKS#12'))
|
dian_fe_llave_publica = fields.MultiValue(fields.Char('Publica PCKS#12'))
|
||||||
dian_fe_llave_privada = fields.MultiValue(fields.Binary('Publica PCKS#12'))
|
dian_fe_llave_privada = fields.MultiValue(fields.Char('Privada PCKS#12'))
|
||||||
dian_fe_llave_frasepaso = fields.MultiValue(fields.Chat('Frasepaso PCKS#12'))
|
dian_fe_llave_frasepaso = fields.MultiValue(fields.Char('Frasepaso PCKS#12'))
|
||||||
dian_fe_test_setid = fields.MultiValue(fields.Chat('Dian testsetid'))
|
dian_fe_test_setid = fields.MultiValue(fields.Char('Dian testsetid'))
|
||||||
dian_fe_NITProveedorTecnologico = fields.MultiValue(fields.Char('NIT Proveedor Tecnologico'))
|
dian_fe_NITProveedorTecnologico = fields.MultiValue(fields.Char('NIT Proveedor Tecnologico'))
|
||||||
dian_fe_NITObligadoFacturarElectronicamente = fields.MultiValue(fields.Char('NIT Obligactior FE'))
|
dian_fe_NITObligadoFacturarElectronicamente = fields.MultiValue(fields.Char('NIT Obligactior FE'))
|
||||||
dian_fe_IdentificadorSoftware = fields.MultiValue(fields.Char('IdentificadorSoftware'))
|
dian_fe_IdentificadorSoftware = fields.MultiValue(fields.Char('IdentificadorSoftware'))
|
||||||
|
@ -38,10 +38,10 @@ class ConfigurationDianFECompany(ModelSQL, CompanyValueMixin):
|
||||||
dian_fe_numeracion_password = fields.Char('Password')
|
dian_fe_numeracion_password = fields.Char('Password')
|
||||||
dian_fe_numeracion_key = fields.Char('Dian FE Numeracion Key')
|
dian_fe_numeracion_key = fields.Char('Dian FE Numeracion Key')
|
||||||
dian_fe_key = fields.Char('Dian FE Key')
|
dian_fe_key = fields.Char('Dian FE Key')
|
||||||
dian_fe_llave_publica = fields.Binary('Publica PCKS#12')
|
dian_fe_llave_publica = fields.Char('Publica PCKS#12')
|
||||||
dian_fe_llave_privada = fields.Binary('Publica PCKS#12')
|
dian_fe_llave_privada = fields.Char('Publica PCKS#12')
|
||||||
dian_fe_test_setid = fields.Chat('Dian testsetid')
|
dian_fe_test_setid = fields.Char('Dian testsetid')
|
||||||
dian_fe_llave_frasepaso = fields.Chat('Frasepaso PCKS#12')
|
dian_fe_llave_frasepaso = fields.Char('Frasepaso PCKS#12')
|
||||||
dian_fe_NITProveedorTecnologico = fields.Char('NIT Proveedor Tecnologico')
|
dian_fe_NITProveedorTecnologico = fields.Char('NIT Proveedor Tecnologico')
|
||||||
dian_fe_NITObligadoFacturarElectronicamente = fields.Char('NIT Obligactior FE')
|
dian_fe_NITObligadoFacturarElectronicamente = fields.Char('NIT Obligactior FE')
|
||||||
dian_fe_IdentificadorSoftware = fields.Char('IdentificadorSoftware')
|
dian_fe_IdentificadorSoftware = fields.Char('IdentificadorSoftware')
|
||||||
|
|
114
invoice.py
114
invoice.py
|
@ -1,5 +1,5 @@
|
||||||
from trytond.model import fields
|
from trytond.model import fields, ModelView
|
||||||
from trytond.pool import PoolMeta
|
from trytond.pool import PoolMeta, Pool
|
||||||
from trytond.pyson import Eval
|
from trytond.pyson import Eval
|
||||||
from trytond.transaction import Transaction
|
from trytond.transaction import Transaction
|
||||||
from trytond.rpc import RPC
|
from trytond.rpc import RPC
|
||||||
|
@ -7,10 +7,19 @@ from trytond.exceptions import UserError
|
||||||
|
|
||||||
from facho.fe import form
|
from facho.fe import form
|
||||||
from facho import fe
|
from facho import fe
|
||||||
from fache.fe.client import dian
|
from facho.fe.client import dian
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
|
||||||
|
class Cron(metaclass=PoolMeta):
|
||||||
|
__name__ = 'ir.cron'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __setup__(cls):
|
||||||
|
super().__setup__()
|
||||||
|
cls.method.selection.extend([
|
||||||
|
('account.invoice|fe_delivery', 'FE Delivery')
|
||||||
|
])
|
||||||
|
|
||||||
class Party(metaclass=PoolMeta):
|
class Party(metaclass=PoolMeta):
|
||||||
__name__ = 'party.party'
|
__name__ = 'party.party'
|
||||||
|
@ -18,27 +27,28 @@ class Party(metaclass=PoolMeta):
|
||||||
fe_tipo_responsabilidad = fields.Many2One('account_invoice_facho.fe_generic_code',
|
fe_tipo_responsabilidad = fields.Many2One('account_invoice_facho.fe_generic_code',
|
||||||
'Tipo Responsabilidad',
|
'Tipo Responsabilidad',
|
||||||
domain=[('resource', '=', 'tipo_responsabilidad')],
|
domain=[('resource', '=', 'tipo_responsabilidad')],
|
||||||
states=_states,
|
required=True)
|
||||||
depends=_depends)
|
|
||||||
|
|
||||||
fe_tipo_organizacion = fields.Many2One('account_invoice_facho.fe_generic_code',
|
fe_tipo_organizacion = fields.Many2One('account_invoice_facho.fe_generic_code',
|
||||||
'Tipo Organizacion',
|
'Tipo Organizacion',
|
||||||
domain=[('resource', '=', 'tipo_organizacion')],
|
domain=[('resource', '=', 'tipo_organizacion')],
|
||||||
states=_states,
|
required=True)
|
||||||
depends=_depends)
|
|
||||||
|
|
||||||
|
|
||||||
def tofacho(self):
|
def tofacho(self):
|
||||||
return form.Party(
|
return form.Party(
|
||||||
name = self.name
|
name = self.name,
|
||||||
|
ident = self.name, # TODO
|
||||||
|
responsability_code = self.fe_tipo_responsabilidad.code,
|
||||||
|
organization_code = self.fe_tipo_organizacion.code,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Invoice(metaclass=PoolMeta):
|
class Invoice(metaclass=PoolMeta):
|
||||||
__name__ = 'account.invoice'
|
__name__ = 'account.invoice'
|
||||||
|
|
||||||
_states = {'invisible': ~Eval('is_fe_colombia', False),
|
_states = {'invisible': ~Eval('is_fe_colombia', False)}
|
||||||
'required': Eval('is_fe_colombia', False)}
|
|
||||||
_depends = ['is_fe_colombia']
|
_depends = ['is_fe_colombia']
|
||||||
|
|
||||||
is_fe_colombia = fields.Boolean('FE Colombia?',
|
is_fe_colombia = fields.Boolean('FE Colombia?',
|
||||||
|
@ -47,12 +57,13 @@ class Invoice(metaclass=PoolMeta):
|
||||||
states=_states,
|
states=_states,
|
||||||
depends=_depends)
|
depends=_depends)
|
||||||
fe_delivery_state = fields.Selection([
|
fe_delivery_state = fields.Selection([
|
||||||
|
('draft', 'draft'),
|
||||||
('queued', 'Queued'), # local encola
|
('queued', 'Queued'), # local encola
|
||||||
('delivered', 'Delivered'), # remoto encola
|
('delivered', 'Delivered'), # remoto encola
|
||||||
('exception', 'Exception'), # local exception
|
('exception', 'Exception'), # local exception
|
||||||
('failure', 'Failure'), # remoto fallo
|
('failure', 'Failure'), # remoto fallo
|
||||||
('done', 'Done') # remoto ok
|
('done', 'Done') # remoto ok
|
||||||
], 'Delivery', states=_states, dependes=_depends)
|
], 'Delivery State', states=_states, depends=_depends)
|
||||||
fe_delivery_trackid = fields.Char('Delivery TrackID',
|
fe_delivery_trackid = fields.Char('Delivery TrackID',
|
||||||
states=_states,
|
states=_states,
|
||||||
depends=_depends)
|
depends=_depends)
|
||||||
|
@ -62,7 +73,10 @@ class Invoice(metaclass=PoolMeta):
|
||||||
del _states
|
del _states
|
||||||
del _depends
|
del _depends
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def default_fe_delivery_state():
|
||||||
|
return 'draft'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default_is_fe_colombia():
|
def default_is_fe_colombia():
|
||||||
return Transaction().context.get('is_fe_colombia', False)
|
return Transaction().context.get('is_fe_colombia', False)
|
||||||
|
@ -73,7 +87,6 @@ class Invoice(metaclass=PoolMeta):
|
||||||
|
|
||||||
for invoice in invoices:
|
for invoice in invoices:
|
||||||
for line in invoice.lines:
|
for line in invoice.lines:
|
||||||
tax_subtotals = []
|
|
||||||
for taxes, unit_price, quantity in line.taxable_lines:
|
for taxes, unit_price, quantity in line.taxable_lines:
|
||||||
for tax in taxes:
|
for tax in taxes:
|
||||||
if tax.type != 'percentage':
|
if tax.type != 'percentage':
|
||||||
|
@ -85,7 +98,6 @@ class Invoice(metaclass=PoolMeta):
|
||||||
for (model, field, error) in validator.errors:
|
for (model, field, error) in validator.errors:
|
||||||
raise UserError('model %s in field %s has %s' % (model, field, error))
|
raise UserError('model %s in field %s has %s' % (model, field, error))
|
||||||
|
|
||||||
|
|
||||||
def tofacho(self):
|
def tofacho(self):
|
||||||
inv = form.Invoice()
|
inv = form.Invoice()
|
||||||
inv.set_period(self.invoice_date, self.invoice_date)
|
inv.set_period(self.invoice_date, self.invoice_date)
|
||||||
|
@ -100,52 +112,74 @@ class Invoice(metaclass=PoolMeta):
|
||||||
inv.calculate()
|
inv.calculate()
|
||||||
return inv
|
return inv
|
||||||
|
|
||||||
|
def do_dian_request(self, request, config=None):
|
||||||
def fe_get_status(self):
|
if config is None:
|
||||||
config = pool.get('account_invoice_facho.configuration')(1)
|
config = Pool().get('account_invoice_facho.configuration')(1)
|
||||||
client = dian.DianSignatureClient(config.dian_fe_llave_privada,
|
client = dian.DianSignatureClient(config.dian_fe_llave_privada,
|
||||||
config.dian_fe_llave_publica,
|
config.dian_fe_llave_publica,
|
||||||
password=config.dian_fe_llave_frasepaso)
|
password=config.dian_fe_llave_frasepaso)
|
||||||
|
return client.request(request)
|
||||||
|
|
||||||
|
def fe_update_status(self):
|
||||||
req = dian.GetStatusZip
|
req = dian.GetStatusZip
|
||||||
if self.fe_habilitacion:
|
if self.fe_habilitacion:
|
||||||
req = dian.Habilitacion.GetStatusZip
|
req = dian.Habilitacion.GetStatusZip
|
||||||
resp = client.request(req(trackId = self.fe_delivery_trackid))
|
resp = self.do_dian_request(req(trackId = self.fe_delivery_trackid))
|
||||||
|
|
||||||
self.fe_delivery_status = str(resp)
|
params = {}
|
||||||
if resp['IsValid']:
|
params['fe_delivery_status'] = resp.StatusDescription
|
||||||
self.fe_delivery_state = 'done'
|
if resp.IsValid:
|
||||||
|
params['fe_delivery_state'] = 'donde'
|
||||||
else:
|
else:
|
||||||
self.fe_delivery_state = 'failure'
|
params['fe_delivery_state'] = 'failure'
|
||||||
self.save()
|
|
||||||
|
|
||||||
|
|
||||||
|
self._force_write(params)
|
||||||
|
|
||||||
|
def _force_write(self, params):
|
||||||
|
self.state = 'draft'
|
||||||
|
self.write([self], params)
|
||||||
|
|
||||||
|
|
||||||
def fe_delivery_test(self):
|
def fe_delivery_test(self):
|
||||||
if self.fe_delivery_state != 'queued':
|
config = Pool().get('account_invoice_facho.configuration')(1)
|
||||||
|
if self.fe_delivery_state not in ['queued', 'draft']:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
facho_invoice = self.tofacho()
|
facho_invoice = self.tofacho()
|
||||||
xml_invoice = from.DIANInvoiceXML(facho_invoice)
|
xml_invoice = form.DIANInvoiceXML(facho_invoice)
|
||||||
|
|
||||||
zipdata = io.BytesIO()
|
zipdata = io.BytesIO()
|
||||||
with fe.DianZIP(zipdata) as dianzip:
|
with fe.DianZIP(zipdata) as dianzip:
|
||||||
name_invoice = dianzip.add_invoice_xml(facho_invoice.invoice_ident, str(xml_invoice))
|
dianzip.add_invoice_xml(facho_invoice.invoice_ident, str(xml_invoice))
|
||||||
|
|
||||||
config = pool.get('account_invoice_facho.configuration')(1)
|
zipdata.seek(0)
|
||||||
client = dian.DianSignatureClient(config.dian_fe_llave_privada,
|
|
||||||
config.dian_fe_llave_publica,
|
|
||||||
password=config.dian_fe_llave_frasepaso)
|
|
||||||
filename = 'invoice_%s' % (facho_invoice.invoice_ident)
|
filename = 'invoice_%s' % (facho_invoice.invoice_ident)
|
||||||
req = client.required(dian.Habilitacion.SendTestSetAsync(
|
res = self.do_dian_request(dian.Habilitacion.SendTestSetAsync(
|
||||||
filename, zipdata.read(),
|
filename, zipdata.read(),
|
||||||
config.dian_fe_test_setid
|
config.dian_fe_test_setid
|
||||||
))
|
))
|
||||||
if req['ErrorMessageList']:
|
if not res.ZipKey:
|
||||||
raise UserError(str(req))
|
raise UserError(str(res))
|
||||||
self.fe_delivery_trackid = req['ZipKey']
|
# HACK force draft for allow write
|
||||||
self.state = 'delivered'
|
self._force_write({'fe_delivery_state': 'delivered',
|
||||||
self.save()
|
'fe_delivery_trackid': res.ZipKey})
|
||||||
|
|
||||||
|
|
||||||
|
def fe_process(self):
|
||||||
|
if self.fe_habilitacion:
|
||||||
|
# TODO forzar facturas contabilidadas
|
||||||
|
self.fe_delivery_test()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fe_delivery(cls):
|
||||||
|
for inv in cls.search([('is_fe_colombia', '=', True),
|
||||||
|
('state', 'in', ['posted', 'paid'])]):
|
||||||
|
inv.fe_process()
|
||||||
|
if inv.fe_delivery_state == 'delivered':
|
||||||
|
inv.fe_update_status()
|
||||||
|
|
||||||
|
|
||||||
class Product(metaclass=PoolMeta):
|
class Product(metaclass=PoolMeta):
|
||||||
__name__ = 'product.product'
|
__name__ = 'product.product'
|
||||||
|
|
||||||
|
@ -161,7 +195,7 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||||
|
|
||||||
def tofacho(self):
|
def tofacho(self):
|
||||||
tax_subtotals = []
|
tax_subtotals = []
|
||||||
for taxes, unit_price, quantity in line.taxable_lines:
|
for taxes, unit_price, quantity in self.taxable_lines:
|
||||||
for tax in taxes:
|
for tax in taxes:
|
||||||
tax_subtotals.append(form.TaxSubTotal(
|
tax_subtotals.append(form.TaxSubTotal(
|
||||||
percent = tax.rate
|
percent = tax.rate
|
||||||
|
@ -171,7 +205,7 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||||
quantity = self.quantity,
|
quantity = self.quantity,
|
||||||
description = self.description,
|
description = self.description,
|
||||||
# TODO debe ser decimal
|
# TODO debe ser decimal
|
||||||
price_amount = float(line.unit_price),
|
price_amount = float(self.unit_price),
|
||||||
item = self.product.tofacho(),
|
item = self.product.tofacho(),
|
||||||
tax = form.TaxTotal(
|
tax = form.TaxTotal(
|
||||||
subtotals = tax_subtotals
|
subtotals = tax_subtotals
|
||||||
|
|
11
invoice.xml
11
invoice.xml
|
@ -13,6 +13,17 @@
|
||||||
<field name="inherit" ref="account_invoice.invoice_view_form"/>
|
<field name="inherit" ref="account_invoice.invoice_view_form"/>
|
||||||
<field name="name">invoice_fe_colombia_form</field>
|
<field name="name">invoice_fe_colombia_form</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record model="ir.ui.view" id="invoice_fe_page_in_form">
|
||||||
|
<field name="model">account.invoice</field>
|
||||||
|
<field name="inherit" ref="account_invoice.invoice_view_form"/>
|
||||||
|
<field name="name">invoice_page_form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="party_fe_colombia_form">
|
||||||
|
<field name="model">party.party</field>
|
||||||
|
<field name="inherit" ref="party.party_view_form"/>
|
||||||
|
<field name="name">party_form</field>
|
||||||
|
</record>
|
||||||
<menuitem name="Customer Invoice Fe Colombia"
|
<menuitem name="Customer Invoice Fe Colombia"
|
||||||
parent="account_invoice.menu_invoices" action="act_invoice_in_form"
|
parent="account_invoice.menu_invoices" action="act_invoice_in_form"
|
||||||
id="menu_invoice_fe_colombia_in_form" sequence="3"/>
|
id="menu_invoice_fe_colombia_in_form" sequence="3"/>
|
||||||
|
|
|
@ -15,6 +15,12 @@
|
||||||
<field name="dian_fe_NITObligadoFacturarElectronicamente"/>
|
<field name="dian_fe_NITObligadoFacturarElectronicamente"/>
|
||||||
<label name="dian_fe_IdentificadorSoftware"/>
|
<label name="dian_fe_IdentificadorSoftware"/>
|
||||||
<field name="dian_fe_IdentificadorSoftware"/>
|
<field name="dian_fe_IdentificadorSoftware"/>
|
||||||
<label name="dian_fe_certificado"/>
|
<label name="dian_fe_llave_publica"/>
|
||||||
<field name="dian_fe_certificado"/>
|
<field name="dian_fe_llave_publica"/>
|
||||||
|
<label name="dian_fe_llave_privada"/>
|
||||||
|
<field name="dian_fe_llave_privada"/>
|
||||||
|
<label name="dian_fe_test_setid"/>
|
||||||
|
<field name="dian_fe_test_setid"/>
|
||||||
|
<label name="dian_fe_llave_frasepaso"/>
|
||||||
|
<field name="dian_fe_llave_frasepaso"/>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
<data>
|
<data>
|
||||||
<xpath expr="/form/field[@name='reference']"
|
<xpath expr="/form/field[@name='reference']"
|
||||||
position="after">
|
position="after">
|
||||||
<label name="tipo_responsabilidad"/>
|
<label name="fe_habilitacion"/>
|
||||||
<field name="tipo_responsabilidad"/>
|
<field name="fe_habilitacion"/>
|
||||||
<label name="tipo_organizacion"/>
|
|
||||||
<field name="tipo_organizacion"/>
|
|
||||||
</xpath>
|
</xpath>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<data>
|
||||||
|
<xpath expr="/form/notebook/page[@id='payment']"
|
||||||
|
position="after">
|
||||||
|
<page string="FE Colombia" name="fe_colombia">
|
||||||
|
<label name="fe_delivery_state"/>
|
||||||
|
<field name="fe_delivery_state"/>
|
||||||
|
<label name="fe_delivery_status"/>
|
||||||
|
<field name="fe_delivery_status"/>
|
||||||
|
<label name="fe_delivery_trackid"/>
|
||||||
|
<field name="fe_delivery_trackid"/>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<data>
|
||||||
|
<xpath expr="/form/notebook/page[@name='identifiers']"
|
||||||
|
position="after">
|
||||||
|
<page string="FE Colombia" name="fe_colombia">
|
||||||
|
<label name="fe_tipo_responsabilidad"/>
|
||||||
|
<field name="fe_tipo_responsabilidad"/>
|
||||||
|
<label name="fe_tipo_organizacion"/>
|
||||||
|
<field name="fe_tipo_organizacion"/>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
Loading…
Reference in New Issue