xrechnung 0.1.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.
@@ -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
@@ -0,0 +1,3 @@
1
+ module Xrechnung
2
+ Id = Struct.new(:id, :scheme_id)
3
+ end
@@ -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