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

View File

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

View File

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

View File

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

View File

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

View File

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

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>