implementacion de envio de factura para habilitacion

This commit is contained in:
Jovany Leandro G.C 2020-06-02 14:09:37 -05:00
parent 558f09b6a7
commit 80ec627f98
8 changed files with 130 additions and 55 deletions

View File

@ -2,7 +2,6 @@ from trytond.pool import Pool
from . import configuration
from . import invoice
from . import party
from . import facho
from . import dian
@ -18,6 +17,7 @@ def register():
invoice.InvoiceLine,
invoice.Product,
invoice.Party,
invoice.Cron,
dian.RangoFacturacion,
module='account_invoice_facho', type_='model')
Pool.register(

View File

@ -13,10 +13,10 @@ class Configuration(
dian_fe_numeracion_username = fields.MultiValue(fields.Char('Username'))
dian_fe_numeracion_password = fields.MultiValue(fields.Char('Password'))
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_privada = fields.MultiValue(fields.Binary('Publica PCKS#12'))
dian_fe_llave_frasepaso = fields.MultiValue(fields.Chat('Frasepaso PCKS#12'))
dian_fe_test_setid = fields.MultiValue(fields.Chat('Dian testsetid'))
dian_fe_llave_publica = fields.MultiValue(fields.Char('Publica PCKS#12'))
dian_fe_llave_privada = fields.MultiValue(fields.Char('Privada PCKS#12'))
dian_fe_llave_frasepaso = fields.MultiValue(fields.Char('Frasepaso PCKS#12'))
dian_fe_test_setid = fields.MultiValue(fields.Char('Dian testsetid'))
dian_fe_NITProveedorTecnologico = fields.MultiValue(fields.Char('NIT Proveedor Tecnologico'))
dian_fe_NITObligadoFacturarElectronicamente = fields.MultiValue(fields.Char('NIT Obligactior FE'))
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_key = fields.Char('Dian FE Numeracion Key')
dian_fe_key = fields.Char('Dian FE Key')
dian_fe_llave_publica = fields.Binary('Publica PCKS#12')
dian_fe_llave_privada = fields.Binary('Publica PCKS#12')
dian_fe_test_setid = fields.Chat('Dian testsetid')
dian_fe_llave_frasepaso = fields.Chat('Frasepaso PCKS#12')
dian_fe_llave_publica = fields.Char('Publica PCKS#12')
dian_fe_llave_privada = fields.Char('Publica PCKS#12')
dian_fe_test_setid = fields.Char('Dian testsetid')
dian_fe_llave_frasepaso = fields.Char('Frasepaso PCKS#12')
dian_fe_NITProveedorTecnologico = fields.Char('NIT Proveedor Tecnologico')
dian_fe_NITObligadoFacturarElectronicamente = fields.Char('NIT Obligactior FE')
dian_fe_IdentificadorSoftware = fields.Char('IdentificadorSoftware')

View File

@ -1,5 +1,5 @@
from trytond.model import fields
from trytond.pool import PoolMeta
from trytond.model import fields, ModelView
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.rpc import RPC
@ -7,10 +7,19 @@ from trytond.exceptions import UserError
from facho.fe import form
from facho import fe
from fache.fe.client import dian
from facho.fe.client import dian
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):
__name__ = 'party.party'
@ -18,27 +27,28 @@ class Party(metaclass=PoolMeta):
fe_tipo_responsabilidad = fields.Many2One('account_invoice_facho.fe_generic_code',
'Tipo Responsabilidad',
domain=[('resource', '=', 'tipo_responsabilidad')],
states=_states,
depends=_depends)
required=True)
fe_tipo_organizacion = fields.Many2One('account_invoice_facho.fe_generic_code',
'Tipo Organizacion',
domain=[('resource', '=', 'tipo_organizacion')],
states=_states,
depends=_depends)
required=True)
def tofacho(self):
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):
__name__ = 'account.invoice'
_states = {'invisible': ~Eval('is_fe_colombia', False),
'required': Eval('is_fe_colombia', False)}
_states = {'invisible': ~Eval('is_fe_colombia', False)}
_depends = ['is_fe_colombia']
is_fe_colombia = fields.Boolean('FE Colombia?',
@ -47,12 +57,13 @@ class Invoice(metaclass=PoolMeta):
states=_states,
depends=_depends)
fe_delivery_state = fields.Selection([
('draft', 'draft'),
('queued', 'Queued'), # local encola
('delivered', 'Delivered'), # remoto encola
('exception', 'Exception'), # local exception
('failure', 'Failure'), # remoto fallo
('done', 'Done') # remoto ok
], 'Delivery', states=_states, dependes=_depends)
], 'Delivery State', states=_states, depends=_depends)
fe_delivery_trackid = fields.Char('Delivery TrackID',
states=_states,
depends=_depends)
@ -62,7 +73,10 @@ class Invoice(metaclass=PoolMeta):
del _states
del _depends
@staticmethod
def default_fe_delivery_state():
return 'draft'
@staticmethod
def default_is_fe_colombia():
return Transaction().context.get('is_fe_colombia', False)
@ -73,7 +87,6 @@ class Invoice(metaclass=PoolMeta):
for invoice in invoices:
for line in invoice.lines:
tax_subtotals = []
for taxes, unit_price, quantity in line.taxable_lines:
for tax in taxes:
if tax.type != 'percentage':
@ -85,7 +98,6 @@ class Invoice(metaclass=PoolMeta):
for (model, field, error) in validator.errors:
raise UserError('model %s in field %s has %s' % (model, field, error))
def tofacho(self):
inv = form.Invoice()
inv.set_period(self.invoice_date, self.invoice_date)
@ -100,52 +112,74 @@ class Invoice(metaclass=PoolMeta):
inv.calculate()
return inv
def fe_get_status(self):
config = pool.get('account_invoice_facho.configuration')(1)
def do_dian_request(self, request, config=None):
if config is None:
config = Pool().get('account_invoice_facho.configuration')(1)
client = dian.DianSignatureClient(config.dian_fe_llave_privada,
config.dian_fe_llave_publica,
password=config.dian_fe_llave_frasepaso)
return client.request(request)
def fe_update_status(self):
req = dian.GetStatusZip
if self.fe_habilitacion:
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)
if resp['IsValid']:
self.fe_delivery_state = 'done'
params = {}
params['fe_delivery_status'] = resp.StatusDescription
if resp.IsValid:
params['fe_delivery_state'] = 'donde'
else:
self.fe_delivery_state = 'failure'
self.save()
params['fe_delivery_state'] = 'failure'
self._force_write(params)
def _force_write(self, params):
self.state = 'draft'
self.write([self], params)
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
facho_invoice = self.tofacho()
xml_invoice = from.DIANInvoiceXML(facho_invoice)
xml_invoice = form.DIANInvoiceXML(facho_invoice)
zipdata = io.BytesIO()
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)
client = dian.DianSignatureClient(config.dian_fe_llave_privada,
config.dian_fe_llave_publica,
password=config.dian_fe_llave_frasepaso)
zipdata.seek(0)
filename = 'invoice_%s' % (facho_invoice.invoice_ident)
req = client.required(dian.Habilitacion.SendTestSetAsync(
res = self.do_dian_request(dian.Habilitacion.SendTestSetAsync(
filename, zipdata.read(),
config.dian_fe_test_setid
))
if req['ErrorMessageList']:
raise UserError(str(req))
self.fe_delivery_trackid = req['ZipKey']
self.state = 'delivered'
self.save()
if not res.ZipKey:
raise UserError(str(res))
# HACK force draft for allow write
self._force_write({'fe_delivery_state': 'delivered',
'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):
__name__ = 'product.product'
@ -161,7 +195,7 @@ class InvoiceLine(metaclass=PoolMeta):
def tofacho(self):
tax_subtotals = []
for taxes, unit_price, quantity in line.taxable_lines:
for taxes, unit_price, quantity in self.taxable_lines:
for tax in taxes:
tax_subtotals.append(form.TaxSubTotal(
percent = tax.rate
@ -171,7 +205,7 @@ class InvoiceLine(metaclass=PoolMeta):
quantity = self.quantity,
description = self.description,
# TODO debe ser decimal
price_amount = float(line.unit_price),
price_amount = float(self.unit_price),
item = self.product.tofacho(),
tax = form.TaxTotal(
subtotals = tax_subtotals

View File

@ -13,6 +13,17 @@
<field name="inherit" ref="account_invoice.invoice_view_form"/>
<field name="name">invoice_fe_colombia_form</field>
</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"
parent="account_invoice.menu_invoices" action="act_invoice_in_form"
id="menu_invoice_fe_colombia_in_form" sequence="3"/>

View File

@ -15,6 +15,12 @@
<field name="dian_fe_NITObligadoFacturarElectronicamente"/>
<label name="dian_fe_IdentificadorSoftware"/>
<field name="dian_fe_IdentificadorSoftware"/>
<label name="dian_fe_certificado"/>
<field name="dian_fe_certificado"/>
<label name="dian_fe_llave_publica"/>
<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>

View File

@ -2,9 +2,7 @@
<data>
<xpath expr="/form/field[@name='reference']"
position="after">
<label name="tipo_responsabilidad"/>
<field name="tipo_responsabilidad"/>
<label name="tipo_organizacion"/>
<field name="tipo_organizacion"/>
<label name="fe_habilitacion"/>
<field name="fe_habilitacion"/>
</xpath>
</data>

View File

@ -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>

12
view/party_form.xml Normal file
View File

@ -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>