Encapsulates different methods to be able to perform a better inheritance in other modules

This commit is contained in:
Carlos G?lvez 2018-09-13 13:21:30 +02:00
parent 12084c44a3
commit 4dada49b1c
5 changed files with 151 additions and 74 deletions

View File

@ -18,6 +18,7 @@ from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval
from trytond.transaction import Transaction
from trytond.wizard import Wizard, StateView, StateTransition, Button
from trytond import backend
__all__ = ['Invoice', 'InvoiceLine', 'CreditInvoiceStart', 'CreditInvoice',
'GenerateFacturaeStart', 'GenerateFacturae']
@ -107,6 +108,9 @@ FACe_REQUIRED_FIELDS = ['facturae_person_type', 'facturae_residence_type']
_slugify_strip_re = re.compile(r'[^\w\s-]')
_slugify_hyphenate_re = re.compile(r'[-\s]+')
DEFAULT_FACTURAE_TEMPLATE = 'template_facturae_3.2.1.xml'
DEFAULT_FACTURAE_SCHEMA = 'Facturaev3_2_1-offline.xsd'
def slugify(value):
if not isinstance(value, unicode):
@ -258,20 +262,37 @@ class Invoice:
@classmethod
def generate_facturae_default(cls, invoices, certificate_password):
to_save = []
to_write = ([],)
for invoice in invoices:
if invoice.invoice_facturae:
continue
facturae_content = invoice.get_facturae()
invoice._validate_facturae(facturae_content)
invoice.invoice_facturae = invoice._sign_facturae(
facturae_content, certificate_password)
to_save.append(invoice)
if to_save:
cls.save(to_save)
if backend.name() != 'sqlite':
invoice_facturae = invoice._sign_facturae(
facturae_content, certificate_password)
else:
invoice_facturae = facturae_content
to_write[0].append(invoice)
to_write += ({'invoice_facturae': invoice_facturae},)
if to_write:
cls.write(*to_write)
def get_facturae(self):
"""Return the content of factura-e XML file"""
jinja_env = Environment(
loader=FileSystemLoader(module_path()),
trim_blocks=True,
lstrip_blocks=True,
)
template = DEFAULT_FACTURAE_TEMPLATE
return self._get_jinja_template(jinja_env, template).render(
self._get_content_to_render(), ).encode('utf-8')
def _get_jinja_template(self, jinja_env, template):
return jinja_env.get_template(template)
def _get_content_to_render(self):
"""Return the content to render in factura-e XML file"""
pool = Pool()
Currency = pool.get('currency.currency')
Date = pool.get('ir.date')
@ -306,6 +327,7 @@ class Invoice:
or len(self.company.party.vat_code) > 30):
self.raise_user_error('company_vat_identifier',
(self.company.party.rec_name,))
if (not self.company.party.addresses
or not self.company.party.addresses[0].street
or not self.company.party.addresses[0].zip
@ -398,34 +420,29 @@ class Invoice:
'invoice': self.rec_name,
})
jinja_env = Environment(
loader=FileSystemLoader(module_path()),
trim_blocks=True,
lstrip_blocks=True,
)
jinja_template = jinja_env.get_template('template_facturae_3.2.1.xml')
return jinja_template.render({
return {
'invoice': self,
'Decimal': Decimal,
'Currency': Currency,
'euro': euro,
'exchange_rate': exchange_rate,
'exchange_rate_date': exchange_rate_date,
'UOM_CODE2TYPE': UOM_CODE2TYPE,
}, ).encode('utf-8')
}
def _validate_facturae(self, xml_string):
def _validate_facturae(self, xml_string, schema_file_path=None):
"""
Inspired by https://github.com/pedrobaeza/l10n-spain/blob/d01d049934db55130471e284012be7c860d987eb/l10n_es_facturae/wizard/create_facturae.py
"""
logger = logging.getLogger('account_invoice_facturae')
schema_file_path = os.path.join(
module_path(),
'Facturaev3_2_1-offline.xsd')
if not schema_file_path:
schema_file_path = os.path.join(
module_path(),
DEFAULT_FACTURAE_SCHEMA)
with open(schema_file_path) as schema_file:
facturae_schema = etree.XMLSchema(file=schema_file)
logger.debug("Schema Facturaev3_2_1-offline.xsd loaded")
logger.debug("%s loaded" % schema_file_path)
try:
facturae_schema.assertValid(etree.fromstring(xml_string))
@ -435,7 +452,8 @@ class Invoice:
logger.warning("Error validating generated Factura-e file",
exc_info=True)
logger.debug(xml_string)
self.raise_user_error('invalid_factura_xml_file', (self.rec_name, e))
self.raise_user_error('invalid_factura_xml_file',
(self.rec_name, e))
return True
def _sign_facturae(self, xml_string, certificate_password):

View File

@ -49,6 +49,7 @@
<field name="wiz_name">account.invoice.generate_facturae</field>
<field name="model">account.invoice</field>
</record>
<!--<record model="ir.action.keyword" id="generate_signed_facturae_keyword">
<field name="keyword">form_action</field>
<field name="model">account.invoice,-1</field>

View File

@ -29,7 +29,7 @@
<EquivalentInEuros>{{ Currency.compute(invoice.currency, invoice.total_amount, euro) }}</EquivalentInEuros>
{% endif %}
</TotalExecutableAmount>
<InvoiceCurrencyCode>{{ invoice.currency.code }}</InvoiceCurrencyCode>
<InvoiceCurrencyCode>{{ invoice.currency.code.upper() }}</InvoiceCurrencyCode>
</Batch>
{# FactoryAssignmentData optional: not supported (factoring not supported) #}
</FileHeader>
@ -158,11 +158,11 @@
{# OperationDate required only if is different to IssueDate, but we consider OperatinDate==invoice_date: not supported #}
{# PlaceOfIssue optional: not supported #}
{# InvoicingPeriod required only for Recapitulativas or temporary service: not supported #}
<InvoiceCurrencyCode>{{ invoice.currency.code }}</InvoiceCurrencyCode>
<InvoiceCurrencyCode>{{ invoice.currency.code.upper() }}</InvoiceCurrencyCode>
{% if invoice.currency != euro %}
<ExchangeRateDetails>
<ExchangeRate>{{ exchange_rate_date }}</ExchangeRate>
<ExchangeRateDate>{{ exchange_rate_date.isoformat() }}</ExchangeRateDate>
<ExchangeRate>{{ exchange_rate }}</ExchangeRate>
<ExchangeRateDate>{{ exchange_rate_date }}</ExchangeRateDate>
</ExchangeRateDetails>
{% endif %}
<TaxCurrencyCode>EUR</TaxCurrencyCode>
@ -255,7 +255,7 @@
- TransactionDate
- Extensions
#}
<ItemDescription>{{ line.description[:2500] }}</ItemDescription>
<ItemDescription>{{ line.description and line.description[:2500] or '' }}</ItemDescription>
<Quantity>{{ line.quantity }}</Quantity>
<UnitOfMeasure>{{ UOM_CODE2TYPE.get(line.unit.symbol, '05') if line.unit else '05' }}</UnitOfMeasure>
<UnitPriceWithoutTax>{{ line.unit_price }}</UnitPriceWithoutTax>

View File

@ -29,7 +29,7 @@
{% else %}
<OverseasAddress>
<Address>{{ address.street[:80] }}</Address>
<PostCodeAndTown>{{ (', '.join(address.zip, address.city))[:50] }}</PostCode>
<PostCodeAndTown>{{ (', '.join([address.zip, address.city]))[:50] }}</PostCodeAndTown>
<Province>{{ address.subdivision.name[:20] }}</Province>
<CountryCode>{{ address.country.code3 }}</CountryCode>
</OverseasAddress>

View File

@ -6,12 +6,12 @@ import unittest
from decimal import Decimal
import trytond.tests.test_tryton
from trytond.pool import Pool
from trytond.transaction import Transaction
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.modules.account.tests import get_fiscalyear
from trytond.modules.account_es.tests import create_chart
from trytond.modules.account.tests import get_fiscalyear, create_chart
from trytond.modules.company.tests import create_company, set_company
from trytond.modules.account_invoice.tests import set_invoice_sequences
from trytond.modules.currency.tests import create_currency, add_currency_rate
CURRENT_PATH = os.path.dirname(os.path.abspath(__file__))
@ -22,31 +22,49 @@ class TestAccountInvoiceFacturaeCase(ModuleTestCase):
@with_transaction()
def test_invoice_generation(self):
'Test invoice generation'
pool = Pool()
Account = pool.get('account.account')
FiscalYear = pool.get('account.fiscalyear')
GenerateSignedFacturae = pool.get('account.invoice.generate_facturae',
type='wizard')
Invoice = pool.get('account.invoice')
InvoiceLine = pool.get('account.invoice.line')
ModelData = pool.get('ir.model.data')
Party = pool.get('party.party')
PaymentTerm = pool.get('account.invoice.payment_term')
ProductUom = pool.get('product.uom')
ProductTemplate = pool.get('product.template')
Product = pool.get('product.product')
Tax = pool.get('account.tax')
Address = pool.get('party.address')
PartyIdentifier = pool.get('party.identifier')
Country = pool.get('country.country')
Subdivision = pool.get('country.subdivision')
PaymentType = pool.get('account.payment.type')
revenue_template_id = ModelData.get_id('account_es', 'pgc_7000_child')
expense_template_id = ModelData.get_id('account_es', 'pgc_600_child')
vat21_template_id = ModelData.get_id('account_es', 'iva_rep_21')
country = Country(name='Country', code='ES', code3='ESP')
country.save()
subdivision = Subdivision(name='Subdivision', country=country,
code='SUB', type='area')
subdivision.save()
company = create_company()
currency = create_currency('EUR')
add_currency_rate(currency, Decimal(1.0))
tax_identifier = PartyIdentifier()
tax_identifier.type = 'eu_vat'
tax_identifier.code = 'BE0897290877'
company.header = 'Report Header'
company.party.identifiers = [tax_identifier]
company.party.facturae_person_type = 'J'
company.party.facturae_residence_type = 'R'
company.party.name = 'Seller'
company.party.save()
company.save()
# Save certificate into company
with open(os.path.join(
CURRENT_PATH, 'certificate.pfx'), 'rb') as cert_file:
company.facturae_certificate = cert_file.read()
company.save()
payment_term, = PaymentTerm.create([{
'name': '20 days, 40 days',
@ -79,13 +97,55 @@ class TestAccountInvoiceFacturaeCase(ModuleTestCase):
fiscalyear.save()
FiscalYear.create_period([fiscalyear])
revenue, = Account.search([('template', '=', revenue_template_id)])
expense, = Account.search([('template', '=', expense_template_id)])
vat21, = Tax.search([('template', '=', vat21_template_id)])
payment_receivable, = PaymentType.create([{
'name': 'Payment Receivable',
'kind': 'receivable',
'company': company.id,
'facturae_type': '01',
}])
revenue, = Account.search([('kind', '=', 'revenue')])
expense, = Account.search([('kind', '=', 'expense')])
tax_account, = Account.search([
('name', '=', 'Main Tax'),
])
with Transaction().set_user(0):
vat21 = Tax()
vat21.name = vat21.description = '21% VAT'
vat21.type = 'percentage'
vat21.rate = Decimal('0.21')
vat21.invoice_account = tax_account
vat21.report_type = '05'
vat21.credit_note_account = tax_account
party = Party(name='Party')
vat21.save()
company_address, = company.party.addresses
company_address.street = 'street'
company_address.zip = '08201'
company_address.city = 'City'
company_address.subdivision = subdivision
company_address.country = country
company_address.save()
party = Party(name='Buyer')
party.facturae_person_type = 'J'
party.facturae_residence_type = 'R'
tax_identifier = PartyIdentifier()
tax_identifier.type = 'eu_vat'
tax_identifier.code = 'BE0897290877'
party.identifiers = [tax_identifier]
party.save()
address_dict = {
'party': party.id,
'street': 'St sample, 15',
'city': 'City',
'zip': '08201',
'subdivision': subdivision.id,
'country': country.id,
}
address, = Address.create([address_dict])
term, = PaymentTerm.create([{
'name': 'Payment term',
'lines': [
@ -116,43 +176,41 @@ class TestAccountInvoiceFacturaeCase(ModuleTestCase):
product.template = template
product.save()
invoice = Invoice()
invoice.type = 'out'
invoice.on_change_type()
invoice.party = party
invoice.on_change_party()
invoice.payment_term = term
with Transaction().set_user(0):
invoice = Invoice()
invoice.type = 'out'
invoice.on_change_type()
invoice.party = party
invoice.on_change_party()
invoice.payment_type = payment_receivable
invoice.payment_term = term
invoice.currency = currency
invoice.company = company
line1 = InvoiceLine()
line1.product = product
line1.on_change_product()
line1.on_change_account()
line1.quantity = 5
line1.unit_price = Decimal('40')
line1 = InvoiceLine()
line1.account = revenue
line1.product = product
line1.on_change_product()
line1.on_change_account()
line1.description = 'TestLine2'
line1.quantity = 5
line1.unit_price = Decimal('40')
line2 = InvoiceLine()
line2.account = revenue
line2.on_change_account()
line2.description = 'Test'
line2.quantity = 1
line2.unit_price = Decimal(20)
line2 = InvoiceLine()
line2.account = revenue
line2.product = product
line2.on_change_product()
line2.on_change_account()
line2.description = 'TestLine2'
line2.quantity = 1
line2.unit_price = Decimal(20)
invoice.lines = [line1, line2]
invoice.on_change_lines()
invoice.save()
# invoice.untaxed_amount == Decimal('220.00')
# invoice.tax_amount == Decimal('20.00')
# invoice.total_amount == Decimal('240.00')
invoice.lines = [line1, line2]
invoice.on_change_lines()
invoice.save()
Invoice.post([invoice])
session_id, _, _ = GenerateSignedFacturae.create()
generate_signed_facturae = GenerateSignedFacturae(session_id)
generate_signed_facturae.account.certificate_password = (
'privatepassword')
generate_signed_facturae.transition_generate()
self.assertIsNotNone(invoice.invoice_facturae)
Invoice.generate_facturae_default([invoice], 'privatepassword')
def suite():