2022-10-20 12:43:58 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2023-06-30 09:21:48 +00:00
|
|
|
# This file is part of the account-invoice-xrechnung-module
|
|
|
|
# from m-ds for Tryton. The COPYRIGHT file at the top level of
|
|
|
|
# this repository contains the full copyright notices and license terms.
|
2022-10-20 12:43:58 +00:00
|
|
|
|
|
|
|
import zipfile
|
2024-12-05 16:32:56 +00:00
|
|
|
from facturx import generate_from_binary
|
2022-10-20 12:43:58 +00:00
|
|
|
from io import BytesIO
|
2024-12-06 13:04:35 +00:00
|
|
|
from slugify import slugify
|
2022-10-20 12:43:58 +00:00
|
|
|
from trytond.report import Report
|
|
|
|
from trytond.pool import Pool
|
2024-12-06 13:04:35 +00:00
|
|
|
from trytond.exceptions import UserError
|
|
|
|
from trytond.i18n import gettext
|
2024-12-12 11:37:56 +00:00
|
|
|
from trytond.transaction import Transaction
|
2022-10-20 12:43:58 +00:00
|
|
|
from .wizard_runreport import edoc_versions
|
|
|
|
|
|
|
|
|
|
|
|
class XReport(Report):
|
|
|
|
'eDocument Export'
|
|
|
|
__name__ = 'account_invoice_xrechnung.export'
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def compress_as_zip(cls, fname, data):
|
|
|
|
""" compress
|
|
|
|
"""
|
|
|
|
content = BytesIO()
|
|
|
|
with zipfile.ZipFile(content, 'w') as content_zip:
|
|
|
|
content_zip.writestr(fname, data)
|
|
|
|
return content.getvalue()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def execute(cls, ids, data):
|
|
|
|
""" skip export-engine, run edocument-xml-convert
|
|
|
|
"""
|
2024-12-06 13:04:35 +00:00
|
|
|
def export_data(exp_content, fname, ext, data2):
|
|
|
|
""" get tuple to return from report.execute,
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
exp_content (bytes or str): result data of report
|
|
|
|
fname (str): file name
|
|
|
|
ext (str): extension
|
|
|
|
data2 (dict): data
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
tuple: return value of report
|
|
|
|
"""
|
|
|
|
if data2['as_zip'] is True:
|
|
|
|
return (
|
|
|
|
'zip',
|
|
|
|
cls.compress_as_zip(
|
|
|
|
'%(fname)s.%(ext)s' % {
|
|
|
|
'fname': fname, 'ext': ext},
|
|
|
|
exp_content),
|
|
|
|
False,
|
|
|
|
file_name)
|
|
|
|
else:
|
|
|
|
return (ext, exp_content, False, fname)
|
|
|
|
|
2022-10-20 12:43:58 +00:00
|
|
|
pool = Pool()
|
|
|
|
IrDate = pool.get('ir.date')
|
|
|
|
Invoice = pool.get('account.invoice')
|
2024-12-05 16:32:56 +00:00
|
|
|
|
2024-12-06 13:04:35 +00:00
|
|
|
document_para = data['edocument'].split('-')
|
|
|
|
EDocument = pool.get(document_para[0])
|
2024-12-05 16:32:56 +00:00
|
|
|
document_var = document_para[1] if len(document_para) > 1 else None
|
2022-10-20 12:43:58 +00:00
|
|
|
|
|
|
|
invoice, = Invoice.browse([data['invoice']])
|
|
|
|
template = EDocument(invoice)
|
2024-12-05 16:32:56 +00:00
|
|
|
invoice_xml = template.render(edoc_versions[data['edocument']])
|
2022-10-20 12:43:58 +00:00
|
|
|
|
|
|
|
file_name = slugify('%(date)s-%(descr)s' % {
|
2024-11-21 13:34:17 +00:00
|
|
|
'date': IrDate.today().isoformat().replace('-', ''),
|
|
|
|
'descr': invoice.rec_name},
|
|
|
|
max_length=100, word_boundary=True, save_order=True)
|
2022-10-20 12:43:58 +00:00
|
|
|
|
2024-12-06 13:04:35 +00:00
|
|
|
if document_var and (
|
|
|
|
document_var == 'ferd') and (
|
|
|
|
EDocument.__name__ == 'edocument.facturxext.invoice'):
|
|
|
|
# convert to zugferd
|
|
|
|
invoice_pdf = cls.get_zugferd_pdf(invoice, invoice_xml)
|
|
|
|
return export_data(invoice_pdf, file_name, 'pdf', data)
|
2023-06-30 09:21:48 +00:00
|
|
|
else:
|
2024-12-06 13:04:35 +00:00
|
|
|
return export_data(invoice_xml, file_name, 'xml', data)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_used_report(cls):
|
|
|
|
""" get report to use from config
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
UserError: if not report was found
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
record: ir.action.report
|
|
|
|
"""
|
|
|
|
pool = Pool()
|
|
|
|
Configuration = pool.get('account.configuration')
|
|
|
|
ActionReport = pool.get('ir.action.report')
|
|
|
|
|
|
|
|
cfg1 = Configuration.get_singleton()
|
|
|
|
act_report = None
|
|
|
|
if cfg1 and cfg1.xrechn_zugferd_report:
|
|
|
|
act_report = cfg1.xrechn_zugferd_report
|
|
|
|
else:
|
|
|
|
# no report defined, use 1st found
|
|
|
|
act_report = ActionReport.search([
|
|
|
|
('model', '=', 'account.invoice'),
|
2025-01-27 07:35:30 +00:00
|
|
|
('extension', '=', 'pdf')], limit=1)
|
2024-12-06 13:04:35 +00:00
|
|
|
if act_report:
|
|
|
|
act_report = act_report[0]
|
|
|
|
if not act_report:
|
|
|
|
raise UserError(gettext(
|
|
|
|
'account_invoice_xrechnung.msg_no_report_found'))
|
|
|
|
return act_report
|
|
|
|
|
2024-12-12 11:37:56 +00:00
|
|
|
@classmethod
|
|
|
|
def generate_pdf_data(cls, records):
|
|
|
|
""" generate pdf of invoice using defined report
|
|
|
|
|
|
|
|
Args:
|
|
|
|
records (list): records of account.invoice
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
list: dicts - {'content': <pdf-data>, 'fname': 'file-name'}
|
|
|
|
"""
|
|
|
|
report_action = cls.get_used_report()
|
|
|
|
RepInvoice = Pool().get(report_action.report_name, type='report')
|
|
|
|
|
|
|
|
# run selected report on invoices w/o stored report-data
|
|
|
|
data2 = {}
|
|
|
|
data2['action_id'] = report_action.id
|
|
|
|
data2['model'] = report_action.model
|
|
|
|
result = []
|
|
|
|
for record in records:
|
|
|
|
data2['id'] = record.id
|
|
|
|
data2['ids'] = [record.id]
|
|
|
|
|
|
|
|
with Transaction().set_context({'with_rec_name': False}):
|
|
|
|
(ext, content, print1, fname) = RepInvoice.execute(
|
|
|
|
[record.id], data2)
|
|
|
|
result.append({
|
|
|
|
'content': content,
|
|
|
|
'fname': fname})
|
|
|
|
return result
|
|
|
|
|
2024-12-06 13:04:35 +00:00
|
|
|
@classmethod
|
|
|
|
def get_zugferd_pdf(cls, invoice, invoice_xml):
|
|
|
|
""" generate ZugFeRD-PDF
|
|
|
|
|
|
|
|
Args:
|
|
|
|
invoice (record): model account.invoice
|
|
|
|
invoice_xml (str): xml-data
|
|
|
|
"""
|
2024-12-12 11:37:56 +00:00
|
|
|
# pdf was already stored to db, must be pdf
|
|
|
|
if invoice.invoice_report_cache:
|
|
|
|
if invoice.invoice_report_format != 'pdf':
|
|
|
|
raise UserError(gettext(
|
|
|
|
'account_invoice_xrechnung.msg_invalid_cachecontent',
|
|
|
|
invoice_name=invoice.rec_name))
|
|
|
|
pdf_data = invoice.invoice_report_cache
|
|
|
|
else:
|
|
|
|
# run report, could fail because we are in
|
|
|
|
# readonly-transaction
|
|
|
|
report_data = cls.generate_pdf_data([invoice])
|
|
|
|
if report_data:
|
|
|
|
pdf_data = report_data[0]['content']
|
2024-12-06 13:04:35 +00:00
|
|
|
|
|
|
|
zugferd_pdf = generate_from_binary(
|
2024-12-12 11:37:56 +00:00
|
|
|
pdf_file=pdf_data,
|
2024-12-06 13:04:35 +00:00
|
|
|
xml=invoice_xml,
|
|
|
|
check_xsd=True,
|
|
|
|
pdf_metadata={
|
|
|
|
'author': invoice.company.rec_name,
|
|
|
|
'keywords': 'Factur-X, Invoice, Tryton',
|
|
|
|
'title': invoice.number,
|
|
|
|
'subject': invoice.description},
|
|
|
|
lang='de-DE')
|
|
|
|
return zugferd_pdf
|
2022-10-20 12:43:58 +00:00
|
|
|
|
|
|
|
# end XReport
|