factur-x 3.16.dev0__tar.gz → 4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. {factur_x-3.16.dev0 → factur_x-4.0}/PKG-INFO +11 -2
  2. {factur_x-3.16.dev0 → factur_x-4.0}/README.rst +9 -1
  3. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/__init__.py +2 -5
  4. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/facturx.py +203 -57
  5. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/scripts/pdfextractxml.py +10 -6
  6. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/scripts/pdfgen.py +12 -6
  7. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/scripts/xmlcheck.py +11 -6
  8. factur_x-4.0/facturx/xsd/facturx-basic/Factur-X_1.08_BASIC-compiled.xsl +9175 -0
  9. factur_x-4.0/facturx/xsd/facturx-basic/Factur-X_1.08_BASIC_codedb.xml +4866 -0
  10. factur_x-4.0/facturx/xsd/facturx-basicwl/Factur-X_1.08_BASICWL-compiled.xsl +6581 -0
  11. factur_x-4.0/facturx/xsd/facturx-basicwl/Factur-X_1.08_BASICWL_codedb.xml +2260 -0
  12. factur_x-4.0/facturx/xsd/facturx-en16931/Factur-X_1.08_EN16931-compiled.xsl +11895 -0
  13. factur_x-4.0/facturx/xsd/facturx-en16931/Factur-X_1.08_EN16931_codedb.xml +5897 -0
  14. factur_x-4.0/facturx/xsd/facturx-extended/Factur-X_1.08_EXTENDED-compiled.xsl +26245 -0
  15. factur_x-4.0/facturx/xsd/facturx-extended/Factur-X_1.08_EXTENDED_codedb.xml +7113 -0
  16. factur_x-4.0/facturx/xsd/facturx-minimum/Factur-X_1.08_MINIMUM-compiled.xsl +1939 -0
  17. factur_x-4.0/facturx/xsd/facturx-minimum/Factur-X_1.08_MINIMUM_codedb.xml +1111 -0
  18. factur_x-4.0/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B-compiled.xsl +2931 -0
  19. factur_x-4.0/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B-compiled.xsl +4364 -0
  20. factur_x-4.0/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B-compiled.xsl +6855 -0
  21. {factur_x-3.16.dev0 → factur_x-4.0}/requirements.txt +1 -0
  22. {factur_x-3.16.dev0 → factur_x-4.0}/.flake8 +0 -0
  23. {factur_x-3.16.dev0 → factur_x-4.0}/.gitignore +0 -0
  24. {factur_x-3.16.dev0 → factur_x-4.0}/LICENSE +0 -0
  25. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/scripts/__init__.py +0 -0
  26. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/scripts/webservice.py +0 -0
  27. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xmp/Factur-X_extension_schema.xmp +0 -0
  28. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xmp/ZUGFeRD_extension_schema.xmp +0 -0
  29. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-basic/Factur-X_1.08_BASIC.xsd +0 -0
  30. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-basic/Factur-X_1.08_BASIC_urn_un_unece_uncefact_data_standard_QualifiedDataType_100.xsd +0 -0
  31. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-basic/Factur-X_1.08_BASIC_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_100.xsd +0 -0
  32. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-basic/Factur-X_1.08_BASIC_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_100.xsd +0 -0
  33. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-basicwl/Factur-X_1.08_BASICWL.xsd +0 -0
  34. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-basicwl/Factur-X_1.08_BASICWL_urn_un_unece_uncefact_data_standard_QualifiedDataType_100.xsd +0 -0
  35. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-basicwl/Factur-X_1.08_BASICWL_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_100.xsd +0 -0
  36. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-basicwl/Factur-X_1.08_BASICWL_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_100.xsd +0 -0
  37. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-en16931/Factur-X_1.08_EN16931.xsd +0 -0
  38. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-en16931/Factur-X_1.08_EN16931_urn_un_unece_uncefact_data_standard_QualifiedDataType_100.xsd +0 -0
  39. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-en16931/Factur-X_1.08_EN16931_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_100.xsd +0 -0
  40. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-en16931/Factur-X_1.08_EN16931_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_100.xsd +0 -0
  41. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-extended/Factur-X_1.08_EXTENDED.xsd +0 -0
  42. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-extended/Factur-X_1.08_EXTENDED_urn_un_unece_uncefact_data_standard_QualifiedDataType_100.xsd +0 -0
  43. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-extended/Factur-X_1.08_EXTENDED_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_100.xsd +0 -0
  44. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-extended/Factur-X_1.08_EXTENDED_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_100.xsd +0 -0
  45. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-minimum/Factur-X_1.08_MINIMUM.xsd +0 -0
  46. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-minimum/Factur-X_1.08_MINIMUM_urn_un_unece_uncefact_data_standard_QualifiedDataType_100.xsd +0 -0
  47. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-minimum/Factur-X_1.08_MINIMUM_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_100.xsd +0 -0
  48. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/facturx-minimum/Factur-X_1.08_MINIMUM_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_100.xsd +0 -0
  49. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B.xsd +0 -0
  50. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_ISO_ISO3AlphaCurrencyCode_2012-08-31.xsd +0 -0
  51. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_ActionCode_D20A.xsd +0 -0
  52. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DeliveryTermsCode_2010.xsd +0 -0
  53. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DeliveryTermsFunctionCode_D20A.xsd +0 -0
  54. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DocumentNameCode_D20A.xsd +0 -0
  55. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DocumentStatusCode_D20A.xsd +0 -0
  56. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_MessageFunctionCode_D20A.xsd +0 -0
  57. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_QualifiedDataType_128.xsd +0 -0
  58. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_128.xsd +0 -0
  59. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_128.xsd +0 -0
  60. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-basic/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_identifierlist_standard_ISO_ISOTwo-letterCountryCode_SecondEdition2006.xsd +0 -0
  61. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B.xsd +0 -0
  62. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_EDIFICAS-EU_AccountingAccountType_D11A.xsd +0 -0
  63. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_EN16931_AllowanceChargeReasonCode_D20A.xsd +0 -0
  64. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_ISO_ISO3AlphaCurrencyCode_2012-08-31.xsd +0 -0
  65. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_ActionCode_D20A.xsd +0 -0
  66. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_ContactFunctionCode_D20A.xsd +0 -0
  67. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DeliveryTermsCode_2010.xsd +0 -0
  68. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DeliveryTermsFunctionCode_D20A.xsd +0 -0
  69. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DocumentNameCode_D20A.xsd +0 -0
  70. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DocumentStatusCode_D20A.xsd +0 -0
  71. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DutyTaxFeeTypeCode_D20A.xsd +0 -0
  72. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DutyorTaxorFeeCategoryCode_D20A.xsd +0 -0
  73. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_MessageFunctionCode_D20A.xsd +0 -0
  74. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_PackageTypeCode_2006.xsd +0 -0
  75. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_PaymentMeansCode_D20A.xsd +0 -0
  76. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_ReferenceTypeCode_D20A.xsd +0 -0
  77. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_QualifiedDataType_128.xsd +0 -0
  78. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_128.xsd +0 -0
  79. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_128.xsd +0 -0
  80. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-comfort/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_identifierlist_standard_ISO_ISOTwo-letterCountryCode_SecondEdition2006.xsd +0 -0
  81. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B.xsd +0 -0
  82. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_EDIFICAS-EU_AccountingAccountType_D11A.xsd +0 -0
  83. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_EN16931_AllowanceChargeReasonCode_D20A.xsd +0 -0
  84. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_ISO_ISO3AlphaCurrencyCode_2012-08-31.xsd +0 -0
  85. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_ActionCode_D20A.xsd +0 -0
  86. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_ContactFunctionCode_D20A.xsd +0 -0
  87. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DeliveryTermsCode_2010.xsd +0 -0
  88. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DeliveryTermsFunctionCode_D20A.xsd +0 -0
  89. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DocumentNameCode_D20A.xsd +0 -0
  90. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DocumentStatusCode_D20A.xsd +0 -0
  91. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DutyTaxFeeTypeCode_D20A.xsd +0 -0
  92. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_DutyorTaxorFeeCategoryCode_D20A.xsd +0 -0
  93. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_EventTimeReferenceCode_D20A.xsd +0 -0
  94. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_MessageFunctionCode_D20A.xsd +0 -0
  95. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_PackageTypeCode_2006.xsd +0 -0
  96. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_PaymentMeansCode_D20A.xsd +0 -0
  97. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_ReferenceTypeCode_D20A.xsd +0 -0
  98. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_codelist_standard_UNECE_TimePointFormatCode_D19B.xsd +0 -0
  99. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_QualifiedDataType_128.xsd +0 -0
  100. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_128.xsd +0 -0
  101. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_128.xsd +0 -0
  102. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/orderx-extended/SCRDMCCBDACIOMessageStructure_100pD20B_urn_un_unece_uncefact_identifierlist_standard_ISO_ISOTwo-letterCountryCode_SecondEdition2006.xsd +0 -0
  103. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/zugferd/ZUGFeRD1p0.xsd +0 -0
  104. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/zugferd/ZUGFeRD1p0_urn_un_unece_uncefact_data_standard_QualifiedDataType_12.xsd +0 -0
  105. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/zugferd/ZUGFeRD1p0_urn_un_unece_uncefact_data_standard_ReusableAggregateBusinessInformationEntity_12.xsd +0 -0
  106. {factur_x-3.16.dev0 → factur_x-4.0}/facturx/xsd/zugferd/ZUGFeRD1p0_urn_un_unece_uncefact_data_standard_UnqualifiedDataType_15.xsd +0 -0
  107. {factur_x-3.16.dev0 → factur_x-4.0}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: factur-x
3
- Version: 3.16.dev0
3
+ Version: 4.0
4
4
  Summary: Factur-X and Order-X: electronic invoicing and ordering standards
5
5
  Project-URL: Homepage, https://github.com/akretion/factur-x
6
6
  Project-URL: Source, https://github.com/akretion/factur-x
@@ -41,6 +41,7 @@ Requires-Python: >=3.7
41
41
  Requires-Dist: importlib-resources; python_version < '3.9'
42
42
  Requires-Dist: lxml
43
43
  Requires-Dist: pypdf>=5.3.0
44
+ Requires-Dist: saxonche
44
45
  Description-Content-Type: text/x-rst
45
46
 
46
47
  Factur-X and Order-X Python library
@@ -164,7 +165,15 @@ Contributors
164
165
  Changelog
165
166
  =========
166
167
 
167
- * Version 3.16 dated 2026-03-15
168
+ * Version 4.0 dated 2026-03-18
169
+
170
+ * Add method xml_check_schematron(). It adds a dependency in saxonche.
171
+ * Add named argument check_shematron=True on generate_from_binary(), generate_from_file() and get_xml_from_pdf(). When this new arg is True, it calls the new method xml_check_schematron(). When check_shematron=True, the performance impact is signifiant (it adds about 0.3 seconds on my laptop).
172
+ * Remove deprecated methods check_facturx_xsd(), get_facturx_flavor(), generate_facturx_from_binary() and generate_facturx_from_file(). These methods have been marked as deprecated for 5 years now (since release 2.0 dated April 4th 2021).
173
+ * Update scripts facturx-pdfextractxml and facturx-pdfgen: add option --disable-schematron-check
174
+ * Update script facturx-xmlcheck: add check against schematron after the check against XSD
175
+
176
+ * Version 3.16 dated 2026-03-13
168
177
 
169
178
  * /CheckSum is now a Bytes string object instead of a string object (patch by Adrian Devries)
170
179
 
@@ -119,7 +119,15 @@ Contributors
119
119
  Changelog
120
120
  =========
121
121
 
122
- * Version 3.16 dated 2026-03-15
122
+ * Version 4.0 dated 2026-03-18
123
+
124
+ * Add method xml_check_schematron(). It adds a dependency in saxonche.
125
+ * Add named argument check_shematron=True on generate_from_binary(), generate_from_file() and get_xml_from_pdf(). When this new arg is True, it calls the new method xml_check_schematron(). When check_shematron=True, the performance impact is signifiant (it adds about 0.3 seconds on my laptop).
126
+ * Remove deprecated methods check_facturx_xsd(), get_facturx_flavor(), generate_facturx_from_binary() and generate_facturx_from_file(). These methods have been marked as deprecated for 5 years now (since release 2.0 dated April 4th 2021).
127
+ * Update scripts facturx-pdfextractxml and facturx-pdfgen: add option --disable-schematron-check
128
+ * Update script facturx-xmlcheck: add check against schematron after the check against XSD
129
+
130
+ * Version 3.16 dated 2026-03-13
123
131
 
124
132
  * /CheckSum is now a Bytes string object instead of a string object (patch by Adrian Devries)
125
133
 
@@ -1,15 +1,12 @@
1
- __version__ = "3.16-dev"
1
+ __version__ = "4.0"
2
2
  from .facturx import generate_from_file, \
3
- generate_facturx_from_file, \
4
3
  generate_from_binary, \
5
- generate_facturx_from_binary, \
6
4
  get_xml_namespaces, \
7
- get_facturx_flavor, \
8
5
  get_flavor, \
9
6
  get_facturx_level, \
10
7
  get_level, \
11
- check_facturx_xsd, \
12
8
  xml_check_xsd, \
9
+ xml_check_schematron, \
13
10
  get_facturx_xml_from_pdf, \
14
11
  get_orderx_xml_from_pdf, \
15
12
  get_xml_from_pdf, \
@@ -34,6 +34,7 @@ from datetime import datetime
34
34
  from pypdf import PdfWriter, PdfReader
35
35
  from pypdf.generic import DictionaryObject, DecodedStreamObject, \
36
36
  NameObject, NumberObject, ArrayObject, create_string_object, ByteStringObject
37
+ import saxonche
37
38
  import importlib.resources as importlib_resources
38
39
  try:
39
40
  importlib_resources.files # added in py3.9
@@ -108,13 +109,6 @@ XML_NAMESPACES = {
108
109
  CREATOR = f'factur-x Python lib v{VERSION} by Alexis de Lattre'
109
110
 
110
111
 
111
- def check_facturx_xsd(
112
- facturx_xml, flavor='autodetect', facturx_level='autodetect'):
113
- logger.warning(
114
- 'check_facturx_xsd() is deprecated. Use xml_check_xsd() instead.')
115
- return xml_check_xsd(facturx_xml, flavor=flavor, level=facturx_level)
116
-
117
-
118
112
  def xml_check_xsd(xml, flavor='autodetect', level='autodetect'):
119
113
  """
120
114
  Validate the XML file against the XSD
@@ -137,6 +131,7 @@ def xml_check_xsd(xml, flavor='autodetect', level='autodetect'):
137
131
  raise ValueError('Wrong type for flavor argument')
138
132
  if not isinstance(level, (type(None), str)):
139
133
  raise ValueError('Wrong type for level argument')
134
+ start_chrono = datetime.now()
140
135
  xml_etree = None
141
136
  if isinstance(xml, bytes):
142
137
  xml_bytes = xml
@@ -202,7 +197,6 @@ def xml_check_xsd(xml, flavor='autodetect', level='autodetect'):
202
197
  try:
203
198
  t = etree.parse(BytesIO(xml_bytes))
204
199
  official_schema.assertValid(t)
205
- logger.info('%s XML file successfully validated against XSD', flavor)
206
200
  except Exception as e:
207
201
  # if the validation of the XSD fails, we arrive here
208
202
  logger.error(
@@ -213,26 +207,176 @@ def xml_check_xsd(xml, flavor='autodetect', level='autodetect'):
213
207
  "XML Schema Definition. "
214
208
  "Here is the error, which may give you an idea on the "
215
209
  "cause of the problem: %s." % (flavor.capitalize(), str(e)))
210
+ end_chrono = datetime.now()
211
+ logger.info(
212
+ '%s XML file successfully validated against XSD in %s sec',
213
+ flavor, (end_chrono - start_chrono).total_seconds())
216
214
  return True
217
215
 
218
216
 
219
- def get_facturx_xml_from_pdf(pdf_file, check_xsd=True):
217
+ def xml_check_schematron(xml, flavor='autodetect', level='autodetect'):
218
+ """
219
+ Validate the XML file against the schematron
220
+ :param xml: the Factur-X or Order-X XML
221
+ :type xml: string, file or etree object
222
+ :param flavor: possible values: 'factur-x', 'zugferd', 'order-x' or 'autodetect'.
223
+ Value 'zugferd' means ZUGFeRD 1.0.
224
+ :type flavor: string
225
+ :param level: the level of the Factur-X or Order-X XML file. Default value
226
+ is 'autodetect'. The only advantage to specifiy a particular value instead
227
+ of using the autodetection is for a small perf improvement.
228
+ Possible values for Factur-X: minimum, basicwl, basic, en16931, extended.
229
+ Possible values for Order-X: basic, comfort, extended.
230
+ :return: True if the XML is valid against the schematron
231
+ raise an error if it is not valid against the schematron
232
+ """
233
+ logger.debug(
234
+ 'xml_check_schematron with factur-x lib %s', VERSION)
235
+ if not isinstance(flavor, str):
236
+ raise ValueError('Wrong type for flavor argument')
237
+ if not isinstance(level, (type(None), str)):
238
+ raise ValueError('Wrong type for level argument')
239
+ start_chrono = datetime.now()
240
+ xml_etree = None
241
+ if isinstance(xml, bytes):
242
+ xml_bytes = xml
243
+ xml_str = xml_bytes.decode('utf-8')
244
+ elif isinstance(xml, str):
245
+ xml_str = xml
246
+ xml_bytes = xml_str.encode('utf-8')
247
+ elif isinstance(xml, type(etree.Element('pouet'))):
248
+ xml_etree = xml
249
+ xml_bytes = etree.tostring(
250
+ xml, pretty_print=True, encoding='UTF-8',
251
+ xml_declaration=True)
252
+ xml_str = xml_bytes.decode('utf-8')
253
+ elif isinstance(xml, IOBase):
254
+ xml.seek(0)
255
+ xml_bytes = xml.read()
256
+ xml.close()
257
+ xml_str = xml_bytes.decode('utf-8')
258
+ else:
259
+ raise ValueError('Wrong type for xml argument')
260
+
261
+ if not xml_str or not xml_bytes:
262
+ raise ValueError('xml argument is empty')
263
+
264
+ # autodetect
265
+ if flavor not in ('factur-x', 'facturx', 'zugferd', 'order-x', 'orderx'):
266
+ if xml_etree is None:
267
+ try:
268
+ xml_etree = etree.fromstring(xml_bytes)
269
+ except Exception as e:
270
+ raise Exception(
271
+ f"The XML syntax is invalid: {str(e)}.")
272
+ flavor = get_flavor(xml_etree)
273
+ if flavor in ('factur-x', 'facturx'):
274
+ if level not in FACTURX_LEVEL2xsd:
275
+ if xml_etree is None:
276
+ try:
277
+ xml_etree = etree.fromstring(xml_bytes)
278
+ except Exception as e:
279
+ raise Exception(
280
+ f"The XML syntax is invalid: {str(e)}.")
281
+ level = get_level(xml_etree, flavor)
282
+ if level not in FACTURX_LEVEL2xsd:
283
+ raise ValueError(
284
+ f"Wrong level '{level}' for Factur-X invoice.")
285
+ xsd_filename = FACTURX_LEVEL2xsd[level]
286
+ elif flavor in ('order-x', 'orderx'):
287
+ if level not in ORDERX_LEVEL2xsd:
288
+ if xml_etree is None:
289
+ try:
290
+ xml_etree = etree.fromstring(xml_bytes)
291
+ except Exception as e:
292
+ raise Exception(
293
+ f"The XML syntax is invalid: {str(e)}.")
294
+ level = get_level(xml_etree, flavor)
295
+ if level not in ORDERX_LEVEL2xsd:
296
+ raise ValueError(
297
+ f"xsd/{ORDERX_LEVEL2xsd[level][:-4]}-compiled-saxonc.xsl")
298
+ xsd_filename = ORDERX_LEVEL2xsd[level]
299
+ else:
300
+ logger.warning('There is no schematron check for flavor %s', flavor)
301
+ return True
302
+
303
+ relative_xsl_file = f"xsd/{xsd_filename[:-4]}-compiled.xsl"
304
+ xsl_file = str(importlib_resources.files(__package__).joinpath(relative_xsl_file))
305
+ logger.debug('Using schematron XSL file %s', xsl_file)
306
+ xml_str_no_bom = xml_str.lstrip('\ufeff')
307
+ errors = []
308
+ with saxonche.PySaxonProcessor() as saxproc:
309
+ xslt_proc = saxproc.new_xslt30_processor()
310
+ xdm_node = saxproc.parse_xml(xml_text=xml_str_no_bom)
311
+ # compile_stylesheet() is the slow/heavy part
312
+ # It can be optimized by generating stylesheet export files using saxon EE
313
+ # stylesheet export files can then be used by saxon HE
314
+ executable = xslt_proc.compile_stylesheet(stylesheet_file=xsl_file)
315
+ result_str = executable.transform_to_string(xdm_node=xdm_node)
316
+ logger.debug('schematron result_str=%s', result_str)
317
+
318
+ try:
319
+ svrl_root = etree.fromstring(result_str.encode('utf-8'))
320
+ except Exception as e:
321
+ logger.error(f"Schematron check generated an invalid XML output. Error: {str(e)}")
322
+ logger.info('Unable to validate %s XML file against schematron', flavor)
323
+ return False
324
+ xpath_errors = svrl_root.xpath(
325
+ ".//svrl:successful-report | .//svrl:failed-assert", namespaces=svrl_root.nsmap)
326
+ error_nr = 1
327
+ for xpath_error in xpath_errors:
328
+ detail_xpath = xpath_error.xpath("*[local-name() = 'text']", namespaces=svrl_root.nsmap)
329
+ if detail_xpath:
330
+ error_msg = detail_xpath[0].text and detail_xpath[0].text.strip()
331
+ error_msg = f'{error_nr}. {error_msg}'
332
+ location = xpath_error.attrib and xpath_error.attrib.get('location')
333
+ if location:
334
+ error_msg = f'{error_msg}\nError location: {location}'
335
+ errors.append(error_msg)
336
+ error_nr += 1
337
+
338
+ if errors:
339
+ logger.error(
340
+ "The XML file is invalid against the schematron: %d errors found.", len(errors))
341
+ for error_msg in errors:
342
+ logger.error(error_msg)
343
+ full_error = "The Factur-X XML file is not valid against the official "\
344
+ f"schematron. {len(errors)} errors found:\n{'\n'.join(errors)}"
345
+ raise Exception(full_error)
346
+ end_chrono = datetime.now()
347
+ logger.info(
348
+ '%s XML file successfully validated against schematron in %s sec',
349
+ flavor, (end_chrono - start_chrono).total_seconds())
350
+ return True
351
+
352
+
353
+ def get_facturx_xml_from_pdf(pdf_file, check_xsd=True, check_schematron=True):
220
354
  filenames = [FACTURX_FILENAME] + ZUGFERD_FILENAMES
221
- return get_xml_from_pdf(pdf_file, check_xsd=check_xsd, filenames=filenames)
355
+ return get_xml_from_pdf(
356
+ pdf_file,
357
+ check_xsd=check_xsd,
358
+ check_schematron=check_schematron,
359
+ filenames=filenames)
222
360
 
223
361
 
224
- def get_orderx_xml_from_pdf(pdf_file, check_xsd=True):
362
+ def get_orderx_xml_from_pdf(pdf_file, check_xsd=True, check_schematron=True):
225
363
  filenames = [ORDERX_FILENAME]
226
- return get_xml_from_pdf(pdf_file, check_xsd=check_xsd, filenames=filenames)
364
+ return get_xml_from_pdf(
365
+ pdf_file,
366
+ check_xsd=check_xsd,
367
+ check_schematron=check_schematron,
368
+ filenames=filenames)
227
369
 
228
370
 
229
- def get_xml_from_pdf(pdf_file, check_xsd=True, filenames=[]):
371
+ def get_xml_from_pdf(pdf_file, check_xsd=True, check_schematron=True, filenames=[]):
230
372
  logger.debug(
231
373
  'get_xml_from_pdf with factur-x lib %s', VERSION)
232
374
  if not pdf_file:
233
375
  raise ValueError('Missing pdf_invoice argument')
234
376
  if not isinstance(check_xsd, bool):
235
377
  raise ValueError('Bad type for check_xsd argument')
378
+ if not isinstance(check_schematron, bool):
379
+ raise ValueError('Bad type for check_schematron argument')
236
380
  if not isinstance(filenames, list):
237
381
  raise ValueError('Bad type for filenames argument')
238
382
  if isinstance(pdf_file, (str, bytes)):
@@ -278,11 +422,30 @@ def get_xml_from_pdf(pdf_file, check_xsd=True, filenames=[]):
278
422
  "Filename is %s but detected flavor is %s. "
279
423
  "This is very weird: skipping file.", filename, flavor)
280
424
  continue
281
- if check_xsd:
425
+ level = False
426
+ if check_xsd or check_schematron:
282
427
  try:
283
- xml_check_xsd(xml_root, flavor=flavor)
428
+ level = get_level(xml_root, flavor)
284
429
  except Exception:
285
- # Logs are already present in xml_check_xsd()
430
+ logger.warning(
431
+ 'Skipping file %s because the level could not be identified',
432
+ filename)
433
+ continue
434
+ if check_xsd and level:
435
+ try:
436
+ xml_check_xsd(xml_root, flavor=flavor, level=level)
437
+ except Exception:
438
+ logger.warning(
439
+ 'Skipping file %s because it is not valid against the XSD',
440
+ filename)
441
+ continue
442
+ if check_schematron and level:
443
+ try:
444
+ xml_check_schematron(xml_root, flavor=flavor, level=level)
445
+ except Exception:
446
+ logger.warning(
447
+ 'Skipping file %s because it is not valid against the schematron',
448
+ filename)
286
449
  continue
287
450
  xml_bytes = attach_obj.content
288
451
  xml_filename = filename
@@ -789,12 +952,6 @@ def get_level(xml_etree, flavor='autodetect'):
789
952
  return level
790
953
 
791
954
 
792
- def get_facturx_flavor(facturx_xml_etree):
793
- logger.warning(
794
- 'get_facturx_flavor() is deprecated. Use get_flavor() instead.')
795
- return get_flavor(facturx_xml_etree)
796
-
797
-
798
955
  def get_flavor(xml_etree):
799
956
  if not isinstance(xml_etree, type(etree.Element('pouet'))):
800
957
  raise ValueError('xml_etree must be an etree.Element() object')
@@ -829,23 +986,11 @@ def get_orderx_type(xml_etree):
829
986
  return ORDERX_code2type[code]
830
987
 
831
988
 
832
- def generate_facturx_from_binary(
833
- pdf_file, xml, facturx_level='autodetect',
834
- check_xsd=True, pdf_metadata=None, lang=None, attachments=None):
835
- logger.warning(
836
- 'generate_facturx_from_binary() is deprecated. '
837
- 'Use generate_from_binary() instead.')
838
- return generate_from_binary(
839
- pdf_file, xml, flavor='factur-x', level=facturx_level,
840
- check_xsd=check_xsd, pdf_metadata=pdf_metadata, lang=lang,
841
- attachments=attachments)
842
-
843
-
844
989
  def generate_from_binary(
845
990
  pdf_file, xml, flavor='autodetect', level='autodetect',
846
991
  orderx_type='autodetect',
847
- check_xsd=True, pdf_metadata=None, lang=None, attachments=None,
848
- afrelationship='data', xmp_compression=True):
992
+ check_xsd=True, check_schematron=True, pdf_metadata=None, lang=None,
993
+ attachments=None, afrelationship='data', xmp_compression=True):
849
994
  """
850
995
  Generate a Factur-X or Order-X PDF from a regular PDF and a factur-X
851
996
  or Order-X XML file. The method uses a binary as input (the regular PDF)
@@ -873,6 +1018,11 @@ def generate_from_binary(
873
1018
  beforehand, you should disable this feature to avoid a double check
874
1019
  and get a small performance improvement.
875
1020
  :type check_xsd: boolean
1021
+ :param check_schematron: if enable, checks the Factur-X XML file against
1022
+ the schematron. If this step has already been performed
1023
+ beforehand, you should disable this feature to avoid a double check
1024
+ and get a small performance improvement.
1025
+ :type check_schematron: boolean
876
1026
  :param pdf_metadata: Specify the metadata of the generated PDF.
877
1027
  If pdf_metadata is None (default value), this lib will generate some
878
1028
  metadata in English by extracting relevant info from the Factur-X/Order-X XML.
@@ -923,7 +1073,8 @@ def generate_from_binary(
923
1073
  f.write(pdf_file)
924
1074
  generate_from_file(
925
1075
  f, xml, flavor=flavor, level=level, orderx_type=orderx_type,
926
- check_xsd=check_xsd, pdf_metadata=pdf_metadata, lang=lang,
1076
+ check_xsd=check_xsd, check_schematron=check_schematron,
1077
+ pdf_metadata=pdf_metadata, lang=lang,
927
1078
  attachments=attachments, afrelationship=afrelationship,
928
1079
  xmp_compression=xmp_compression)
929
1080
  f.seek(0)
@@ -932,28 +1083,11 @@ def generate_from_binary(
932
1083
  return result_pdf
933
1084
 
934
1085
 
935
- def generate_facturx_from_file(
936
- pdf_file, facturx_xml, facturx_level='autodetect',
937
- check_xsd=True, pdf_metadata=None, output_pdf_file=None,
938
- additional_attachments=None, attachments=None, lang=None):
939
- logger.warning(
940
- 'generate_facturx_from_file() is deprecated. '
941
- 'Use generate_from_file() instead.')
942
- if additional_attachments:
943
- logger.warning(
944
- "The argument additional_attachments is not supported "
945
- "any more. Use the attachments arg instead.")
946
- return generate_from_file(
947
- pdf_file, facturx_xml, flavor='factur-x', level=facturx_level,
948
- check_xsd=check_xsd, pdf_metadata=pdf_metadata,
949
- output_pdf_file=output_pdf_file,
950
- attachments=attachments, lang=lang)
951
-
952
-
953
1086
  def generate_from_file(
954
1087
  pdf_file, xml, flavor='autodetect', level='autodetect',
955
1088
  orderx_type='autodetect',
956
- check_xsd=True, pdf_metadata=None, lang=None, output_pdf_file=None,
1089
+ check_xsd=True, check_schematron=True, pdf_metadata=None, lang=None,
1090
+ output_pdf_file=None,
957
1091
  attachments=None, afrelationship='data', xmp_compression=True):
958
1092
  """
959
1093
  Generate a Factur-X or Order-X PDF file from a regular PDF and a Factur-X
@@ -983,6 +1117,11 @@ def generate_from_file(
983
1117
  beforehand, you should disable this feature to avoid a double check
984
1118
  and get a small performance improvement.
985
1119
  :type check_xsd: boolean
1120
+ :param check_schematron: if enable, checks the Factur-X XML file against
1121
+ the schematron. If this step has already been performed
1122
+ beforehand, you should disable this feature to avoid a double check
1123
+ and get a small performance improvement.
1124
+ :type check_schematron: boolean
986
1125
  :param pdf_metadata: Specify the metadata of the generated PDF.
987
1126
  If pdf_metadata is None (default value), this lib will generate some
988
1127
  metadata in English by extracting relevant info from the Factur-X/Order-X XML.
@@ -1036,6 +1175,7 @@ def generate_from_file(
1036
1175
  logger.debug('optional arg level=%s', level)
1037
1176
  logger.debug('optional arg orderx_type=%s', orderx_type)
1038
1177
  logger.debug('optional arg check_xsd=%s', check_xsd)
1178
+ logger.debug(f"optional arg check_schematron={check_schematron}")
1039
1179
  logger.debug('optional arg pdf_metadata=%s', pdf_metadata)
1040
1180
  logger.debug('optional arg lang=%s', lang)
1041
1181
  logger.debug('optional arg output_pdf_file=%s', output_pdf_file)
@@ -1056,6 +1196,9 @@ def generate_from_file(
1056
1196
  if not isinstance(check_xsd, bool):
1057
1197
  raise ValueError(
1058
1198
  'check_xsd argument is a %s, must be a boolean' % type(check_xsd))
1199
+ if not isinstance(check_schematron, bool):
1200
+ raise ValueError(
1201
+ "check_schematron argument is a {type(check_schematron)}, must be a boolean")
1059
1202
  if not isinstance(pdf_metadata, (dict, type(None))):
1060
1203
  raise ValueError(
1061
1204
  'pdf_metadata argument is a %s, must be a dict or None'
@@ -1186,6 +1329,9 @@ def generate_from_file(
1186
1329
  if check_xsd:
1187
1330
  xml_check_xsd(
1188
1331
  xml_bytes, flavor=flavor, level=level)
1332
+ if flavor in ('factur-x', 'order-x') and check_schematron:
1333
+ xml_check_schematron(
1334
+ xml_bytes, flavor=flavor, level=level)
1189
1335
  if pdf_metadata is None:
1190
1336
  if xml_root is None:
1191
1337
  xml_root = etree.fromstring(xml_bytes)
@@ -1220,6 +1366,6 @@ def generate_from_file(
1220
1366
  pdf_writer.write(pdf_file)
1221
1367
  end_chrono = datetime.now()
1222
1368
  logger.info(
1223
- '%s PDF generated in %s seconds',
1369
+ '%s PDF generated in %s sec',
1224
1370
  flavor, (end_chrono - start_chrono).total_seconds())
1225
1371
  return True
@@ -9,8 +9,8 @@ import logging
9
9
  from os.path import isfile, isdir
10
10
 
11
11
  __author__ = "Alexis de Lattre <alexis.delattre@akretion.com>"
12
- __date__ = "July 2025"
13
- __version__ = "0.3"
12
+ __date__ = "March 2026"
13
+ __version__ = "0.4"
14
14
 
15
15
 
16
16
  def pdfextractxml(args):
@@ -42,13 +42,12 @@ def pdfextractxml(args):
42
42
  'output XML filename)', out_xml_filename)
43
43
  sys.exit(1)
44
44
  pdf_file = open(pdf_filename, 'rb')
45
- check_xsd = True
46
- if args.disable_xsd_check:
47
- check_xsd = False
45
+ check_xsd = not args.disable_xsd_check
46
+ check_schematron = not args.disable_schematron_check
48
47
  # The important line of code is below !
49
48
  try:
50
49
  (xml_filename, xml_string) = get_xml_from_pdf(
51
- pdf_file, check_xsd=check_xsd)
50
+ pdf_file, check_xsd=check_xsd, check_schematron=check_schematron)
52
51
  except Exception as e:
53
52
  logger.error(e)
54
53
  sys.exit(1)
@@ -82,6 +81,11 @@ def main(args=None):
82
81
  action='store_true',
83
82
  help="De-activate XML Schema Definition check on Factur-X/Order-X XML file "
84
83
  "(the check is enabled by default)")
84
+ parser.add_argument(
85
+ '-ds', '--disable-schematron-check', dest='disable_schematron_check',
86
+ action='store_true',
87
+ help="De-activate Schematron check on Factur-X/Order-X XML file "
88
+ "(the check is enabled by default)")
85
89
  parser.add_argument(
86
90
  "facturx_orderx_file", help="PDF Factur-X or Order-X file")
87
91
  parser.add_argument(
@@ -43,9 +43,8 @@ def pdfgen(args):
43
43
  '3rd argument %s is a directory name (should be a the '
44
44
  'Factur-X or Order-X PDF filename)', output_pdf_filename)
45
45
  sys.exit(1)
46
- check_xsd = True
47
- if args.disable_xsd_check:
48
- check_xsd = False
46
+ check_xsd = not args.disable_xsd_check
47
+ check_schematron = not args.disable_schematron_check
49
48
  pdf_metadata = None
50
49
  if (
51
50
  args.meta_author or
@@ -76,13 +75,15 @@ def pdfgen(args):
76
75
  try:
77
76
  # The important line of code is below !
78
77
  generate_from_file(
79
- pdf_filename, args.xml_file, check_xsd=check_xsd,
78
+ pdf_filename, args.xml_file,
79
+ check_xsd=check_xsd, check_schematron=check_schematron,
80
80
  flavor=args.flavor, level=args.level, orderx_type=args.orderx_type,
81
81
  pdf_metadata=pdf_metadata, lang=lang, output_pdf_file=output_pdf_filename,
82
82
  attachments=attachments, afrelationship=args.afrelationship,
83
83
  xmp_compression=xmp_compression)
84
- except Exception as e:
85
- logger.error('factur-x lib call failed. Error: %s', e)
84
+ except Exception:
85
+ # no need to re-print the error log, it is already present in the logs
86
+ logger.error('factur-x lib call failed, exiting.')
86
87
  sys.exit(1)
87
88
 
88
89
 
@@ -112,6 +113,11 @@ def main(args=None):
112
113
  action='store_true',
113
114
  help="De-activate XML Schema Definition check on XML file "
114
115
  "(the check is enabled by default)")
116
+ parser.add_argument(
117
+ '-ds', '--disable-schematron-check', dest='disable_schematron_check',
118
+ action='store_true',
119
+ help="De-activate Schematron check on XML file "
120
+ "(the check is enabled by default)")
115
121
  parser.add_argument(
116
122
  '-f', '--flavor', dest='flavor', default='autodetect',
117
123
  help="Specify if you want to generate a Factur-X or Order-X PDF file. "
@@ -3,14 +3,14 @@
3
3
 
4
4
  import argparse
5
5
  import sys
6
- from facturx import xml_check_xsd, __version__ as fxversion
6
+ from facturx import xml_check_xsd, xml_check_schematron, __version__ as fxversion
7
7
  from facturx.facturx import logger
8
8
  import logging
9
9
  from os.path import isfile
10
10
 
11
11
  __author__ = "Alexis de Lattre <alexis.delattre@akretion.com>"
12
- __date__ = "July 2025"
13
- __version__ = "0.4"
12
+ __date__ = "March 2026"
13
+ __version__ = "0.5"
14
14
 
15
15
 
16
16
  def xmlcheck(args):
@@ -34,11 +34,16 @@ def xmlcheck(args):
34
34
  if not isfile(args.xml_file):
35
35
  logger.error('%s is not a filename', args.xml_file)
36
36
  sys.exit(1)
37
- xml_file = open(args.xml_file, 'rb')
38
- # The important line of code is below !
37
+ with open(args.xml_file, 'rb') as xml_file:
38
+ xml_bytes = xml_file.read()
39
39
  try:
40
40
  xml_check_xsd(
41
- xml_file, flavor=args.flavor, level=args.level)
41
+ xml_bytes, flavor=args.flavor, level=args.level)
42
+ except Exception as e:
43
+ logger.error(e)
44
+ sys.exit(1)
45
+ try:
46
+ xml_check_schematron(xml_bytes, flavor=args.flavor, level=args.level)
42
47
  except Exception as e:
43
48
  logger.error(e)
44
49
  sys.exit(1)