forked from fortytools/ruby-secretariat
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from Skulli/feature_export_to_zugferd
Modul zum Konvertieren von PDF/A1 zu PDF/A2 und zum Erzeugen…
- Loading branch information
Showing
6 changed files
with
356 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module Secretariat | ||
module Export | ||
end | ||
end |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
module Secretariat | ||
module Export | ||
module Zugferd | ||
require "tmpdir" | ||
require "logger" | ||
require "open3" | ||
|
||
JAR_PATH = Secretariat.file_path("/lib/secretariat/export/bin/jar/Mustang-CLI-2.11.0.jar") | ||
|
||
def self.logger | ||
log_directory = File.expand_path("../../../../log", __FILE__) | ||
FileUtils.mkdir_p(log_directory) unless Dir.exist?(log_directory) | ||
|
||
@@logger ||= Logger.new(File.join(log_directory, "export.log")) | ||
end | ||
|
||
def self.convert_a1_to_a3_params(pdf_a1, output_file_path) | ||
"--action a3only --source #{pdf_a1} --out #{output_file_path}" | ||
end | ||
|
||
def self.combine_pdf_and_xml_params(source_pdf, source_xml, output_file_path, attachments_pathlist: '\"\"') | ||
"--action combine --source #{source_pdf} --source-xml #{source_xml} --out #{output_file_path} --format zf --version 2 --profile E --attachments #{attachments_pathlist}" | ||
end | ||
|
||
def self.call_jar(params) | ||
command = "java -Xmx1G -Dfile.encoding=UTF-8 -jar #{JAR_PATH} #{params}" | ||
|
||
logger.info("Executing command: #{command}") | ||
|
||
stdout, stderr, status = Open3.capture3(command) | ||
|
||
if status.success? | ||
logger.info("Command executed successfully") | ||
else | ||
logger.error("Command failed with error: #{stderr}") | ||
logger.error("Standard output: #{stdout}") | ||
end | ||
|
||
status.success? | ||
end | ||
|
||
def self.convert_to_a3(source_pdf:, output_filename: output_filename_with_suffix(source_pdf, "_a3"), output_dir: Dir.mktmpdir) | ||
output_file_path = File.join(output_dir, output_filename) | ||
|
||
begin | ||
result = call_jar(convert_a1_to_a3_params(source_pdf, output_file_path)) | ||
|
||
unless result | ||
logger.error("Failed to convert pdf") | ||
raise "Failed to convert pdf" | ||
end | ||
|
||
logger.info("Successfully converted PDF to PDFA/3: #{output_file_path}") | ||
|
||
output_file_path | ||
rescue => e | ||
logger.error("Error during file conversion: #{e.message}") | ||
raise e | ||
ensure | ||
File.delete(output_file_path) if output_dir == Dir.tmpdir && File.exist?(output_file_path) | ||
end | ||
end | ||
|
||
def self.combine_files(source_pdf:, source_xml:, output_filename: output_filename_with_suffix(source_pdf, "_zugferd"), output_dir: Dir.mktmpdir) | ||
output_file_path = File.join(output_dir, output_filename) | ||
|
||
begin | ||
result = call_jar(combine_pdf_and_xml_params(source_pdf, source_xml, output_file_path)) | ||
|
||
unless result | ||
logger.error("Failed to combine files") | ||
raise "Failed to combine files" | ||
end | ||
|
||
logger.info("Successfully combined files into #{output_file_path}") | ||
|
||
output_file_path | ||
rescue => e | ||
logger.error("Error during file combination: #{e.message}") | ||
raise e | ||
ensure | ||
File.delete(output_file_path) if output_dir == Dir.tmpdir && File.exist?(output_file_path) | ||
end | ||
end | ||
|
||
def self.output_filename_with_suffix(source_file, suffix) | ||
ext = File.extname(source_file) | ||
File.basename(source_file, ext) + suffix + ext | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
|
||
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"> | ||
<rsm:ExchangedDocumentContext> | ||
<ram:GuidelineSpecifiedDocumentContextParameter> | ||
<ram:ID>urn:cen.eu:en16931:2017</ram:ID> | ||
</ram:GuidelineSpecifiedDocumentContextParameter> | ||
</rsm:ExchangedDocumentContext> | ||
<rsm:ExchangedDocument> | ||
<ram:ID>RE-20190610/507</ram:ID> | ||
<ram:TypeCode>380</ram:TypeCode> | ||
<ram:IssueDateTime> | ||
<udt:DateTimeString format="102">20190610</udt:DateTimeString> | ||
</ram:IssueDateTime> | ||
</rsm:ExchangedDocument> | ||
<rsm:SupplyChainTradeTransaction> | ||
<ram:IncludedSupplyChainTradeLineItem> | ||
<ram:AssociatedDocumentLineDocument> | ||
<ram:LineID>1</ram:LineID> | ||
</ram:AssociatedDocumentLineDocument> | ||
<ram:SpecifiedTradeProduct> | ||
<ram:Name>Design (hours): Of a sample invoice</ram:Name> | ||
<ram:Description/> | ||
</ram:SpecifiedTradeProduct> | ||
<ram:SpecifiedLineTradeAgreement> | ||
<ram:GrossPriceProductTradePrice> | ||
<ram:ChargeAmount>160.0000</ram:ChargeAmount> | ||
<ram:BasisQuantity unitCode="HUR">1.0000</ram:BasisQuantity> | ||
</ram:GrossPriceProductTradePrice> | ||
<ram:NetPriceProductTradePrice> | ||
<ram:ChargeAmount>160.0000</ram:ChargeAmount> | ||
<ram:BasisQuantity unitCode="HUR">1.0000</ram:BasisQuantity> | ||
</ram:NetPriceProductTradePrice> | ||
</ram:SpecifiedLineTradeAgreement> | ||
<ram:SpecifiedLineTradeDelivery> | ||
<ram:BilledQuantity unitCode="HUR">1.0000</ram:BilledQuantity> | ||
</ram:SpecifiedLineTradeDelivery> | ||
<ram:SpecifiedLineTradeSettlement> | ||
<ram:ApplicableTradeTax> | ||
<ram:TypeCode>VAT</ram:TypeCode> | ||
<ram:CategoryCode>S</ram:CategoryCode> | ||
<ram:RateApplicablePercent>7.00</ram:RateApplicablePercent> | ||
</ram:ApplicableTradeTax> | ||
<ram:SpecifiedTradeSettlementLineMonetarySummation> | ||
<ram:LineTotalAmount>160.00</ram:LineTotalAmount> | ||
</ram:SpecifiedTradeSettlementLineMonetarySummation> | ||
</ram:SpecifiedLineTradeSettlement> | ||
</ram:IncludedSupplyChainTradeLineItem> | ||
<ram:IncludedSupplyChainTradeLineItem> | ||
<ram:AssociatedDocumentLineDocument> | ||
<ram:LineID>2</ram:LineID> | ||
</ram:AssociatedDocumentLineDocument> | ||
<ram:SpecifiedTradeProduct> | ||
<ram:Name>Ballons: various colors, ~2000ml</ram:Name> | ||
<ram:Description/> | ||
</ram:SpecifiedTradeProduct> | ||
<ram:SpecifiedLineTradeAgreement> | ||
<ram:GrossPriceProductTradePrice> | ||
<ram:ChargeAmount>0.7900</ram:ChargeAmount> | ||
<ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity> | ||
</ram:GrossPriceProductTradePrice> | ||
<ram:NetPriceProductTradePrice> | ||
<ram:ChargeAmount>0.7900</ram:ChargeAmount> | ||
<ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity> | ||
</ram:NetPriceProductTradePrice> | ||
</ram:SpecifiedLineTradeAgreement> | ||
<ram:SpecifiedLineTradeDelivery> | ||
<ram:BilledQuantity unitCode="C62">400.0000</ram:BilledQuantity> | ||
</ram:SpecifiedLineTradeDelivery> | ||
<ram:SpecifiedLineTradeSettlement> | ||
<ram:ApplicableTradeTax> | ||
<ram:TypeCode>VAT</ram:TypeCode> | ||
<ram:CategoryCode>S</ram:CategoryCode> | ||
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent> | ||
</ram:ApplicableTradeTax> | ||
<ram:SpecifiedTradeSettlementLineMonetarySummation> | ||
<ram:LineTotalAmount>316.00</ram:LineTotalAmount> | ||
</ram:SpecifiedTradeSettlementLineMonetarySummation> | ||
</ram:SpecifiedLineTradeSettlement> | ||
</ram:IncludedSupplyChainTradeLineItem> | ||
<ram:IncludedSupplyChainTradeLineItem> | ||
<ram:AssociatedDocumentLineDocument> | ||
<ram:LineID>3</ram:LineID> | ||
</ram:AssociatedDocumentLineDocument> | ||
<ram:SpecifiedTradeProduct> | ||
<ram:Name>Hot air „heiße Luft“ (litres)</ram:Name> | ||
<ram:Description/> | ||
</ram:SpecifiedTradeProduct> | ||
<ram:SpecifiedLineTradeAgreement> | ||
<ram:GrossPriceProductTradePrice> | ||
<ram:ChargeAmount>0.0250</ram:ChargeAmount> | ||
<ram:BasisQuantity unitCode="LTR">1.0000</ram:BasisQuantity> | ||
</ram:GrossPriceProductTradePrice> | ||
<ram:NetPriceProductTradePrice> | ||
<ram:ChargeAmount>0.0250</ram:ChargeAmount> | ||
<ram:BasisQuantity unitCode="LTR">1.0000</ram:BasisQuantity> | ||
</ram:NetPriceProductTradePrice> | ||
</ram:SpecifiedLineTradeAgreement> | ||
<ram:SpecifiedLineTradeDelivery> | ||
<ram:BilledQuantity unitCode="LTR">800.0000</ram:BilledQuantity> | ||
</ram:SpecifiedLineTradeDelivery> | ||
<ram:SpecifiedLineTradeSettlement> | ||
<ram:ApplicableTradeTax> | ||
<ram:TypeCode>VAT</ram:TypeCode> | ||
<ram:CategoryCode>S</ram:CategoryCode> | ||
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent> | ||
</ram:ApplicableTradeTax> | ||
<ram:SpecifiedTradeSettlementLineMonetarySummation> | ||
<ram:LineTotalAmount>20.00</ram:LineTotalAmount> | ||
</ram:SpecifiedTradeSettlementLineMonetarySummation> | ||
</ram:SpecifiedLineTradeSettlement> | ||
</ram:IncludedSupplyChainTradeLineItem> | ||
<ram:ApplicableHeaderTradeAgreement> | ||
<ram:BuyerReference>AB321</ram:BuyerReference> | ||
<ram:SellerTradeParty> | ||
<ram:Name>Bei Spiel GmbH</ram:Name> | ||
<ram:SpecifiedLegalOrganization> | ||
<ram:ID schemeID="9930">DE136695976</ram:ID> | ||
<ram:TradingBusinessName>Bei Spiel GmbH</ram:TradingBusinessName> | ||
</ram:SpecifiedLegalOrganization> | ||
<ram:DefinedTradeContact> | ||
<ram:PersonName>Ingmar N. Fo</ram:PersonName> | ||
<ram:TelephoneUniversalCommunication> | ||
<ram:CompleteNumber>++49(0)237823</ram:CompleteNumber> | ||
</ram:TelephoneUniversalCommunication> | ||
<ram:EmailURIUniversalCommunication> | ||
<ram:URIID>[email protected]</ram:URIID> | ||
</ram:EmailURIUniversalCommunication> | ||
</ram:DefinedTradeContact> | ||
<ram:PostalTradeAddress> | ||
<ram:PostcodeCode>12345</ram:PostcodeCode> | ||
<ram:LineOne>Ecke 12</ram:LineOne> | ||
<ram:CityName>Stadthausen</ram:CityName> | ||
<ram:CountryID>DE</ram:CountryID> | ||
</ram:PostalTradeAddress> | ||
<ram:SpecifiedTaxRegistration> | ||
<ram:ID schemeID="FC">22/815/0815/4</ram:ID> | ||
</ram:SpecifiedTaxRegistration> | ||
<ram:SpecifiedTaxRegistration> | ||
<ram:ID schemeID="VA">DE136695976</ram:ID> | ||
</ram:SpecifiedTaxRegistration> | ||
</ram:SellerTradeParty> | ||
<ram:BuyerTradeParty> | ||
<ram:Name>Theodor Est</ram:Name> | ||
<ram:PostalTradeAddress> | ||
<ram:PostcodeCode>88802</ram:PostcodeCode> | ||
<ram:LineOne>Bahnstr. 42</ram:LineOne> | ||
<ram:CityName>Spielkreis</ram:CityName> | ||
<ram:CountryID>DE</ram:CountryID> | ||
</ram:PostalTradeAddress> | ||
<ram:SpecifiedTaxRegistration> | ||
<ram:ID schemeID="VA">DE999999999</ram:ID> | ||
</ram:SpecifiedTaxRegistration> | ||
</ram:BuyerTradeParty> | ||
</ram:ApplicableHeaderTradeAgreement> | ||
<ram:ApplicableHeaderTradeDelivery> | ||
<ram:ActualDeliverySupplyChainEvent> | ||
<ram:OccurrenceDateTime> | ||
<udt:DateTimeString format="102">20190610</udt:DateTimeString> | ||
</ram:OccurrenceDateTime> | ||
</ram:ActualDeliverySupplyChainEvent> | ||
</ram:ApplicableHeaderTradeDelivery> | ||
<ram:ApplicableHeaderTradeSettlement> | ||
<ram:PaymentReference>RE-20190610/507</ram:PaymentReference> | ||
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode> | ||
<ram:SpecifiedTradeSettlementPaymentMeans> | ||
<ram:TypeCode>42</ram:TypeCode> | ||
<ram:Information>Überweisung</ram:Information> | ||
<ram:PayeePartyCreditorFinancialAccount> | ||
<ram:IBANID>DE88 2008 0000 0970 3757 00</ram:IBANID> | ||
<ram:ProprietaryID>44421800</ram:ProprietaryID> | ||
</ram:PayeePartyCreditorFinancialAccount> | ||
<ram:PayeeSpecifiedCreditorFinancialInstitution> | ||
<ram:BICID>COBADEFFXXX</ram:BICID> | ||
</ram:PayeeSpecifiedCreditorFinancialInstitution> | ||
</ram:SpecifiedTradeSettlementPaymentMeans> | ||
<ram:ApplicableTradeTax> | ||
<ram:CalculatedAmount>11.20</ram:CalculatedAmount> | ||
<ram:TypeCode>VAT</ram:TypeCode> | ||
<ram:BasisAmount>160.00</ram:BasisAmount> | ||
<ram:CategoryCode>S</ram:CategoryCode> | ||
<ram:RateApplicablePercent>7.00</ram:RateApplicablePercent> | ||
</ram:ApplicableTradeTax> | ||
<ram:ApplicableTradeTax> | ||
<ram:CalculatedAmount>63.84</ram:CalculatedAmount> | ||
<ram:TypeCode>VAT</ram:TypeCode> | ||
<ram:BasisAmount>336.00</ram:BasisAmount> | ||
<ram:CategoryCode>S</ram:CategoryCode> | ||
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent> | ||
</ram:ApplicableTradeTax> | ||
<ram:SpecifiedTradePaymentTerms> | ||
<ram:Description>Zahlbar ohne Abzug bis 01.07.2019</ram:Description> | ||
<ram:DueDateDateTime> | ||
<udt:DateTimeString format="102">20190701</udt:DateTimeString> | ||
</ram:DueDateDateTime> | ||
</ram:SpecifiedTradePaymentTerms> | ||
<ram:SpecifiedTradeSettlementHeaderMonetarySummation> | ||
<ram:LineTotalAmount>496.00</ram:LineTotalAmount> | ||
<ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount> | ||
<ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount> | ||
<ram:TaxBasisTotalAmount>496.00</ram:TaxBasisTotalAmount> | ||
<ram:TaxTotalAmount currencyID="EUR">75.04</ram:TaxTotalAmount> | ||
<ram:GrandTotalAmount>571.04</ram:GrandTotalAmount> | ||
<ram:DuePayableAmount>571.04</ram:DuePayableAmount> | ||
</ram:SpecifiedTradeSettlementHeaderMonetarySummation> | ||
</ram:ApplicableHeaderTradeSettlement> | ||
</rsm:SupplyChainTradeTransaction> | ||
</rsm:CrossIndustryInvoice> |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
require "spec_helper" | ||
require "./lib/secretariat" | ||
require "./lib/secretariat/export" | ||
require "./lib/secretariat/export/zugferd" | ||
|
||
RSpec.describe Secretariat::Export::Zugferd, type: :module do | ||
let(:source_pdf) { Secretariat.file_path("spec/fixtures/zugferd_2/test_a1.pdf") } | ||
let(:source_xml) { Secretariat.file_path("spec/fixtures/zugferd_2/test-x.xml") } | ||
let(:output_dir) { Dir.mktmpdir } | ||
let(:output_filename2) { "output2.pdf" } | ||
let(:output_file_path) { File.join(output_dir, "test_a1_a3.pdf") } | ||
let(:output_file_path2) { File.join(output_dir, output_filename2) } | ||
let(:attachments_pathlist) { "" } | ||
|
||
after(:each) do | ||
FileUtils.remove_entry(output_dir) if File.exist?(output_dir) | ||
end | ||
|
||
describe "convert_file" do | ||
it "converts a1 to a3" do | ||
result = Secretariat::Export::Zugferd.convert_to_a3( | ||
source_pdf: source_pdf, | ||
output_dir: output_dir | ||
) | ||
|
||
expect(result).to eq(output_file_path) | ||
expect(File.exist?(result)).to be(true) | ||
end | ||
|
||
after do | ||
File.delete(output_file_path) if File.exist?(output_file_path) | ||
end | ||
end | ||
|
||
describe "combine_files" do | ||
it "combines PDF and XML files" do | ||
result = Secretariat::Export::Zugferd.combine_files( | ||
source_pdf: source_pdf, | ||
source_xml: source_xml, | ||
output_filename: output_filename2, | ||
output_dir: output_dir | ||
) | ||
|
||
expect(result).to eq(output_file_path2) | ||
expect(File.exist?(result)).to be(true) | ||
end | ||
|
||
after do | ||
File.delete(output_file_path2) if File.exist?(output_file_path2) | ||
end | ||
end | ||
end |