first commit

This commit is contained in:
root
2026-03-14 09:42:12 +00:00
commit 0adbd20c2c
10991 changed files with 1646955 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

View File

@@ -0,0 +1,256 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from base64 import b64decode, b64encode
from functools import wraps
from io import BytesIO
from urllib.parse import urljoin
import requests
from lxml import etree
from trytond.i18n import gettext
from trytond.model import fields
from trytond.modules.edocument_peppol.exceptions import PeppolServiceError
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from trytond.transaction import Transaction
from .exceptions import PeppyrusCredentialWarning, PeppyrusError
URLS = {
'testing': 'https://api.test.peppyrus.be/v1/',
'production': 'https://api.peppyrus.be/v1/',
}
UBL_NAMESPACES = {
'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2',
'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2',
}
BIS_BILLING_3 = (
'urn:cen.eu:en16931:2017#compliant'
'#urn:fdc:peppol.eu:2017:poacc:billing:3.0')
def peppyrus_api(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except requests.HTTPError as e:
error_message = e.response.text or e.args[0]
raise PeppyrusError(
gettext('edocument_peppol_peppyrus'
'.msg_peppyrus_webserver_error',
message=error_message)) from e
return wrapper
class PeppolService(metaclass=PoolMeta):
__name__ = 'edocument.peppol.service'
peppyrus_api_key = fields.Char(
"API Key",
states={
'invisible': Eval('service') != 'peppyrus',
'required': Eval('service') == 'peppyrus',
})
peppyrus_server = fields.Selection([
(None, ""),
('testing', "Testing"),
('production', "Production"),
], "Server",
states={
'invisible': Eval('service') != 'peppyrus',
'required': Eval('service') == 'peppyrus',
})
@classmethod
def __setup__(cls):
super().__setup__()
cls.service.selection.append(('peppyrus', "Peppyrus"))
@fields.depends('service', 'peppyrus_server')
def on_change_service(self):
if (self.service == 'peppyrus'
and not self.peppyrus_server):
self.peppyrus_server = 'testing'
@peppyrus_api
def _post_peppyrus(self, document):
tree = etree.parse(BytesIO(document.data))
response = requests.post(
urljoin(URLS[self.peppyrus_server], 'message'),
json={
'sender': self._peppyrus_sender(document.type, tree),
'recipient': self._peppyrus_recipient(document.type, tree),
'processType': (
self._peppyrus_process_type(document.type, tree)),
'documentType': (
self._peppyrus_document_type(document.type, tree)),
'fileContent': b64encode(document.data).decode(),
},
headers={
'Accept': 'application/json',
'X-Api-Key': self.peppyrus_api_key,
})
if response.status_code == 422:
raise PeppolServiceError(response.json())
response.raise_for_status()
return response.json()['id']
def _peppyrus_sender(self, type, tree):
if type == 'bis-billing-3':
el = tree.find(
'.//{*}AccountingSupplierParty/{*}Party/{*}EndpointID')
if el is not None:
scheme = el.get('schemeID', '')
return f'{scheme}:{el.text}'
def _peppyrus_recipient(self, type, tree):
if type == 'bis-billing-3':
el = tree.find(
'.//{*}AccountingCustomerParty/{*}Party/{*}EndpointID')
if el is not None:
scheme = el.get('schemeID', '')
return f'{scheme}:{el.text}'
def _peppyrus_process_type(self, type, tree):
if type == 'bis-billing-3':
return 'cenbii-procid-ubl::' + tree.findtext('.//{*}ProfileID')
def _peppyrus_document_type(self, type, tree):
root = tree.getroot()
namespace = root.nsmap.get(root.prefix)
if type == 'bis-billing-3':
return (
'busdox-docid-qns::'
f'{namespace}::{etree.QName(root).localname}##'
+ tree.findtext('.//{*}CustomizationID') + '::2.1')
@peppyrus_api
def _update_status_peppyrus(self, document):
assert document.direction == 'out'
response = requests.get(
urljoin(
URLS[self.peppyrus_server],
f'message/{document.transmission_id}'),
headers={
'Accept': 'application/json',
'X-Api-Key': self.peppyrus_api_key,
})
response.raise_for_status()
message = response.json()
assert message['id'] == document.transmission_id
if message['folder'] == 'sent':
document.succeed()
elif message['folder'] == 'failed':
document.fail(status=self._peppyrus_status(document))
@peppyrus_api
def _peppyrus_status(self, document):
response = requests.get(
urljoin(
URLS[self.peppyrus_server],
f'message/{document.transmission_id}/report'),
headers={
'Accept': 'application/json',
'X-Api-Key': self.peppyrus_api_key,
})
response.raise_for_status()
report = response.json()
return report.get('transmissionRules')
@classmethod
def peppyrus_fetch(cls, services=None):
if services is None:
services = cls.search([('service', '=', 'peppyrus')])
for service in services:
service._peppyrus_fetch()
@peppyrus_api
def _peppyrus_fetch(self):
assert self.service == 'peppyrus'
response = requests.get(
urljoin(URLS[self.peppyrus_server], 'message/list'),
params={
'folder': 'INBOX',
'confirmed': 'false',
'perPage': 100,
},
headers={
'Accept': 'application/json',
'X-Api-Key': self.peppyrus_api_key,
})
response.raise_for_status()
for message in response.json()['items']:
self.__class__.__queue__.peppyrus_store(self, message)
def peppyrus_store(self, message):
pool = Pool()
Document = pool.get('edocument.peppol')
transaction = Transaction()
assert message['direction'] == 'IN'
def confirm(server, api_key, message, silent=False):
try:
response = requests.patch(
urljoin(
URLS[server],
f"message/{message['id']}/confirm"),
headers={
'Accept': 'application/json',
'X-Api-Key': api_key,
})
if response.status_code != 404:
response.raise_for_status()
except Exception:
if not silent:
raise
server = self.peppyrus_server
api_key = self.peppyrus_api_key
if Document.search([
('service', '=', self.id),
('transmission_id', '=', message['id']),
]):
confirm(server, api_key, message)
return
document = Document(
direction='in',
company=self.company,
type=self._peppyrus_type(message['documentType']),
service=self,
data=b64decode(message['fileContent']),
transmission_id=message['id'],
)
document.save()
document.submit()
transaction.atexit(confirm, server, api_key, message, True)
@classmethod
def _peppyrus_type(cls, document_type):
schema, document_identifier = document_type.split('::', 1)
syntax, customization = document_identifier.split('##', 1)
namespace, localname = syntax.split('::', 1)
customization_id, version = customization.split('::', 1)
if (schema in {'busdox-docid-qns', 'peppol-doctype-wildcard'}
and namespace in UBL_NAMESPACES
and customization_id in BIS_BILLING_3):
return 'bis-billing-3'
@classmethod
def check_modification(cls, mode, services, values=None, external=False):
pool = Pool()
Warning = pool.get('res.user.warning')
super().check_modification(
mode, services, values=values, external=external)
if mode == 'write' and external and 'peppyrus_api_key' in values:
warning_name = Warning.format('peppyrus_credential', services)
if Warning.check(warning_name):
raise PeppyrusCredentialWarning(
warning_name,
gettext('edocument_peppol_peppyrus'
'.msg_peppyrus_credential_modified'))

View File

@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="edocument_peppol_service_view_form">
<field name="model">edocument.peppol.service</field>
<field name="inherit" ref="edocument_peppol.edocument_peppol_service_view_form"/>
<field name="name">edocument_peppol_service_form</field>
</record>
</data>
<data noupdate="1">
<record model="ir.cron" id="cron_edocument_peppol_service_peppyrus_fetch">
<field name="method">edocument.peppol.service|peppyrus_fetch</field>
<field name="interval_number" eval="1"/>
<field name="interval_type">hours</field>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,12 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.exceptions import UserError, UserWarning
class PeppyrusError(UserError):
pass
class PeppyrusCredentialWarning(UserWarning):
pass

View File

@@ -0,0 +1,16 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.pool import PoolMeta
class Cron(metaclass=PoolMeta):
__name__ = 'ir.cron'
@classmethod
def __setup__(cls):
super().__setup__()
cls.method.selection.extend([
('edocument.peppol.service|peppyrus_fetch',
"Fetch Peppyrus messages"),
])

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,41 @@
#
#, fuzzy
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr "Clau API"
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr "Servidor"
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr "Esteu segurs que voleu modificar les credencials de Peppyrus?"
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
"Ha fallat la crida al servei web de Peppyrus amb el següent missatge d'error:\n"
"%(message)s"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr "Producció"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr "Proves"
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr "Peppyrus"
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr "Obtenir missatges de Peppyrus"

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,41 @@
#
#, fuzzy
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr "API-Schlüssel"
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr "Server"
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr "Sind Sie sicher, dass Sie die Peppyrus-Anmeldedaten ändern möchten?"
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
"Der Aufruf des Peppyrus-Webdienstes ist mit der folgenden Fehlermeldung fehlgeschlagen:\n"
"%(message)s"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr "Produktivumgebung"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr "Testumgebung"
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr "Peppyrus"
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr "Peppyrus-Nachrichten abrufen"

View File

@@ -0,0 +1,41 @@
#
#, fuzzy
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr "Clave API"
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr "Servidor"
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr "Estas seguro que quieres modificar las credenciales de Peppyrus?"
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
"Ha fallado la llamada al servicio web de Peppyrus con el siguiente mensaje de error: \n"
"%(message)s"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr "Producción"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr "Pruebas"
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr "Peppyrus"
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr "Obtener mensajes de Peppyrus"

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,41 @@
#
#, fuzzy
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr "Clé API"
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr "Serveur"
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr "Êtes-vous sûr de vouloir modifier les identifiants Peppyrus ?"
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
"L'appel au service web Peppyrus a échoué avec le message d'erreur suivant :\n"
"%(message)s"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr "Production"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr "Essai"
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr "Peppyrus"
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr "Récupérer les messages Peppyrus"

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,41 @@
#
#, fuzzy
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr "API sleutel"
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr "Server"
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr "Weet u zeker dat u de Peppyrus-inloggegevens wilt wijzigen?"
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
"De aanvraag naar de Peppyrus-webservice is mislukt met de volgende foutmelding:\n"
"%(message)s"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr "Productie"
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr "Testen"
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr "Peppyrus"
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr "Haal Peppyrus-berichten op"

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,38 @@
#
msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:edocument.peppol.service,peppyrus_api_key:"
msgid "API Key"
msgstr ""
msgctxt "field:edocument.peppol.service,peppyrus_server:"
msgid "Server"
msgstr ""
msgctxt "model:ir.message,text:msg_peppyrus_credential_modified"
msgid "Are you sure you want to modify Peppyrus credentials?"
msgstr ""
#, python-format
msgctxt "model:ir.message,text:msg_peppyrus_webserver_error"
msgid ""
"Peppyrus webservice call failed with the following error message:\n"
"%(message)s"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Production"
msgstr ""
msgctxt "selection:edocument.peppol.service,peppyrus_server:"
msgid "Testing"
msgstr ""
msgctxt "selection:edocument.peppol.service,service:"
msgid "Peppyrus"
msgstr ""
msgctxt "selection:ir.cron,method:"
msgid "Fetch Peppyrus messages"
msgstr ""

View File

@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data grouped="1">
<record model="ir.message" id="msg_peppyrus_credential_modified">
<field name="text">Are you sure you want to modify Peppyrus credentials?</field>
</record>
<record model="ir.message" id="msg_peppyrus_webserver_error">
<field name="text">Peppyrus webservice call failed with the following error message:
%(message)s</field>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,2 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>123</cbc:ID>
<cbc:IssueDate>2026-01-01</cbc:IssueDate>
<cbc:DueDate>2026-01-01</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:OrderReference>
<cbc:ID>NA</cbc:ID>
</cac:OrderReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="PARTICIPANT_SCHEME">PARTICIPANT_ID</cbc:EndpointID>
<cac:PostalAddress>
<cac:Country>
<cbc:IdentificationCode>BE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>PARTICIPANT_VAT</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Supplier</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="PARTICIPANT_SCHEME">PARTICIPANT_ID</cbc:EndpointID>
<cac:PostalAddress>
<cac:Country>
<cbc:IdentificationCode>BE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>PARTICIPANT_VAT</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Customer</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">10</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">100</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">10</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>10</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">100</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">100</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">110</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="EUR">110</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="C62">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">100</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Product</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>10</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">100</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>

View File

@@ -0,0 +1,84 @@
==================================
EDocument Peppol Peppyrus Scenario
==================================
Imports::
>>> import os
>>> import time
>>> from unittest.mock import patch
>>> from proteus import Model
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.modules.edocument_peppol.edocument import Peppol
>>> from trytond.tests.tools import activate_modules
>>> from trytond.tools import file_open
>>> FETCH_SLEEP, MAX_SLEEP = 1, 200
Patch Peppol::
>>> _validate = patch.object(Peppol, '_validate').start()
>>> render = patch.object(Peppol, 'render').start()
>>> participant_scheme, participant_id = (
... os.getenv('PEPPYRUS_PARTICIPANT_ID').split(':', 1))
>>> participant_vat = os.getenv('PEPPYRUS_PARTICIPANT_VAT')
>>> with file_open('edocument_peppol_peppyrus/tests/invoice.xml', mode='r') as fp:
... render.return_value = (
... fp.read()
... .replace('PARTICIPANT_SCHEME', participant_scheme)
... .replace('PARTICIPANT_ID', participant_id)
... .replace('PARTICIPANT_VAT', participant_vat.upper())
... .encode())
Activate modules::
>>> config = activate_modules('edocument_peppol_peppyrus', create_company)
>>> Cron = Model.get('ir.cron')
>>> Peppol = Model.get('edocument.peppol')
>>> PeppolService = Model.get('edocument.peppol.service')
Create a service::
>>> peppol_service = PeppolService(sequence=1)
>>> peppol_service.types = ['bis-billing-3']
>>> peppol_service.service = 'peppyrus'
>>> peppol_service.peppyrus_server = 'testing'
>>> peppol_service.peppyrus_api_key = os.getenv('PEPPYRUS_API_KEY')
>>> peppol_service.save()
Send out a Peppol document::
>>> peppol = Peppol(direction='out')
>>> peppol.type = 'bis-billing-3'
>>> peppol.service = peppol_service
>>> peppol.click('submit')
>>> peppol.state
'processing'
>>> bool(peppol.transmission_id)
True
Check Peppol status::
>>> cron, = Cron.find([
... ('method', '=', 'edocument.peppol|update_status'),
... ], limit=1)
>>> while peppol.state == 'processing':
... cron.click('run_once')
... peppol.reload()
>>> peppol.state
'succeeded'
Fetch messages::
>>> for _ in range(MAX_SLEEP):
... cron, = Cron.find([
... ('method', '=', 'edocument.peppol.service|peppyrus_fetch'),
... ], limit=1)
... cron.click('run_once')
... if bool(Peppol.find([('direction', '=', 'in')])):
... break
... time.sleep(FETCH_SLEEP)
>>> bool(Peppol.find([('direction', '=', 'in')]))
True

View File

@@ -0,0 +1,11 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.tests.test_tryton import ModuleTestCase
class EdocumentPeppolPeppyrusTestCase(ModuleTestCase):
"Test Edocument Peppol Peppyrus module"
module = 'edocument_peppol_peppyrus'
del ModuleTestCase

View File

@@ -0,0 +1,17 @@
# 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 os
from trytond.tests.test_tryton import TEST_NETWORK, load_doc_tests
def load_tests(*args, **kwargs):
if (not TEST_NETWORK
or not (os.getenv('PEPPYRUS_API_KEY')
and os.getenv('PEPPYRUS_PARTICIPANT_ID')
and os.getenv('PEPPYRUS_PARTICIPANT_VAT'))):
kwargs.setdefault('skips', set()).update({
'scenario_edocument_peppol_peppyrus.rst',
})
return load_doc_tests(__name__, __file__, *args, **kwargs)

View File

@@ -0,0 +1,13 @@
[tryton]
version=7.8.3
depends:
ir
edocument_peppol
xml:
edocument.xml
message.xml
[register]
model:
ir.Cron
edocument.PeppolService

View File

@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="//label[@name='types']" position="before">
<newline/>
<label name="peppyrus_server"/>
<field name="peppyrus_server"/>
<label name="peppyrus_api_key"/>
<field name="peppyrus_api_key"/>
<newline/>
</xpath>
</data>