xrechnung 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/validate-fixtures.yml +32 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.rubocop.yml +375 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +70 -0
- data/LICENSE.txt +21 -0
- data/README.md +44 -0
- data/Rakefile +55 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/xrechnung.rb +326 -0
- data/lib/xrechnung/allowance_charge.rb +32 -0
- data/lib/xrechnung/contact.rb +26 -0
- data/lib/xrechnung/currency.rb +22 -0
- data/lib/xrechnung/id.rb +3 -0
- data/lib/xrechnung/invoice_document_reference.rb +20 -0
- data/lib/xrechnung/invoice_line.rb +41 -0
- data/lib/xrechnung/item.rb +40 -0
- data/lib/xrechnung/legal_monetary_total.rb +51 -0
- data/lib/xrechnung/member_container.rb +68 -0
- data/lib/xrechnung/party.rb +61 -0
- data/lib/xrechnung/party_legal_entity.rb +16 -0
- data/lib/xrechnung/party_tax_scheme.rb +23 -0
- data/lib/xrechnung/payee_financial_account.rb +35 -0
- data/lib/xrechnung/payment_means.rb +29 -0
- data/lib/xrechnung/postal_address.rb +44 -0
- data/lib/xrechnung/price.rb +31 -0
- data/lib/xrechnung/quantity.rb +12 -0
- data/lib/xrechnung/tax_category.rb +43 -0
- data/lib/xrechnung/tax_subtotal.rb +47 -0
- data/lib/xrechnung/tax_total.rb +46 -0
- data/lib/xrechnung/version.rb +3 -0
- data/xrechnung.gemspec +29 -0
- metadata +122 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class AllowanceCharge
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
# @!attribute charge_indicator
|
6
|
+
# @return [TrueClass, FalseClass]
|
7
|
+
member :charge_indicator, type: [TrueClass, FalseClass]
|
8
|
+
|
9
|
+
# @!attribute amount
|
10
|
+
# @return [Xrechnung::Currency]
|
11
|
+
member :amount, type: Xrechnung::Currency
|
12
|
+
|
13
|
+
# @!attribute base_amount
|
14
|
+
# @return [Xrechnung::Currency]
|
15
|
+
member :base_amount, type: Xrechnung::Currency
|
16
|
+
|
17
|
+
def initialize(**kwargs)
|
18
|
+
kwargs[:amount] = Currency::EUR(kwargs[:amount])
|
19
|
+
kwargs[:base_amount] = Currency::EUR(kwargs[:base_amount])
|
20
|
+
super(**kwargs)
|
21
|
+
end
|
22
|
+
|
23
|
+
# noinspection RubyResolve
|
24
|
+
def to_xml(xml)
|
25
|
+
xml.cac :AllowanceCharge do
|
26
|
+
xml.cbc :ChargeIndicator, charge_indicator
|
27
|
+
xml.cbc :Amount, *amount.xml_args
|
28
|
+
xml.cbc :BaseAmount, *base_amount.xml_args
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class Contact
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
# @!attribute name
|
6
|
+
# @return [String]
|
7
|
+
member :name, type: String
|
8
|
+
|
9
|
+
# @!attribute telephone
|
10
|
+
# @return [String]
|
11
|
+
member :telephone, type: String
|
12
|
+
|
13
|
+
# @!attribute electronic_mail
|
14
|
+
# @return [String]
|
15
|
+
member :electronic_mail, type: String
|
16
|
+
|
17
|
+
# noinspection RubyResolve
|
18
|
+
def to_xml(xml)
|
19
|
+
xml.cac :Contact do
|
20
|
+
xml.cbc :Name, name
|
21
|
+
xml.cbc :Telephone, telephone
|
22
|
+
xml.cbc :ElectronicMail, electronic_mail
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bigdecimal"
|
2
|
+
require "bigdecimal/util"
|
3
|
+
|
4
|
+
module Xrechnung
|
5
|
+
Currency = Struct.new(:currency_id, :value, keyword_init: true) do
|
6
|
+
def value_to_s
|
7
|
+
format("%0.2f", value)
|
8
|
+
end
|
9
|
+
|
10
|
+
def xml_args
|
11
|
+
[value_to_s, currencyID: currency_id]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class << Currency
|
16
|
+
def EUR(value)
|
17
|
+
raise ArgumentError, "value must respond to :to_d" unless value.respond_to? :to_d
|
18
|
+
|
19
|
+
Currency.new(currency_id: "EUR", value: value.to_d)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/xrechnung/id.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class InvoiceDocumentReference
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
# @!attribute id
|
6
|
+
# @return [String]
|
7
|
+
member :id, type: String
|
8
|
+
|
9
|
+
# @!attribute issue_date
|
10
|
+
# @return [Date]
|
11
|
+
member :issue_date, type: Date
|
12
|
+
|
13
|
+
def to_xml(xml)
|
14
|
+
xml.cac :InvoiceDocumentReference do
|
15
|
+
xml.cbc :ID, id
|
16
|
+
xml.cbc :IssueDate, issue_date
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class InvoiceLine
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
# @!attribute id
|
6
|
+
# @return [Integer]
|
7
|
+
member :id, type: Integer
|
8
|
+
|
9
|
+
# @!attribute invoiced_quantity
|
10
|
+
# @return [Xrechnung::Quantity]
|
11
|
+
member :invoiced_quantity, type: Xrechnung::Quantity
|
12
|
+
|
13
|
+
# @!attribute line_extension_amount
|
14
|
+
# @return [Xrechnung::Currency]
|
15
|
+
member :line_extension_amount, type: Xrechnung::Currency
|
16
|
+
|
17
|
+
# @!attribute item
|
18
|
+
# @return [Xrechnung::Item]
|
19
|
+
member :item, type: Xrechnung::Item
|
20
|
+
|
21
|
+
# @!attribute price
|
22
|
+
# @return [Xrechnung::Price]
|
23
|
+
member :price, type: Xrechnung::Price
|
24
|
+
|
25
|
+
def initialize(**kwargs)
|
26
|
+
kwargs[:line_extension_amount] = Currency::EUR(kwargs[:line_extension_amount])
|
27
|
+
super(**kwargs)
|
28
|
+
end
|
29
|
+
|
30
|
+
# noinspection RubyResolve
|
31
|
+
def to_xml(xml)
|
32
|
+
xml.cac :InvoiceLine do
|
33
|
+
xml.cbc :ID, id
|
34
|
+
xml.cbc :InvoicedQuantity, invoiced_quantity.amount_to_s, unitCode: invoiced_quantity.unit_code
|
35
|
+
xml.cbc :LineExtensionAmount, *line_extension_amount.xml_args
|
36
|
+
item&.to_xml(xml)
|
37
|
+
price&.to_xml(xml)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class Item
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
# @!attribute description
|
6
|
+
# @return [String]
|
7
|
+
member :description, type: String
|
8
|
+
|
9
|
+
# @!attribute name
|
10
|
+
# @return [String]
|
11
|
+
member :name, type: String
|
12
|
+
|
13
|
+
# @!attribute standard_item_identification_id
|
14
|
+
# @return [Xrechnung::Id]
|
15
|
+
member :standard_item_identification_id, type: Xrechnung::Id, optional: true
|
16
|
+
|
17
|
+
# @!attribute commodity_classification
|
18
|
+
# @return [Xrechnung::TaxCategory]
|
19
|
+
member :commodity_classification, type: Xrechnung::TaxCategory
|
20
|
+
|
21
|
+
# @!attribute classified_tax_category
|
22
|
+
# @return [Xrechnung::TaxCategory]
|
23
|
+
member :classified_tax_category, type: Xrechnung::TaxCategory
|
24
|
+
|
25
|
+
# noinspection RubyResolve
|
26
|
+
def to_xml(xml)
|
27
|
+
xml.cac :Item do
|
28
|
+
xml.cbc :Description, description
|
29
|
+
xml.cbc :Name, name
|
30
|
+
unless standard_item_identification_id.nil?
|
31
|
+
xml.cac :StandardItemIdentification do
|
32
|
+
xml.cbc :ID, standard_item_identification_id.id, schemeID: standard_item_identification_id.scheme_id
|
33
|
+
end
|
34
|
+
end
|
35
|
+
xml.cac :CommodityClassification, commodity_classification
|
36
|
+
classified_tax_category&.to_xml(xml, root_tag_name: :ClassifiedTaxCategory)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class LegalMonetaryTotal
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
transform_currency = ->(v) {
|
6
|
+
Currency::EUR(v)
|
7
|
+
}
|
8
|
+
|
9
|
+
# @!attribute line_extension_amount
|
10
|
+
# @return [Xrechnung::Currency]
|
11
|
+
member :line_extension_amount, type: Xrechnung::Currency, transform_value: transform_currency
|
12
|
+
|
13
|
+
# @!attribute tax_exclusive_amount
|
14
|
+
# @return [Xrechnung::Currency]
|
15
|
+
member :tax_exclusive_amount, type: Xrechnung::Currency, transform_value: transform_currency
|
16
|
+
|
17
|
+
# @!attribute tax_inclusive_amount
|
18
|
+
# @return [Xrechnung::Currency]
|
19
|
+
member :tax_inclusive_amount, type: Xrechnung::Currency, transform_value: transform_currency
|
20
|
+
|
21
|
+
# @!attribute allowance_total_amount
|
22
|
+
# @return [Xrechnung::Currency]
|
23
|
+
member :allowance_total_amount, type: Xrechnung::Currency, transform_value: transform_currency
|
24
|
+
|
25
|
+
# @!attribute charge_total_amount
|
26
|
+
# @return [Xrechnung::Currency]
|
27
|
+
member :charge_total_amount, type: Xrechnung::Currency, transform_value: transform_currency
|
28
|
+
|
29
|
+
# @!attribute prepaid_amount
|
30
|
+
# @return [Xrechnung::Currency]
|
31
|
+
member :prepaid_amount, type: Xrechnung::Currency, transform_value: transform_currency
|
32
|
+
|
33
|
+
# @!attribute payable_rounding_amount
|
34
|
+
# @return [Xrechnung::Currency]
|
35
|
+
member :payable_rounding_amount, type: Xrechnung::Currency, transform_value: transform_currency
|
36
|
+
|
37
|
+
# @!attribute payable_amount
|
38
|
+
# @return [Xrechnung::Currency]
|
39
|
+
member :payable_amount, type: Xrechnung::Currency, transform_value: transform_currency
|
40
|
+
|
41
|
+
# noinspection RubyResolve
|
42
|
+
def to_xml(xml)
|
43
|
+
members.each do |member, _options|
|
44
|
+
next if self[member].nil?
|
45
|
+
|
46
|
+
xml.cbc :"#{member.to_s.split("_").map(&:capitalize).join}", *self[member].xml_args
|
47
|
+
end
|
48
|
+
xml.target!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
module MemberContainer
|
3
|
+
def self.included(base)
|
4
|
+
base.instance_variable_set :@members, {}
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(**kwargs)
|
9
|
+
self.class.after_initialize.each do |block|
|
10
|
+
instance_eval(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
kwargs.each do |k, v|
|
14
|
+
self[k] = v
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def members
|
19
|
+
self.class.instance_variable_get :@members
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](key)
|
23
|
+
send(key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def []=(key, value)
|
27
|
+
send(members[key].fetch(:setter_name), value)
|
28
|
+
end
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
# @param [String] member_name
|
32
|
+
# @param [Array<Class>, Class] type
|
33
|
+
# @param [Object] default
|
34
|
+
# @param [TrueClass, FalseClass] optional When true, omits tag rather than rendering an empty tag on nil
|
35
|
+
# @param [Proc] transform_value A Proc which is called with the input value to perform type conversion.
|
36
|
+
def member(member_name, type: nil, default: nil, optional: false, transform_value: nil)
|
37
|
+
attr_reader member_name
|
38
|
+
setter_name = :"#{member_name}="
|
39
|
+
@members[member_name] = { optional: optional, setter_name: setter_name }
|
40
|
+
|
41
|
+
if default
|
42
|
+
after_initialize do
|
43
|
+
send(setter_name, default)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
define_method setter_name do |in_value|
|
48
|
+
in_value = transform_value.call(in_value) if transform_value
|
49
|
+
|
50
|
+
if type && !in_value.nil? && Array(type).none? { |t| in_value.is_a?(t) }
|
51
|
+
raise ArgumentError, "expected #{type} for :#{member_name}, got: #{in_value.class}"
|
52
|
+
end
|
53
|
+
|
54
|
+
instance_variable_set :"@#{member_name}", in_value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def after_initialize(&block)
|
59
|
+
@after_initialize_blocks ||= []
|
60
|
+
if block
|
61
|
+
@after_initialize_blocks << block
|
62
|
+
else
|
63
|
+
@after_initialize_blocks
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class Party
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
# @!attribute name
|
6
|
+
# @return [String]
|
7
|
+
member :name, type: String
|
8
|
+
|
9
|
+
# @!attribute postal_address
|
10
|
+
# @return [Xrechnung::PostalAddress]
|
11
|
+
member :postal_address, type: Xrechnung::PostalAddress
|
12
|
+
|
13
|
+
# @!attribute party_tax_scheme
|
14
|
+
# @return [Xrechnung::PartyTaxScheme]
|
15
|
+
member :party_tax_scheme, type: Xrechnung::PartyTaxScheme
|
16
|
+
|
17
|
+
# @!attribute party_legal_entity
|
18
|
+
# @return [Xrechnung::PartyLegalEntity]
|
19
|
+
member :party_legal_entity, type: Xrechnung::PartyLegalEntity
|
20
|
+
|
21
|
+
# @!attribute contact
|
22
|
+
# @return [Xrechnung::Contact]
|
23
|
+
member :contact, type: Xrechnung::Contact
|
24
|
+
|
25
|
+
attr_accessor :nested
|
26
|
+
|
27
|
+
def initialize(nested: true, **kwargs)
|
28
|
+
super(**kwargs)
|
29
|
+
self.nested = nested
|
30
|
+
end
|
31
|
+
|
32
|
+
# noinspection RubyResolve
|
33
|
+
def to_xml(xml)
|
34
|
+
if nested
|
35
|
+
xml.cac :Party do
|
36
|
+
party_body(xml)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
party_body(xml)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def party_body(xml)
|
46
|
+
unless name.nil? # if blank? -> empty name tag
|
47
|
+
xml.cac :PartyName do
|
48
|
+
if name == ""
|
49
|
+
xml.cbc :Name
|
50
|
+
else
|
51
|
+
xml.cbc :Name, name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
postal_address&.to_xml(xml)
|
56
|
+
party_tax_scheme&.to_xml(xml)
|
57
|
+
party_legal_entity&.to_xml(xml)
|
58
|
+
contact&.to_xml(xml)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class PartyLegalEntity
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
# @!attribute registration_name
|
6
|
+
# @return [String]
|
7
|
+
member :registration_name, type: String
|
8
|
+
|
9
|
+
# noinspection RubyResolve
|
10
|
+
def to_xml(xml)
|
11
|
+
xml.cac :PartyLegalEntity do
|
12
|
+
xml.cbc :RegistrationName, registration_name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
class PartyTaxScheme
|
3
|
+
include MemberContainer
|
4
|
+
|
5
|
+
# @!attribute company_id
|
6
|
+
# @return [String]
|
7
|
+
member :company_id, type: String
|
8
|
+
|
9
|
+
# @!attribute tax_scheme_id
|
10
|
+
# @return [String]
|
11
|
+
member :tax_scheme_id, type: String, default: "VAT"
|
12
|
+
|
13
|
+
# noinspection RubyResolve
|
14
|
+
def to_xml(xml)
|
15
|
+
xml.cac :PartyTaxScheme do
|
16
|
+
xml.cbc :CompanyID, company_id
|
17
|
+
xml.cac :TaxScheme do
|
18
|
+
xml.cbc :ID, tax_scheme_id
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Xrechnung
|
2
|
+
# <cac:PayeeFinancialAccount>
|
3
|
+
# <cbc:ID>DE12500105170648489890</cbc:ID>
|
4
|
+
# <cbc:Name>Harry Hirsch</cbc:Name>
|
5
|
+
# <cac:FinancialInstitutionBranch>
|
6
|
+
# <cbc:ID>XUFUXHB</cbc:ID>
|
7
|
+
# </cac:FinancialInstitutionBranch>
|
8
|
+
# </cac:PayeeFinancialAccount>
|
9
|
+
class PayeeFinancialAccount
|
10
|
+
include MemberContainer
|
11
|
+
|
12
|
+
# @!attribute id
|
13
|
+
# @return [String]
|
14
|
+
member :id, type: String
|
15
|
+
|
16
|
+
# @!attribute name
|
17
|
+
# @return [String]
|
18
|
+
member :name, type: String
|
19
|
+
|
20
|
+
# @!attribute financial_institution_branch_id
|
21
|
+
# @return [String]
|
22
|
+
member :financial_institution_branch_id, type: String
|
23
|
+
|
24
|
+
# noinspection RubyResolve
|
25
|
+
def to_xml(xml)
|
26
|
+
xml.cac :PayeeFinancialAccount do
|
27
|
+
xml.cbc :ID, id
|
28
|
+
xml.cbc :Name, name
|
29
|
+
xml.cac :FinancialInstitutionBranch do
|
30
|
+
xml.cbc :ID, financial_institution_branch_id
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|