fiscalizer 0.0.12 → 1.0.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rspec +2 -0
  4. data/Gemfile +0 -4
  5. data/LICENSE.txt +1 -1
  6. data/README.md +187 -171
  7. data/bin/console +14 -0
  8. data/bin/setup +7 -0
  9. data/doc/Tehnicka specifikacija za korisnike 1.4.pdf +0 -0
  10. data/fiscalizer.gemspec +14 -13
  11. data/lib/fiscalizer.rb +33 -4
  12. data/lib/fiscalizer/constants.rb +11 -0
  13. data/lib/fiscalizer/data_objects/echo.rb +10 -0
  14. data/lib/fiscalizer/data_objects/fee.rb +18 -0
  15. data/lib/fiscalizer/data_objects/invoice.rb +79 -0
  16. data/lib/fiscalizer/data_objects/office.rb +41 -0
  17. data/lib/fiscalizer/{tax.rb → data_objects/tax.rb} +3 -5
  18. data/lib/fiscalizer/deserializers/base.rb +58 -0
  19. data/lib/fiscalizer/deserializers/echo.rb +13 -0
  20. data/lib/fiscalizer/deserializers/invoice.rb +9 -0
  21. data/lib/fiscalizer/deserializers/office.rb +6 -0
  22. data/lib/fiscalizer/fiscalizer.rb +36 -152
  23. data/lib/fiscalizer/fiscalizers/base.rb +54 -0
  24. data/lib/fiscalizer/fiscalizers/echo.rb +13 -0
  25. data/lib/fiscalizer/fiscalizers/invoice.rb +24 -0
  26. data/lib/fiscalizer/fiscalizers/office.rb +15 -0
  27. data/lib/fiscalizer/serializers/base.rb +58 -0
  28. data/lib/fiscalizer/serializers/echo.rb +21 -0
  29. data/lib/fiscalizer/serializers/invoice.rb +92 -0
  30. data/lib/fiscalizer/serializers/office.rb +85 -0
  31. data/lib/fiscalizer/serializers/signature.rb +62 -0
  32. data/lib/fiscalizer/serializers/tax.rb +81 -0
  33. data/lib/fiscalizer/services/request_sender.rb +68 -0
  34. data/lib/fiscalizer/services/security_code_generator.rb +29 -0
  35. data/lib/fiscalizer/version.rb +1 -1
  36. data/spec/fiscalizer_spec.rb +119 -0
  37. data/spec/spec_helper.rb +9 -0
  38. metadata +67 -39
  39. data/doc/README.md +0 -3
  40. data/doc/Tehnicka_specifikacija_za_korisnike_1.2.pdf +0 -0
  41. data/example/README.md +0 -25
  42. data/example/echo_P12.rb +0 -13
  43. data/example/echo_public_and_private_keys.rb +0 -16
  44. data/example/invoice_fiscalization_passing_arguments.rb +0 -44
  45. data/example/invoice_fiscalization_passing_object.rb +0 -48
  46. data/example/office_fiscalization_passing_arguments.rb +0 -22
  47. data/example/office_fiscalization_passing_object.rb +0 -26
  48. data/lib/README.md +0 -0
  49. data/lib/fiscalizer/README.md +0 -9
  50. data/lib/fiscalizer/communication.rb +0 -305
  51. data/lib/fiscalizer/echo.rb +0 -11
  52. data/lib/fiscalizer/fee.rb +0 -20
  53. data/lib/fiscalizer/invoice.rb +0 -190
  54. data/lib/fiscalizer/office.rb +0 -66
  55. data/lib/fiscalizer/response.rb +0 -124
  56. data/test/README.md +0 -13
  57. data/test/test_echo.rb +0 -20
  58. data/test/test_fee +0 -64
  59. data/test/test_fiscalizer.rb +0 -222
  60. data/test/test_fiscalizer_communication.rb +0 -28
  61. data/test/test_invoice.rb +0 -11
  62. data/test/test_office.rb +0 -11
  63. data/test/test_tax.rb +0 -89
@@ -0,0 +1,54 @@
1
+ class Fiscalizer
2
+ module Fiscalizers
3
+ class Base
4
+ # rubocop:disable Metrics/ParameterLists
5
+ def initialize(app_cert_path, password, timeout, demo, ca_cert_path, object)
6
+ @app_cert_path = app_cert_path
7
+ @password = password
8
+ @timeout = timeout
9
+ @demo = demo
10
+ @ca_cert_path = ca_cert_path
11
+ @object = object
12
+ end
13
+
14
+ attr_reader :app_cert_path, :password, :timeout, :demo, :ca_cert_path, :object
15
+
16
+ def call
17
+ # check_echo
18
+ send_request
19
+ end
20
+
21
+ private
22
+
23
+ def send_request
24
+ response = request_sender.send(request_message)
25
+ deserialize(response)
26
+ end
27
+
28
+ def request_message
29
+ serializer.new(object, app_private_key, app_public_key, demo).call
30
+ end
31
+
32
+ def deserialize(response)
33
+ deserializer.new(response.body, object)
34
+ end
35
+
36
+ def request_sender
37
+ @request_sender ||=
38
+ Fiscalizer::RequestSender.new(extracted_app_cert, password, timeout, demo, ca_cert_path)
39
+ end
40
+
41
+ def app_public_key
42
+ @app_public_key ||= OpenSSL::X509::Certificate.new(extracted_app_cert.certificate)
43
+ end
44
+
45
+ def app_private_key
46
+ @app_private_key ||= OpenSSL::PKey::RSA.new(extracted_app_cert.key)
47
+ end
48
+
49
+ def extracted_app_cert
50
+ @extracted_app_cert ||= OpenSSL::PKCS12.new(File.read(app_cert_path), password)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,13 @@
1
+ class Fiscalizer
2
+ module Fiscalizers
3
+ class Echo < Base
4
+ def serializer
5
+ Serializers::Echo
6
+ end
7
+
8
+ def deserializer
9
+ Deserializers::Echo
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ class Fiscalizer
2
+ module Fiscalizers
3
+ class Invoice < Base
4
+ def call
5
+ generate_security_code
6
+ send_request
7
+ end
8
+
9
+ private
10
+
11
+ def generate_security_code
12
+ SecurityCodeGenerator.new(object, app_private_key).call
13
+ end
14
+
15
+ def serializer
16
+ Serializers::Invoice
17
+ end
18
+
19
+ def deserializer
20
+ Deserializers::Invoice
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ class Fiscalizer
2
+ module Fiscalizers
3
+ class Office < Base
4
+ private
5
+
6
+ def serializer
7
+ Serializers::Office
8
+ end
9
+
10
+ def deserializer
11
+ Deserializers::Office
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ class Fiscalizer
2
+ module Serializers
3
+ class Base
4
+ include Constants
5
+
6
+ def initialize(object, private_key, public_key, demo)
7
+ @object = object
8
+ @private_key = private_key
9
+ @public_key = public_key
10
+ @demo = demo
11
+ end
12
+
13
+ attr_reader :object, :private_key, :public_key, :demo
14
+
15
+ def call
16
+ sign_xml
17
+ end
18
+
19
+ private
20
+
21
+ def sign_xml
22
+ document = Xmldsig_fiscalizer::SignedDocument.new(xml_with_soap_envelope.doc.root.to_xml)
23
+ signed_xml = document.sign(private_key)
24
+ signed_xml.sub!('<?xml version="1.0"?>', '')
25
+ signed_xml = signed_xml.gsub(/^$\n/, '')
26
+ object.generated_xml = signed_xml
27
+ signed_xml
28
+ end
29
+
30
+ def xml_with_soap_envelope
31
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
32
+ xml['soapenv'].Envelope('xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/') do
33
+ xml['soapenv'].Body do
34
+ xml << raw_xml.doc.root.to_xml
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def root_hash
41
+ {
42
+ 'xmlns:tns' => TNS,
43
+ 'xmlns:xsi' => XSI,
44
+ 'xsi:schemaLocation' => SCHEMA_LOCATION,
45
+ 'Id' => message_id
46
+ }
47
+ end
48
+
49
+ def add_signature(xml)
50
+ Serializers::Signature.new(xml, "##{message_id}", public_key, cert_issuer).call
51
+ end
52
+
53
+ def cert_issuer
54
+ demo ? DEMO_CERT_ISSUER : PROD_CERT_ISSUER
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,21 @@
1
+ class Fiscalizer
2
+ module Serializers
3
+ class Echo < Base
4
+ private
5
+
6
+ def message_id
7
+ 'EchoRequest'
8
+ end
9
+
10
+ def raw_xml
11
+ @raw_xml ||= begin
12
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
13
+ xml['tns'].EchoRequest(root_hash) do
14
+ xml.text object.message
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,92 @@
1
+ class Fiscalizer
2
+ module Serializers
3
+ class Invoice < Base
4
+ private
5
+
6
+ def message_id
7
+ 'RacunZahtjev'
8
+ end
9
+
10
+ def raw_xml
11
+ @raw_xml ||= begin
12
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
13
+ xml['tns'].RacunZahtjev(root_hash) do
14
+ add_header(xml)
15
+ add_body(xml)
16
+ add_signature(xml)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def add_header(xml)
23
+ xml['tns'].Zaglavlje do
24
+ xml['tns'].IdPoruke object.uuid
25
+ xml['tns'].DatumVrijeme object.time_sent_str
26
+ end
27
+ end
28
+
29
+ def add_body(xml)
30
+ xml['tns'].Racun do
31
+ add_general_invoice_info(xml)
32
+ add_invoice_number_info(xml)
33
+ add_invoice_tax_info(xml)
34
+ add_invoice_fee_info(xml)
35
+ add_invoice_summary(xml)
36
+ add_paragon_label(xml)
37
+ add_specific_purpose(xml)
38
+ end
39
+ end
40
+
41
+ def add_general_invoice_info(xml)
42
+ xml['tns'].Oib object.pin
43
+ xml['tns'].USustPdv object.in_vat_system
44
+ xml['tns'].DatVrijeme object.time_issued_str
45
+ xml['tns'].OznSlijed object.consistance_mark
46
+ end
47
+
48
+ def add_invoice_number_info(xml)
49
+ xml['tns'].BrRac do
50
+ xml['tns'].BrOznRac object.issued_number.to_s
51
+ xml['tns'].OznPosPr object.issued_office.to_s
52
+ xml['tns'].OznNapUr object.issued_machine.to_s
53
+ end
54
+ end
55
+
56
+ def add_invoice_tax_info(xml)
57
+ Serializers::Tax.new(xml, object).call
58
+ end
59
+
60
+ def add_invoice_fee_info(xml)
61
+ return if object.fees.empty?
62
+
63
+ xml['tns'].Naknade do
64
+ object.fees.each do |fee|
65
+ xml['tns'].Naknada do
66
+ xml['tns'].NazivN fee.name
67
+ xml['tns'].IznosN fee.value
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def add_invoice_summary(xml)
74
+ xml['tns'].IznosUkupno object.summed_total_str
75
+ xml['tns'].NacinPlac object.payment_method
76
+ xml['tns'].OibOper object.operator_pin
77
+ xml['tns'].ZastKod object.security_code
78
+ xml['tns'].NakDost object.subsequent_delivery
79
+ end
80
+
81
+ def add_paragon_label(xml)
82
+ return if object.paragon_label.nil?
83
+ xml['tns'].ParagonBrRac object.paragon_label
84
+ end
85
+
86
+ def add_specific_purpose(xml)
87
+ return if object.specific_purpose.nil?
88
+ xml['tns'].SpecNamj object.specific_purpose
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,85 @@
1
+ class Fiscalizer
2
+ module Serializers
3
+ class Office < Base
4
+ private
5
+
6
+ def message_id
7
+ 'PoslovniProstorZahtjev'
8
+ end
9
+
10
+ def raw_xml
11
+ @raw_xml ||= begin
12
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
13
+ xml['tns'].PoslovniProstorZahtjev(root_hash) do
14
+ add_header(xml)
15
+ add_body(xml)
16
+ add_signature(xml)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def add_header(xml)
23
+ xml['tns'].Zaglavlje do
24
+ xml['tns'].IdPoruke object.uuid
25
+ xml['tns'].DatumVrijeme object.time_sent_str
26
+ end
27
+ end
28
+
29
+ def add_body(xml)
30
+ xml['tns'].PoslovniProstor do
31
+ add_general_info(xml)
32
+ add_address_info(xml)
33
+ add_time_info(xml)
34
+ add_closure_mark(xml)
35
+ add_specific_purpose(xml)
36
+ end
37
+ end
38
+
39
+ def add_general_info(xml)
40
+ xml['tns'].Oib object.pin
41
+ xml['tns'].OznPoslProstora object.office_label
42
+ end
43
+
44
+ def add_address_info(xml)
45
+ xml['tns'].AdresniPodatak do
46
+ add_address(xml)
47
+ add_other_address(xml)
48
+ end
49
+ end
50
+
51
+ def add_address(xml)
52
+ return if object.adress_other
53
+
54
+ xml['tns'].Adresa do
55
+ xml['tns'].Ulica object.adress_street_name
56
+ xml['tns'].KucniBroj object.adress_house_num
57
+ xml['tns'].KucniBrojDodatak object.adress_house_num_addendum
58
+ xml['tns'].BrojPoste object.adress_post_num
59
+ xml['tns'].Naselje object.adress_settlement
60
+ xml['tns'].Opcina object.adress_township
61
+ end
62
+ end
63
+
64
+ def add_other_address(xml)
65
+ return if object.adress_other.nil?
66
+ xml['tns'].OstaliTipoviP object.adress_other
67
+ end
68
+
69
+ def add_time_info(xml)
70
+ xml['tns'].RadnoVrijeme object.office_time
71
+ xml['tns'].DatumPocetkaPrimjene object.take_effect_date_str
72
+ end
73
+
74
+ def add_closure_mark(xml)
75
+ return if object.closure_mark.nil?
76
+ xml['tns'].OznakaZatvaranja object.closure_mark
77
+ end
78
+
79
+ def add_specific_purpose(xml)
80
+ return if object.specific_purpose.nil?
81
+ xml['tns'].SpecNamj object.specific_purpose
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,62 @@
1
+ class Fiscalizer
2
+ module Serializers
3
+ class Signature
4
+ def initialize(xml, reference, public_key, cert_issuer)
5
+ @xml = xml
6
+ @reference = reference
7
+ @public_key = public_key
8
+ @cert_issuer = cert_issuer
9
+ end
10
+
11
+ attr_reader :xml, :reference, :public_key, :cert_issuer
12
+
13
+ def call
14
+ xml.Signature('xmlns' => 'http://www.w3.org/2000/09/xmldsig#') do
15
+ add_signed_info
16
+ add_signature_value
17
+ add_key_info
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def add_signed_info
24
+ xml.SignedInfo do
25
+ xml.CanonicalizationMethod('Algorithm' => 'http://www.w3.org/2001/10/xml-exc-c14n#')
26
+ xml.SignatureMethod('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1')
27
+ xml.Reference('URI' => reference) do
28
+ xml.Transforms do
29
+ xml.Transform('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#enveloped-signature')
30
+ xml.Transform('Algorithm' => 'http://www.w3.org/2001/10/xml-exc-c14n#')
31
+ end
32
+ xml.DigestMethod('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#sha1')
33
+ xml.DigestValue
34
+ end
35
+ end
36
+ end
37
+
38
+ def add_signature_value
39
+ xml.SignatureValue
40
+ end
41
+
42
+ def add_key_info
43
+ xml.KeyInfo do
44
+ xml.X509Data do
45
+ xml.X509Certificate public_key_string
46
+ xml.X509IssuerSerial do
47
+ xml.X509IssuerName cert_issuer
48
+ xml.X509SerialNumber '1053520622'
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def public_key_string
55
+ public_key.to_s
56
+ .gsub('-----BEGIN CERTIFICATE-----', '')
57
+ .gsub('-----END CERTIFICATE-----', '')
58
+ .gsub("\n", '')
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,81 @@
1
+ class Fiscalizer
2
+ module Serializers
3
+ class Tax
4
+ def initialize(xml, object)
5
+ @xml = xml
6
+ @object = object
7
+ end
8
+
9
+ attr_reader :xml, :object
10
+
11
+ def call
12
+ add_vat_tax
13
+ add_spending_tax
14
+ add_other_taxes
15
+ add_general_tax_info
16
+ end
17
+
18
+ private
19
+
20
+ def add_vat_tax
21
+ return if object.tax_vat.empty?
22
+
23
+ xml['tns'].Pdv do
24
+ object.tax_vat.each do |tax|
25
+ add_tax(tax)
26
+ end
27
+ end
28
+ end
29
+
30
+ def add_spending_tax
31
+ return if object.tax_spending.empty?
32
+
33
+ xml['tns'].Pnp do
34
+ object.tax_spending.each do |tax|
35
+ add_tax(tax)
36
+ end
37
+ end
38
+ end
39
+
40
+ def add_other_taxes
41
+ return if object.tax_other.empty?
42
+
43
+ xml['tns'].OstaliPor do
44
+ object.tax_other.each do |tax|
45
+ add_tax(tax, true)
46
+ end
47
+ end
48
+ end
49
+
50
+ def add_tax(tax, include_name = false)
51
+ xml['tns'].Porez do
52
+ xml['tns'].Naziv tax.name if include_name
53
+ xml['tns'].Stopa tax.rate_str
54
+ xml['tns'].Osnovica tax.base_str
55
+ xml['tns'].Iznos tax.total_str
56
+ end
57
+ end
58
+
59
+ def add_general_tax_info
60
+ add_tax_liberation
61
+ add_tax_margin
62
+ add_non_taxable
63
+ end
64
+
65
+ def add_tax_liberation
66
+ return if object.value_tax_liberation_str.nil?
67
+ xml['tns'].IznosOslobPdv object.value_tax_liberation_str
68
+ end
69
+
70
+ def add_tax_margin
71
+ return if object.value_tax_margin_str.nil?
72
+ xml['tns'].IznosMarza object.value_tax_margin_str
73
+ end
74
+
75
+ def add_non_taxable
76
+ return if object.value_non_taxable_str.nil?
77
+ xml['tns'].IznosNePodlOpor object.value_non_taxable_str
78
+ end
79
+ end
80
+ end
81
+ end