first commit
This commit is contained in:
218
modules/edocument_uncefact/edocument.py
Normal file
218
modules/edocument_uncefact/edocument.py
Normal file
@@ -0,0 +1,218 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import functools
|
||||
import os
|
||||
from decimal import Decimal
|
||||
|
||||
import genshi
|
||||
import genshi.template
|
||||
# XXX fix: https://genshi.edgewall.org/ticket/582
|
||||
from genshi.template.astutil import ASTCodeGenerator, ASTTransformer
|
||||
|
||||
from trytond.model import Model
|
||||
from trytond.pool import Pool
|
||||
from trytond.rpc import RPC
|
||||
from trytond.tools import cached_property, slugify
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
if not hasattr(ASTCodeGenerator, 'visit_NameConstant'):
|
||||
def visit_NameConstant(self, node):
|
||||
if node.value is None:
|
||||
self._write('None')
|
||||
elif node.value is True:
|
||||
self._write('True')
|
||||
elif node.value is False:
|
||||
self._write('False')
|
||||
else:
|
||||
raise Exception("Unknown NameConstant %r" % (node.value,))
|
||||
ASTCodeGenerator.visit_NameConstant = visit_NameConstant
|
||||
if not hasattr(ASTTransformer, 'visit_NameConstant'):
|
||||
# Re-use visit_Name because _clone is deleted
|
||||
ASTTransformer.visit_NameConstant = ASTTransformer.visit_Name
|
||||
|
||||
loader = genshi.template.TemplateLoader(
|
||||
os.path.join(os.path.dirname(__file__), 'template'),
|
||||
auto_reload=True)
|
||||
|
||||
|
||||
def has_goods_assets(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(self):
|
||||
if any(l.product.type in {'goods', 'assets'}
|
||||
for l in self.invoice.lines if l.product):
|
||||
return func(self)
|
||||
return wrapper
|
||||
|
||||
|
||||
def remove_comment(stream):
|
||||
for kind, data, pos in stream:
|
||||
if kind is genshi.core.COMMENT:
|
||||
continue
|
||||
yield kind, data, pos
|
||||
|
||||
|
||||
class Invoice(Model):
|
||||
__name__ = 'edocument.uncefact.invoice'
|
||||
__slots__ = ('invoice',)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.__rpc__.update({
|
||||
'render': RPC(instantiate=0),
|
||||
})
|
||||
|
||||
def __init__(self, invoice):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
super().__init__()
|
||||
if int(invoice) >= 0:
|
||||
invoice = Invoice(int(invoice))
|
||||
with Transaction().set_context(language=invoice.party_lang):
|
||||
self.invoice = invoice.__class__(int(invoice))
|
||||
else:
|
||||
self.invoice = invoice
|
||||
|
||||
def render(self, template):
|
||||
if self.invoice.state not in {'posted', 'paid'}:
|
||||
raise ValueError("Invoice must be posted")
|
||||
tmpl = self._get_template(template)
|
||||
if not tmpl:
|
||||
raise NotImplementedError
|
||||
return (tmpl.generate(
|
||||
this=self,
|
||||
Decimal=Decimal)
|
||||
.filter(remove_comment)
|
||||
.render()
|
||||
.encode('utf-8'))
|
||||
|
||||
def _get_template(self, version):
|
||||
return loader.load(os.path.join(version, 'CrossIndustryInvoice.xml'))
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
return f'{slugify(self.invoice.rec_name)}.xml'
|
||||
|
||||
@cached_property
|
||||
def type_code(self):
|
||||
if self.invoice.type == 'out':
|
||||
if all(l.amount < 0 for l in self.lines):
|
||||
return '381'
|
||||
else:
|
||||
return '380'
|
||||
else:
|
||||
if all(l.amount < 0 for l in self.lines):
|
||||
return '261'
|
||||
else:
|
||||
return '389'
|
||||
|
||||
@cached_property
|
||||
def type_sign(self):
|
||||
"The sign of the quantity depending of the type code"
|
||||
if self.type_code in {'381', '261'}:
|
||||
return -1
|
||||
return 1
|
||||
|
||||
@cached_property
|
||||
def lines(self):
|
||||
return [l for l in self.invoice.lines if l.type == 'line']
|
||||
|
||||
@cached_property
|
||||
def seller_trade_party(self):
|
||||
if self.invoice.type == 'out':
|
||||
return self.invoice.company.party
|
||||
else:
|
||||
return self.invoice.party
|
||||
|
||||
@cached_property
|
||||
def seller_trade_address(self):
|
||||
if self.invoice.type == 'out':
|
||||
return self.invoice.company.party.address_get('invoice')
|
||||
else:
|
||||
return self.invoice.invoice_address
|
||||
|
||||
@cached_property
|
||||
def seller_trade_tax_identifier(self):
|
||||
if self.invoice.type == 'out':
|
||||
return self.invoice.tax_identifier
|
||||
else:
|
||||
return self.invoice.party_tax_identifier
|
||||
|
||||
@cached_property
|
||||
def buyer_trade_party(self):
|
||||
if self.invoice.type == 'out':
|
||||
return self.invoice.party
|
||||
else:
|
||||
return self.invoice.company.party
|
||||
|
||||
@cached_property
|
||||
def buyer_trade_address(self):
|
||||
if self.invoice.type == 'out':
|
||||
return self.invoice.invoice_address
|
||||
else:
|
||||
return None
|
||||
|
||||
@cached_property
|
||||
def buyer_trade_tax_identifier(self):
|
||||
if self.invoice.type == 'out':
|
||||
return self.invoice.party_tax_identifier
|
||||
else:
|
||||
return self.invoice.tax_identifier
|
||||
|
||||
@cached_property
|
||||
@has_goods_assets
|
||||
def ship_to_trade_party(self):
|
||||
if self.invoice.type == 'out':
|
||||
if getattr(self.invoice, 'sales', None):
|
||||
sale = self.invoice.sales[0] # XXX
|
||||
if sale.shipment_party != self.buyer_trade_party:
|
||||
return sale.shipment_party
|
||||
else:
|
||||
if getattr(self.invoice, 'purchases', None):
|
||||
purchase = self.invoice.purchases[0] # XXX
|
||||
address = purchase.warehouse.address
|
||||
if (address and address.party != self.buyer_trade_party):
|
||||
return address.party
|
||||
|
||||
@cached_property
|
||||
@has_goods_assets
|
||||
def ship_to_trade_address(self):
|
||||
if self.invoice.type == 'out':
|
||||
if getattr(self.invoice, 'sales', None):
|
||||
sale = self.invoice.sales[0] # XXX
|
||||
if sale.shipment_party != self.buyer_trade_party:
|
||||
return sale.shipment_address
|
||||
else:
|
||||
if getattr(self.invoice, 'purchases', None):
|
||||
purchase = self.invoice.purchases[0] # XXX
|
||||
address = purchase.warehouse.address
|
||||
if (address and address.party != self.buyer_trade_party):
|
||||
return address
|
||||
|
||||
@cached_property
|
||||
@has_goods_assets
|
||||
def ship_from_trade_party(self):
|
||||
if self.invoice.type == 'out':
|
||||
if getattr(self.invoice, 'sales', None):
|
||||
sale = self.invoice.sales[0] # XXX
|
||||
address = sale.warehouse.address
|
||||
if address and address.party != self.seller_trade_party:
|
||||
return address.shipment_party
|
||||
|
||||
@cached_property
|
||||
@has_goods_assets
|
||||
def ship_from_trade_address(self):
|
||||
if self.invoice.type == 'out':
|
||||
if getattr(self.invoice, 'sales', None):
|
||||
sale = self.invoice.sales[0] # XXX
|
||||
address = sale.warehouse.address
|
||||
if address and address.party != self.seller_trade_party:
|
||||
return address
|
||||
|
||||
@cached_property
|
||||
def payment_reference(self):
|
||||
return self.invoice.number
|
||||
|
||||
@classmethod
|
||||
def party_legal_ids(cls, party, address):
|
||||
return []
|
||||
Reference in New Issue
Block a user