secretariat 3.2.0 → 3.4.0
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/secretariat/attachment.rb +83 -0
- data/lib/secretariat/constants.rb +16 -2
- data/lib/secretariat/invoice.rb +58 -21
- data/lib/secretariat/line_item.rb +38 -14
- data/lib/secretariat/tax.rb +6 -0
- data/lib/secretariat/trade_party.rb +12 -3
- data/lib/secretariat/version.rb +1 -1
- data/lib/secretariat.rb +1 -0
- metadata +31 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1a42571c7c3ceb77877659b076fbd8974ee661843dbc4f4d294012163a4c99f
|
4
|
+
data.tar.gz: 871cf260400cdf9470c867572a77d6ac5d109886b30b9d99120bbbfa94b5f950
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e69622419e9e48946ddf0dc4921515a02fa3e8c1f53cd1fd23cd14c89d7f2470718e8d220a4c9594142a768db4c143452efd93607917b230c28e9bb647ec537d
|
7
|
+
data.tar.gz: f5c1da6eea2d10742eebb2ae95993026f74ee6968ac630caaa2f38face0c26b03e401d6204752b14e816af8c408d8052843e77071bc6b68f37200bd3607099f6
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ See tests for examples.
|
|
7
7
|
1. This is an opinionated library optimised for my very specific usecase
|
8
8
|
2. While I did start to add some validations to make sure you can't input absolute garbage into this, I cannot guarantee factual (as in taxation law) correctness of the resulting XML.
|
9
9
|
1. The library, for ZUGFeRD 2.x, currently only supports the EN16931 variant. This is probably what you want as well. PRs welcome.
|
10
|
-
3. This does not contain any code to attach the XML to a PDF file, mainly because I have yet to find a ruby library to do that. For software that does this, take a look at [this python library](https://github.com/akretion/factur-x).
|
10
|
+
3. This does not contain any code to attach the XML to a PDF file, mainly because I have yet to find a ruby library to do that. For software that does this, take a look at [this python library](https://github.com/akretion/factur-x) or [this Java library which also does extended validation](https://mustangproject.org)
|
11
11
|
|
12
12
|
## Contributors
|
13
13
|
|
@@ -0,0 +1,83 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright Jan Krutisch
|
3
|
+
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
you may not use this file except in compliance with the License.
|
6
|
+
You may obtain a copy of the License at
|
7
|
+
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
See the License for the specific language governing permissions and
|
14
|
+
limitations under the License.
|
15
|
+
|
16
|
+
=end
|
17
|
+
|
18
|
+
|
19
|
+
require 'mime/types'
|
20
|
+
|
21
|
+
module Secretariat
|
22
|
+
Attachment = Struct.new('Attachment',
|
23
|
+
:filename,
|
24
|
+
:type_code,
|
25
|
+
:base64,
|
26
|
+
|
27
|
+
keyword_init: true
|
28
|
+
) do
|
29
|
+
include Versioner
|
30
|
+
|
31
|
+
def errors
|
32
|
+
@errors
|
33
|
+
end
|
34
|
+
|
35
|
+
def valid?
|
36
|
+
@errors = []
|
37
|
+
|
38
|
+
@errors << "the attribute filename needs to be present" if filename.nil? || filename == ''
|
39
|
+
@errors << "the attribute type_code needs to be present" if type_code.nil? || type_code == ''
|
40
|
+
@errors << "the attribute base64 needs to be present" if base64.nil? || base64 == ''
|
41
|
+
|
42
|
+
if type_code.to_i != 916
|
43
|
+
@errors << "we only support type_code 916"
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
|
47
|
+
if mime_code.nil?
|
48
|
+
@errors << "cannot determine content type for filename: #{filename}"
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
|
52
|
+
if !ALLOWED_MIME_TYPES.include?(mime_code)
|
53
|
+
@errors << "the mime_code '#{mime_code}' is not allowed"
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
def mime_code
|
61
|
+
type_for = MIME::Types.type_for(filename).first
|
62
|
+
return if type_for.nil?
|
63
|
+
|
64
|
+
type_for.content_type
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_xml(xml, attachment_index, version: 2, validate: true)
|
68
|
+
if validate && !valid?
|
69
|
+
pp errors
|
70
|
+
raise ValidationError.new("Attachment #{attachment_index} is invalid", errors)
|
71
|
+
end
|
72
|
+
|
73
|
+
xml['ram'].AdditionalReferencedDocument do
|
74
|
+
xml['ram'].IssuerAssignedID filename
|
75
|
+
xml['ram'].TypeCode type_code
|
76
|
+
xml['ram'].Name filename
|
77
|
+
xml['ram'].AttachmentBinaryObject(mimeCode: mime_code, filename: filename) do
|
78
|
+
xml.text(base64)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -15,6 +15,14 @@ limitations under the License.
|
|
15
15
|
=end
|
16
16
|
|
17
17
|
module Secretariat
|
18
|
+
ALLOWED_MIME_TYPES = [
|
19
|
+
"application/pdf",
|
20
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
21
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
22
|
+
"image/jpeg",
|
23
|
+
"image/png",
|
24
|
+
"text/csv"
|
25
|
+
]
|
18
26
|
|
19
27
|
TAX_CATEGORY_CODES = {
|
20
28
|
:STANDARDRATE => "S",
|
@@ -45,15 +53,21 @@ module Secretariat
|
|
45
53
|
:DEBITADVICE => "31",
|
46
54
|
:CREDITCARD => "48",
|
47
55
|
:DEBIT => "49",
|
48
|
-
:COMPENSATION => "97"
|
56
|
+
:COMPENSATION => "97"
|
49
57
|
}
|
50
58
|
|
51
59
|
TAX_EXEMPTION_REASONS = {
|
52
60
|
:REVERSECHARGE => 'Reverse Charge',
|
53
61
|
:INTRACOMMUNITY => 'Intra-community transaction',
|
54
|
-
:EXPORT => 'Export outside the EU'
|
62
|
+
:EXPORT => 'Export outside the EU',
|
63
|
+
:TAXEXEMPT => 'VAT exempt',
|
64
|
+
:UNTAXEDSERVICE => 'Not subject to VAT'
|
55
65
|
}
|
56
66
|
|
67
|
+
# For the background of vertical and horizontal tax calculation see https://hilfe.pacemo.de/de-form/articles/3489851-rundungsfehler-bei-rechnungen
|
68
|
+
# The idea of introducing an unknown value is that this could be inferred from the given invoice total and line items by probing both variants and selecting the matching one - or reporting a taxation error if neither matches.
|
69
|
+
TAX_CALCULATION_METHODS = %i[HORIZONTAL VERTICAL NONE UNKNOWN].freeze
|
70
|
+
|
57
71
|
UNIT_CODES = {
|
58
72
|
:PIECE => "C62",
|
59
73
|
:DAY => "DAY",
|
data/lib/secretariat/invoice.rb
CHANGED
@@ -28,6 +28,7 @@ module Secretariat
|
|
28
28
|
:line_items,
|
29
29
|
:currency_code,
|
30
30
|
:payment_type,
|
31
|
+
:payment_reference,
|
31
32
|
:payment_text,
|
32
33
|
:payment_terms_text,
|
33
34
|
:payment_due_date,
|
@@ -40,7 +41,8 @@ module Secretariat
|
|
40
41
|
:grand_total_amount,
|
41
42
|
:due_amount,
|
42
43
|
:paid_amount,
|
43
|
-
|
44
|
+
:tax_calculation_method,
|
45
|
+
:attachments,
|
44
46
|
keyword_init: true
|
45
47
|
) do
|
46
48
|
|
@@ -54,21 +56,43 @@ module Secretariat
|
|
54
56
|
tax_reason || TAX_EXEMPTION_REASONS[tax_category]
|
55
57
|
end
|
56
58
|
|
57
|
-
def tax_category_code(version: 2)
|
59
|
+
def tax_category_code(tax, version: 2)
|
58
60
|
if version == 1
|
59
|
-
return TAX_CATEGORY_CODES_1[tax_category] || 'S'
|
61
|
+
return TAX_CATEGORY_CODES_1[tax.tax_category || tax_category] || 'S'
|
60
62
|
end
|
61
|
-
TAX_CATEGORY_CODES[tax_category] || 'S'
|
63
|
+
TAX_CATEGORY_CODES[tax.tax_category || tax_category] || 'S'
|
62
64
|
end
|
63
65
|
|
64
66
|
def taxes
|
65
67
|
taxes = {}
|
68
|
+
# Shortcut for cases where invoices only have one tax and the calculation is off by a cent because of rounding errors
|
69
|
+
# (This can happen if the VAT and the net amount is calculated backwards from a round gross amount)
|
70
|
+
if tax_calculation_method == :NONE
|
71
|
+
tax = Tax.new(tax_percent: BigDecimal(tax_percent || BigDecimal(0)), tax_category: tax_category)
|
72
|
+
tax.base_amount = BigDecimal(basis_amount)
|
73
|
+
tax.tax_amount = BigDecimal(tax_amount || 0)
|
74
|
+
return [tax]
|
75
|
+
end
|
76
|
+
|
66
77
|
line_items.each do |line_item|
|
67
|
-
|
68
|
-
|
69
|
-
|
78
|
+
if line_item.tax_percent.nil?
|
79
|
+
taxes['0'] = Tax.new(tax_percent: BigDecimal(0), tax_category: line_item.tax_category, tax_amount: BigDecimal(0)) if taxes['0'].nil?
|
80
|
+
taxes['0'].base_amount += BigDecimal(line_item.net_amount) * line_item.quantity
|
81
|
+
else
|
82
|
+
taxes[line_item.tax_percent] = Tax.new(tax_percent: BigDecimal(line_item.tax_percent), tax_category: line_item.tax_category) if taxes[line_item.tax_percent].nil?
|
83
|
+
taxes[line_item.tax_percent].tax_amount += BigDecimal(line_item.tax_amount)
|
84
|
+
taxes[line_item.tax_percent].base_amount += BigDecimal(line_item.net_amount) * line_item.quantity
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if tax_calculation_method == :VERTICAL
|
89
|
+
taxes.values.map do |tax|
|
90
|
+
tax.tax_amount = (tax.base_amount * tax.tax_percent / 100).round(2)
|
91
|
+
tax
|
92
|
+
end
|
93
|
+
else
|
94
|
+
taxes.values
|
70
95
|
end
|
71
|
-
taxes.values
|
72
96
|
end
|
73
97
|
|
74
98
|
def payment_code
|
@@ -89,12 +113,14 @@ module Secretariat
|
|
89
113
|
@errors << "Base amount and summed tax base amount deviate: #{basis} / #{summed_tax_base_amount}"
|
90
114
|
return false
|
91
115
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
116
|
+
if tax_calculation_method != :NONE
|
117
|
+
taxes.each do |tax|
|
118
|
+
calc_tax = tax.base_amount * BigDecimal(tax.tax_percent) / BigDecimal(100)
|
119
|
+
calc_tax = calc_tax.round(2)
|
120
|
+
if tax.tax_amount != calc_tax
|
121
|
+
@errors << "Tax amount and calculated tax amount deviate for rate #{tax.tax_percent}: #{tax.tax_amount} / #{calc_tax}"
|
122
|
+
return false
|
123
|
+
end
|
98
124
|
end
|
99
125
|
end
|
100
126
|
grand_total = BigDecimal(grand_total_amount)
|
@@ -104,7 +130,7 @@ module Secretariat
|
|
104
130
|
return false
|
105
131
|
end
|
106
132
|
line_item_sum = line_items.inject(BigDecimal(0)) do |m, item|
|
107
|
-
m + BigDecimal(item.charge_amount)
|
133
|
+
m + BigDecimal(item.quantity.negative? ? -item.charge_amount : item.charge_amount)
|
108
134
|
end
|
109
135
|
if line_item_sum != basis
|
110
136
|
@errors << "Line items do not add up to basis amount #{line_item_sum} / #{basis}"
|
@@ -169,7 +195,7 @@ module Secretariat
|
|
169
195
|
xml.text(issue_date.strftime("%Y%m%d"))
|
170
196
|
end
|
171
197
|
end
|
172
|
-
|
198
|
+
|
173
199
|
end
|
174
200
|
transaction = by_version(version, 'SpecifiedSupplyChainTradeTransaction', 'SupplyChainTradeTransaction')
|
175
201
|
xml['rsm'].send(transaction) do
|
@@ -192,6 +218,13 @@ module Secretariat
|
|
192
218
|
xml['ram'].BuyerTradeParty do
|
193
219
|
buyer.to_xml(xml, version: version)
|
194
220
|
end
|
221
|
+
if version == 2
|
222
|
+
if Array(attachments).size > 0
|
223
|
+
attachments.each_with_index do |attachment, index|
|
224
|
+
attachment.to_xml(xml, index, version: version, validate: validate)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
195
228
|
end
|
196
229
|
|
197
230
|
delivery = by_version(version, 'ApplicableSupplyChainTradeDelivery', 'ApplicableHeaderTradeDelivery')
|
@@ -212,6 +245,9 @@ module Secretariat
|
|
212
245
|
end
|
213
246
|
trade_settlement = by_version(version, 'ApplicableSupplyChainTradeSettlement', 'ApplicableHeaderTradeSettlement')
|
214
247
|
xml['ram'].send(trade_settlement) do
|
248
|
+
if payment_reference && payment_reference != ''
|
249
|
+
xml['ram'].PaymentReference payment_reference
|
250
|
+
end
|
215
251
|
xml['ram'].InvoiceCurrencyCode currency_code
|
216
252
|
xml['ram'].SpecifiedTradeSettlementPaymentMeans do
|
217
253
|
xml['ram'].TypeCode payment_code
|
@@ -230,10 +266,11 @@ module Secretariat
|
|
230
266
|
xml['ram'].ExemptionReason tax_reason_text
|
231
267
|
end
|
232
268
|
Helpers.currency_element(xml, 'ram', 'BasisAmount', tax.base_amount, currency_code, add_currency: version == 1)
|
233
|
-
xml['ram'].CategoryCode tax_category_code(version: version)
|
234
|
-
|
235
|
-
|
236
|
-
|
269
|
+
xml['ram'].CategoryCode tax_category_code(tax, version: version)
|
270
|
+
# unless tax.untaxable?
|
271
|
+
percent = by_version(version, 'ApplicablePercent', 'RateApplicablePercent')
|
272
|
+
xml['ram'].send(percent, Helpers.format(tax.tax_percent))
|
273
|
+
# end
|
237
274
|
end
|
238
275
|
end
|
239
276
|
if version == 2 && service_period_start && service_period_end
|
@@ -271,7 +308,7 @@ module Secretariat
|
|
271
308
|
end
|
272
309
|
if version == 1
|
273
310
|
line_items.each_with_index do |item, i|
|
274
|
-
item.to_xml(xml, i + 1, version: version) # one indexed
|
311
|
+
item.to_xml(xml, i + 1, version: version, validate: validate) # one indexed
|
275
312
|
end
|
276
313
|
end
|
277
314
|
end
|
@@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
15
15
|
=end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
17
|
require 'bigdecimal'
|
20
18
|
module Secretariat
|
21
19
|
|
@@ -49,7 +47,7 @@ module Secretariat
|
|
49
47
|
gross_price = BigDecimal(gross_amount)
|
50
48
|
charge_price = BigDecimal(charge_amount)
|
51
49
|
tax = BigDecimal(tax_amount)
|
52
|
-
unit_price = net_price * BigDecimal(quantity)
|
50
|
+
unit_price = net_price * BigDecimal(quantity.abs)
|
53
51
|
|
54
52
|
if charge_price != unit_price
|
55
53
|
@errors << "charge price and gross price times quantity deviate: #{charge_price} / #{unit_price}"
|
@@ -63,11 +61,15 @@ module Secretariat
|
|
63
61
|
return false
|
64
62
|
end
|
65
63
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
64
|
+
if tax_category != :UNTAXEDSERVICE
|
65
|
+
self.tax_percent ||= BigDecimal(0)
|
66
|
+
calculated_tax = charge_price * BigDecimal(tax_percent) / BigDecimal(100)
|
67
|
+
calculated_tax = calculated_tax.round(2)
|
68
|
+
calculated_tax = -calculated_tax if quantity.negative?
|
69
|
+
if calculated_tax != tax
|
70
|
+
@errors << "Tax and calculated tax deviate: #{tax} / #{calculated_tax}"
|
71
|
+
return false
|
72
|
+
end
|
71
73
|
end
|
72
74
|
return true
|
73
75
|
end
|
@@ -83,9 +85,31 @@ module Secretariat
|
|
83
85
|
TAX_CATEGORY_CODES[tax_category] || 'S'
|
84
86
|
end
|
85
87
|
|
88
|
+
def untaxable?
|
89
|
+
tax_category == :UNTAXEDSERVICE
|
90
|
+
end
|
91
|
+
|
86
92
|
def to_xml(xml, line_item_index, version: 2, validate: true)
|
93
|
+
net_price = net_amount && BigDecimal(net_amount)
|
94
|
+
gross_price = gross_amount && BigDecimal(gross_amount)
|
95
|
+
charge_price = charge_amount && BigDecimal(charge_amount)
|
96
|
+
|
97
|
+
self.tax_percent ||= BigDecimal(0)
|
98
|
+
|
99
|
+
if net_price&.zero?
|
100
|
+
self.tax_percent = 0
|
101
|
+
end
|
102
|
+
|
103
|
+
if net_price&.negative?
|
104
|
+
# Zugferd doesn't allow negative amounts at the item level.
|
105
|
+
# Instead, a negative quantity is used.
|
106
|
+
self.quantity = -quantity
|
107
|
+
self.gross_amount = gross_price&.abs
|
108
|
+
self.net_amount = net_price&.abs
|
109
|
+
self.charge_amount = charge_price&.abs
|
110
|
+
end
|
111
|
+
|
87
112
|
if validate && !valid?
|
88
|
-
pp errors
|
89
113
|
raise ValidationError.new("LineItem #{line_item_index} is invalid", errors)
|
90
114
|
end
|
91
115
|
|
@@ -152,14 +176,14 @@ module Secretariat
|
|
152
176
|
xml['ram'].ApplicableTradeTax do
|
153
177
|
xml['ram'].TypeCode 'VAT'
|
154
178
|
xml['ram'].CategoryCode tax_category_code(version: version)
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
179
|
+
unless untaxable?
|
180
|
+
percent = by_version(version, 'ApplicablePercent', 'RateApplicablePercent')
|
181
|
+
xml['ram'].send(percent,Helpers.format(tax_percent))
|
182
|
+
end
|
159
183
|
end
|
160
184
|
monetary_summation = by_version(version, 'SpecifiedTradeSettlementMonetarySummation', 'SpecifiedTradeSettlementLineMonetarySummation')
|
161
185
|
xml['ram'].send(monetary_summation) do
|
162
|
-
Helpers.currency_element(xml, 'ram', 'LineTotalAmount', charge_amount, currency_code, add_currency: version == 1)
|
186
|
+
Helpers.currency_element(xml, 'ram', 'LineTotalAmount', (quantity.negative? ? -charge_amount : charge_amount), currency_code, add_currency: version == 1)
|
163
187
|
end
|
164
188
|
end
|
165
189
|
|
data/lib/secretariat/tax.rb
CHANGED
@@ -20,6 +20,7 @@ module Secretariat
|
|
20
20
|
Tax = Struct.new('Tax',
|
21
21
|
:tax_percent,
|
22
22
|
:tax_amount,
|
23
|
+
:tax_category,
|
23
24
|
:base_amount,
|
24
25
|
keyword_init: true
|
25
26
|
) do
|
@@ -29,5 +30,10 @@ module Secretariat
|
|
29
30
|
self.tax_amount = 0
|
30
31
|
self.base_amount = 0
|
31
32
|
end
|
33
|
+
|
34
|
+
def untaxable?
|
35
|
+
tax_category == :UNTAXEDSERVICE
|
36
|
+
end
|
37
|
+
|
32
38
|
end
|
33
39
|
end
|
@@ -16,10 +16,15 @@ limitations under the License.
|
|
16
16
|
|
17
17
|
module Secretariat
|
18
18
|
TradeParty = Struct.new('TradeParty',
|
19
|
-
:name, :street1, :street2, :city, :postal_code, :country_id, :vat_id,
|
19
|
+
:name, :street1, :street2, :city, :postal_code, :country_id, :vat_id, :global_id, :global_id_scheme_id, :tax_id,
|
20
20
|
keyword_init: true,
|
21
21
|
) do
|
22
22
|
def to_xml(xml, exclude_tax: false, version: 2)
|
23
|
+
if global_id && global_id != '' && global_id_scheme_id && global_id_scheme_id != ''
|
24
|
+
xml['ram'].GlobalID(schemeID: global_id_scheme_id) do
|
25
|
+
xml.text(global_id)
|
26
|
+
end
|
27
|
+
end
|
23
28
|
xml['ram'].Name name
|
24
29
|
xml['ram'].PostalTradeAddress do
|
25
30
|
xml['ram'].PostcodeCode postal_code
|
@@ -36,9 +41,13 @@ module Secretariat
|
|
36
41
|
xml.text(vat_id)
|
37
42
|
end
|
38
43
|
end
|
44
|
+
elsif tax_id && tax_id != ''
|
45
|
+
xml['ram'].SpecifiedTaxRegistration do
|
46
|
+
xml['ram'].ID(schemeID: 'FC') do
|
47
|
+
xml.text(tax_id)
|
48
|
+
end
|
49
|
+
end
|
39
50
|
end
|
40
51
|
end
|
41
52
|
end
|
42
|
-
|
43
|
-
|
44
53
|
end
|
data/lib/secretariat/version.rb
CHANGED
data/lib/secretariat.rb
CHANGED
metadata
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: secretariat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Krutisch
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
10
|
date: 2024-11-06 00:00:00.000000000 Z
|
@@ -38,6 +37,20 @@ dependencies:
|
|
38
37
|
- - "~>"
|
39
38
|
- !ruby/object:Gem::Version
|
40
39
|
version: '3.1'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: mime-types
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.6.0
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 3.6.0
|
41
54
|
- !ruby/object:Gem::Dependency
|
42
55
|
name: minitest
|
43
56
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +79,20 @@ dependencies:
|
|
66
79
|
- - "~>"
|
67
80
|
- !ruby/object:Gem::Version
|
68
81
|
version: '13.0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: base64
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 0.2.0
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 0.2.0
|
69
96
|
description: a tool to help generate and validate ZUGFeRD invoice xml files
|
70
97
|
email: jan@krutisch.de
|
71
98
|
executables: []
|
@@ -75,6 +102,7 @@ files:
|
|
75
102
|
- README.md
|
76
103
|
- bin/schxslt-cli.jar
|
77
104
|
- lib/secretariat.rb
|
105
|
+
- lib/secretariat/attachment.rb
|
78
106
|
- lib/secretariat/constants.rb
|
79
107
|
- lib/secretariat/helpers.rb
|
80
108
|
- lib/secretariat/invoice.rb
|
@@ -100,7 +128,6 @@ homepage: https://github.com/halfbyte/ruby-secretariat
|
|
100
128
|
licenses:
|
101
129
|
- Apache-2.0
|
102
130
|
metadata: {}
|
103
|
-
post_install_message:
|
104
131
|
rdoc_options: []
|
105
132
|
require_paths:
|
106
133
|
- lib
|
@@ -116,8 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
143
|
version: '0'
|
117
144
|
requirements:
|
118
145
|
- To run the validator, Java must be installed
|
119
|
-
rubygems_version: 3.
|
120
|
-
signing_key:
|
146
|
+
rubygems_version: 3.6.2
|
121
147
|
specification_version: 4
|
122
148
|
summary: A ZUGFeRD xml generator
|
123
149
|
test_files: []
|