barbacompta/tests/test_facturx.py

218 lines
7.7 KiB
Python

# barbacompta - invoicing for dummies
# Copyright (C) 2010-2020 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import io
import xml.etree.ElementTree as ET
import facturx
import pytest
from eo_gestion.eo_facture.facturx import add_facturx_from_bytes, to_pdfa
@pytest.fixture
def fake_invoice_bytes():
with open('tests/fake-invoice.pdf', 'rb') as fd:
return fd.read()
def test_to_pdfa(fake_invoice_bytes):
to_pdfa(fake_invoice_bytes)
def test_add_facturx_from_bytes(fake_invoice_bytes):
facturx_ctx = {
'numero_de_facture': 'F20190001',
'type_facture': '380',
'date_emission': datetime.date(2019, 1, 1),
'chorus_service_code': 'service-code',
'vendeur': 'Entr\'ouvert',
'vendeur_siret': '44317013900036',
'vendeur_tvai': 'FR09491081899',
'client_siret': '1234',
'client_name': 'RGFIPD',
'numero_engagement': '5678',
'numero_marche': 'ABCD',
'montant_ht': '10',
'montant_ttc': '12',
'montant_tva': '2',
'taux_tva': 20.0,
}
facturx_bytes = add_facturx_from_bytes(fake_invoice_bytes, facturx_ctx)
_, xml_str = facturx.get_facturx_xml_from_pdf(io.BytesIO(facturx_bytes))
root = ET.fromstring(xml_str)
def helper(root):
tag = root.tag.split('}')[-1]
if len(root) == 0:
return [tag, root.text or '']
else:
return [tag] + [helper(node) for node in root]
assert helper(root) == [
'CrossIndustryInvoice',
[
'ExchangedDocumentContext',
['BusinessProcessSpecifiedDocumentContextParameter', ['ID', 'A1']],
['GuidelineSpecifiedDocumentContextParameter', ['ID', 'urn:factur-x.eu:1p0:basicwl']],
],
[
'ExchangedDocument',
['ID', 'F20190001'],
['TypeCode', '380'],
['IssueDateTime', ['DateTimeString', '20190101']],
],
[
'SupplyChainTradeTransaction',
[
'ApplicableHeaderTradeAgreement',
['BuyerReference', 'service-code'],
[
'SellerTradeParty',
['Name', "Entr'ouvert"],
['SpecifiedLegalOrganization', ['ID', '44317013900036']],
['PostalTradeAddress', ['CountryID', 'FR']],
['SpecifiedTaxRegistration', ['ID', 'FR09491081899']],
],
[
'BuyerTradeParty',
['Name', 'RGFIPD'],
[
'SpecifiedLegalOrganization',
['ID', '1234'],
],
],
['BuyerOrderReferencedDocument', ['IssuerAssignedID', '5678']],
['ContractReferencedDocument', ['IssuerAssignedID', 'ABCD']],
],
['ApplicableHeaderTradeDelivery', ''],
[
'ApplicableHeaderTradeSettlement',
['InvoiceCurrencyCode', 'EUR'],
[
'ApplicableTradeTax',
['CalculatedAmount', '2'],
['TypeCode', 'VAT'],
['BasisAmount', '10'],
['CategoryCode', 'S'],
['RateApplicablePercent', '20.0'],
],
[
'SpecifiedTradeSettlementHeaderMonetarySummation',
['LineTotalAmount', '10'],
['TaxBasisTotalAmount', '10'],
['TaxTotalAmount', '2'],
['GrandTotalAmount', '12'],
['DuePayableAmount', '12'],
],
],
],
]
def test_add_facturx_from_bytes_facture_avoir(fake_invoice_bytes):
facturx_ctx = {
'numero_de_facture': 'F20190002',
'type_facture': '381',
'date_emission': datetime.date(2019, 1, 1),
'chorus_service_code': 'service-code',
'vendeur': 'Entr\'ouvert',
'vendeur_siret': '44317013900036',
'vendeur_tvai': 'FR09491081899',
'client_siret': '1234',
'client_name': 'RGFIPD',
'numero_engagement': '5678',
'numero_marche': 'ABCD',
'montant_ht': '10',
'montant_ttc': '12',
'montant_tva': '2',
'taux_tva': 20.0,
'annulation_code': 'F20190001',
}
facturx_bytes = add_facturx_from_bytes(fake_invoice_bytes, facturx_ctx)
_, xml_str = facturx.get_facturx_xml_from_pdf(io.BytesIO(facturx_bytes))
root = ET.fromstring(xml_str)
def helper(root):
tag = root.tag.split('}')[-1]
if len(root) == 0:
return [tag, root.text or '']
else:
return [tag] + [helper(node) for node in root]
assert helper(root) == [
'CrossIndustryInvoice',
[
'ExchangedDocumentContext',
['BusinessProcessSpecifiedDocumentContextParameter', ['ID', 'A1']],
['GuidelineSpecifiedDocumentContextParameter', ['ID', 'urn:factur-x.eu:1p0:basicwl']],
],
[
'ExchangedDocument',
['ID', 'F20190002'],
['TypeCode', '381'],
['IssueDateTime', ['DateTimeString', '20190101']],
],
[
'SupplyChainTradeTransaction',
[
'ApplicableHeaderTradeAgreement',
['BuyerReference', 'service-code'],
[
'SellerTradeParty',
['Name', "Entr'ouvert"],
['SpecifiedLegalOrganization', ['ID', '44317013900036']],
['PostalTradeAddress', ['CountryID', 'FR']],
['SpecifiedTaxRegistration', ['ID', 'FR09491081899']],
],
[
'BuyerTradeParty',
['Name', 'RGFIPD'],
[
'SpecifiedLegalOrganization',
['ID', '1234'],
],
],
['BuyerOrderReferencedDocument', ['IssuerAssignedID', '5678']],
['ContractReferencedDocument', ['IssuerAssignedID', 'ABCD']],
],
['ApplicableHeaderTradeDelivery', ''],
[
'ApplicableHeaderTradeSettlement',
['InvoiceCurrencyCode', 'EUR'],
['SpecifiedTradeSettlementPaymentMeans', ['TypeCode', '97']],
[
'ApplicableTradeTax',
['CalculatedAmount', '2'],
['TypeCode', 'VAT'],
['BasisAmount', '10'],
['CategoryCode', 'S'],
['RateApplicablePercent', '20.0'],
],
[
'SpecifiedTradeSettlementHeaderMonetarySummation',
['LineTotalAmount', '10'],
['TaxBasisTotalAmount', '10'],
['TaxTotalAmount', '2'],
['GrandTotalAmount', '12'],
['DuePayableAmount', '12'],
],
['InvoiceReferencedDocument', ['IssuerAssignedID', 'F20190001']],
],
],
]