fiscalizer 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +209 -0
- data/Rakefile +9 -0
- data/doc/FiskalizacijaSchema.xsd +780 -0
- data/doc/Tehnicka_specifikacija_za_korisnike_1.2.pdf +0 -0
- data/doc/fiskalizacija_faq_v2.1_objava.pdf +0 -0
- data/example/echo_P12.rb +13 -0
- data/example/echo_public_and_private_keys.rb +16 -0
- data/example/invoice_fiscalization_passing_object.rb +0 -0
- data/example/invoice_fiscalization_with_arguments.rb +44 -0
- data/fiscalizer.gemspec +25 -0
- data/lib/README.md +0 -0
- data/lib/fiscalizer.rb +8 -0
- data/lib/fiscalizer/README.md +9 -0
- data/lib/fiscalizer/communication.rb +296 -0
- data/lib/fiscalizer/echo.rb +11 -0
- data/lib/fiscalizer/fee.rb +20 -0
- data/lib/fiscalizer/fiscalizer.rb +148 -0
- data/lib/fiscalizer/invoice.rb +190 -0
- data/lib/fiscalizer/office.rb +66 -0
- data/lib/fiscalizer/response.rb +124 -0
- data/lib/fiscalizer/tax.rb +47 -0
- data/lib/fiscalizer/version.rb +3 -0
- data/test/test_echo.rb +20 -0
- data/test/test_fee +64 -0
- data/test/test_fiscalizer.rb +166 -0
- data/test/test_fiscalizer_communication.rb +28 -0
- data/test/test_invoice.rb +11 -0
- data/test/test_office.rb +11 -0
- data/test/test_tax.rb +89 -0
- metadata +139 -0
Binary file
|
Binary file
|
data/example/echo_P12.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
fiscal = Fiscalizer.new certificate_p12_path: "/path/to/FISKAL 1.p12",
|
2
|
+
password: "12345678"
|
3
|
+
echo_response = fiscal.echo text: "It's mearly a flesh wound!"
|
4
|
+
|
5
|
+
if echo_response.errors?
|
6
|
+
puts "There were some nasty errors!"
|
7
|
+
echo_response.errors.each do |error_code, error_message|
|
8
|
+
puts " " + error_code + " : " + error_message
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
puts "The server returned: " + echo_response.response
|
13
|
+
puts "The echo request was successful! Great success!" if echo_response.echo?
|
@@ -0,0 +1,16 @@
|
|
1
|
+
fiscal = Fiscalizer.new url: "https://cistest.apis-it.hr:8449/FiskalizacijaServiceTest",
|
2
|
+
key_public_path: "/path/to/fiskal1.cert",
|
3
|
+
key_private_path: "/path/to/privateKey.key",
|
4
|
+
certificate_path: "/path/to/democacert.pem",
|
5
|
+
certificate_issued_by: "OU=DEMO,O=FINA,C=HR"
|
6
|
+
echo_response = fiscal.echo text: "It's mearly a flesh wound!"
|
7
|
+
|
8
|
+
if echo_response.errors?
|
9
|
+
puts "There were some nasty errors!"
|
10
|
+
echo_response.errors.each do |error_code, error_message|
|
11
|
+
puts " " + error_code + " : " + error_message
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
puts "The server returned: " + echo_response.response
|
16
|
+
puts "The echo request was successful! Great success!" if echo_response.echo?
|
File without changes
|
@@ -0,0 +1,44 @@
|
|
1
|
+
fiscal = Fiscalizer.new certificate_p12_path: "/path/to/FISKAL 1.p12",
|
2
|
+
password: "12345678"
|
3
|
+
# Generate taxes
|
4
|
+
taxes_vat = []
|
5
|
+
taxes_spending = []
|
6
|
+
taxes_other = []
|
7
|
+
(0..5).each do |i|
|
8
|
+
tax = Fiscalizer::Tax.new
|
9
|
+
tax.base = rand(10000 * 100).to_f / 100.0
|
10
|
+
tax.rate = rand(100 * 100).to_f / 100.0
|
11
|
+
taxes_vat << tax
|
12
|
+
end
|
13
|
+
(0..5).each do |i|
|
14
|
+
tax = Fiscalizer::Tax.new
|
15
|
+
tax.base = rand(10000 * 100).to_f / 100.0
|
16
|
+
tax.rate = rand(100 * 100).to_f / 100.0
|
17
|
+
taxes_spending << tax
|
18
|
+
end
|
19
|
+
(0..5).each do |i|
|
20
|
+
tax = Fiscalizer::Tax.new
|
21
|
+
tax.base = rand(10000 * 100).to_f / 100.0
|
22
|
+
tax.rate = rand(100 * 100).to_f / 100.0
|
23
|
+
tax.name = "My Test Tax #{i}"
|
24
|
+
taxes_other << tax
|
25
|
+
end
|
26
|
+
# Generate invoice
|
27
|
+
invoice_response = fiscal.invoice uuid: UUID,
|
28
|
+
time_sent: Time.now,
|
29
|
+
pin: PIN,
|
30
|
+
in_vat_system: true,
|
31
|
+
time_issued: Time.now - 3600,
|
32
|
+
consistance_mark: "P",
|
33
|
+
issued_number: "1",
|
34
|
+
issued_office: "Pm2",
|
35
|
+
issued_machine: "3",
|
36
|
+
tax_vat: taxes_vat,
|
37
|
+
tax_spending: taxes_spending,
|
38
|
+
tax_other: taxes_other,
|
39
|
+
payment_method: "g",
|
40
|
+
operator_pin: PIN_OPERATOR,
|
41
|
+
subsequent_delivery: false,
|
42
|
+
value_non_taxable: 200.0
|
43
|
+
|
44
|
+
puts "The server returned the following JIR: " + invoice_response.unique_identifier if !invoice_response.errors?
|
data/fiscalizer.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fiscalizer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "fiscalizer"
|
8
|
+
spec.version = Fiscalizer::VERSION
|
9
|
+
spec.authors = ["Stanko Krtalić Rusendić"]
|
10
|
+
spec.email = ["stanko.krtalic-rusendic@infinum.hr"]
|
11
|
+
spec.description = %q{Automatic fiscalization}
|
12
|
+
spec.summary = %q{A gem that automatically handles fiscalization}
|
13
|
+
spec.homepage = "https://github.com/infinum/fiscalizer"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "nokogiri"
|
22
|
+
spec.add_dependency "xmldsig-fiscalizer"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
data/lib/README.md
ADDED
File without changes
|
data/lib/fiscalizer.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Fiscalizer
|
2
|
+
|
3
|
+
Fiscalizer is the main class of this gem. It interacts with all other classes, handles certificate decription and connection establishment.
|
4
|
+
|
5
|
+
Methods:
|
6
|
+
|
7
|
+
* `echo` : Creates an echo request to the fiscalization servers
|
8
|
+
* `fiscalize_office` : Creates or recives an office object and fiscalizes it (alias methods `office` and `fiscalize_office_space`)
|
9
|
+
* `fiscalize_invoice` : Creates or recives an invoice object and fiscalizes it (alias methods `invoice`)
|
@@ -0,0 +1,296 @@
|
|
1
|
+
class Fiscalizer
|
2
|
+
class Communication
|
3
|
+
attr_accessor :url, :tns, :schemaLocation,
|
4
|
+
:key_public, :key_private, :certificate,
|
5
|
+
:certificate_issued_by
|
6
|
+
|
7
|
+
def initialize( tns: "http://www.apis-it.hr/fin/2012/types/f73",
|
8
|
+
url: "https://cis.porezna-uprava.hr:8449/FiskalizacijaService",
|
9
|
+
schemaLocation: "http://www.apis-it.hr/fin/2012/types/f73 FiskalizacijaSchema.xsd",
|
10
|
+
key_public: nil, key_private: nil, certificate: nil,
|
11
|
+
certificate_issued_by: "OU=RDC,O=FINA,C=HR" )
|
12
|
+
@tns = tns
|
13
|
+
@url = url
|
14
|
+
@schemaLocation = schemaLocation
|
15
|
+
@key_public = key_public
|
16
|
+
@key_private = key_private
|
17
|
+
@certificate = certificate
|
18
|
+
@certificate_issued_by = certificate_issued_by
|
19
|
+
end # initialize
|
20
|
+
|
21
|
+
def send object
|
22
|
+
# Check input
|
23
|
+
raise "Missing keys" if @key_public == nil || @key_private == nil
|
24
|
+
raise "Missing certificate" if @certificate == nil
|
25
|
+
raise "Can't send nil object" if object == nil
|
26
|
+
|
27
|
+
# Prepare data
|
28
|
+
uri = URI.parse(@url)
|
29
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
30
|
+
http.use_ssl = true
|
31
|
+
http.cert_store = OpenSSL::X509::Store.new
|
32
|
+
http.cert_store.set_default_paths
|
33
|
+
http.cert_store.add_cert(@certificate)
|
34
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
35
|
+
|
36
|
+
# Encode object
|
37
|
+
encoded_object = nil
|
38
|
+
if object.class == Echo
|
39
|
+
encoded_object = encode_echo object
|
40
|
+
elsif object.class == Office
|
41
|
+
encoded_object = encode_office object
|
42
|
+
elsif object.class == Invoice
|
43
|
+
encoded_object = encode_invoice object
|
44
|
+
else
|
45
|
+
# Something broke so we return false,
|
46
|
+
# this enables us to check sucess in an if statement
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
|
50
|
+
# Send it
|
51
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
52
|
+
request.content_type = 'application/xml'
|
53
|
+
request.body = encoded_object
|
54
|
+
response = http.request(request)
|
55
|
+
|
56
|
+
return response
|
57
|
+
end # send
|
58
|
+
|
59
|
+
private
|
60
|
+
def encode_echo object
|
61
|
+
echo_xml = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
62
|
+
xml['tns'].EchoRequest(
|
63
|
+
'xmlns:tns' => @tns,
|
64
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
65
|
+
'xsi:schemaLocation' => @schemaLocation
|
66
|
+
) {
|
67
|
+
xml.text object.text
|
68
|
+
}
|
69
|
+
end
|
70
|
+
xml = soap_envelope(echo_xml).to_xml
|
71
|
+
object.generated_xml = soap_envelope(echo_xml).to_xml
|
72
|
+
return xml
|
73
|
+
end # send_echo
|
74
|
+
|
75
|
+
def encode_office object
|
76
|
+
office_request_xml = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
77
|
+
xml['tns'].PoslovniProstorZahtjev(
|
78
|
+
'xmlns:tns' => @tns,
|
79
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
80
|
+
'xsi:schemaLocation' => schemaLocation,
|
81
|
+
'Id' => 'PoslovniProstorZahtjev'
|
82
|
+
) {
|
83
|
+
# Header
|
84
|
+
xml['tns'].Zaglavlje {
|
85
|
+
xml['tns'].IdPoruke object.uuid
|
86
|
+
xml['tns'].DatumVrijeme object.time_sent_str
|
87
|
+
}
|
88
|
+
# Body
|
89
|
+
xml['tns'].PoslovniProstor {
|
90
|
+
# Important info
|
91
|
+
xml['tns'].Oib object.pin
|
92
|
+
xml['tns'].OznPoslProstora object.office_label
|
93
|
+
xml['tns'].AdresniPodatak {
|
94
|
+
xml['tns'].Adresa {
|
95
|
+
xml['tns'].Ulica object.adress_street_name
|
96
|
+
xml['tns'].KucniBroj object.adress_house_num
|
97
|
+
xml['tns'].KucniBrojDodatak object.adress_house_num_addendum
|
98
|
+
xml['tns'].BrojPoste object.adress_post_num
|
99
|
+
xml['tns'].Naselje object.adress_settlement
|
100
|
+
xml['tns'].Opcina object.adress_township
|
101
|
+
} if object.adress_other == nil
|
102
|
+
xml['tns'].OstaliTipoviPP object.adress_other if object.adress_other != nil
|
103
|
+
}
|
104
|
+
xml['tns'].RadnoVrijeme object.office_time
|
105
|
+
xml['tns'].DatumPocetkaPrimjene object.take_effect_date_str
|
106
|
+
# Optional info
|
107
|
+
xml['tns'].OznakaZatvaranja object.closure_mark if object.closure_mark != nil
|
108
|
+
xml['tns'].SpecNamj object.specific_purpose if object.specific_purpose != nil
|
109
|
+
}
|
110
|
+
xml.Signature('xmlns' => 'http://www.w3.org/2000/09/xmldsig#') {
|
111
|
+
xml.SignedInfo {
|
112
|
+
xml.CanonicalizationMethod('Algorithm' => 'http://www.w3.org/2001/10/xml-exc-c14n#')
|
113
|
+
xml.SignatureMethod('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1')
|
114
|
+
xml.Reference('URI' => '#PoslovniProstorZahtjev') {
|
115
|
+
xml.Transforms {
|
116
|
+
xml.Transform('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#enveloped-signature')
|
117
|
+
xml.Transform('Algorithm' => 'http://www.w3.org/2001/10/xml-exc-c14n#')
|
118
|
+
}
|
119
|
+
xml.DigestMethod('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#sha1')
|
120
|
+
xml.DigestValue
|
121
|
+
}
|
122
|
+
}
|
123
|
+
xml.SignatureValue
|
124
|
+
xml.KeyInfo {
|
125
|
+
xml.X509Data {
|
126
|
+
xml.X509Certificate key_public.to_s.gsub('-----BEGIN CERTIFICATE-----','').gsub('-----END CERTIFICATE-----','').gsub("\n",'')
|
127
|
+
xml.X509IssuerSerial {
|
128
|
+
xml.X509IssuerName @certificate_issued_by
|
129
|
+
xml.X509SerialNumber "1053520622" # Explane
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
body = soap_envelope(office_request_xml)
|
138
|
+
unsigned_document = Xmldsig::SignedDocument.new(body.doc.root.to_xml)
|
139
|
+
signed_xml = unsigned_document.sign(@key_private)
|
140
|
+
signed_xml.sub! '<?xml version="1.0"?>', ''
|
141
|
+
signed_xml = signed_xml.gsub /^$\n/, ''
|
142
|
+
|
143
|
+
object.generated_xml = signed_xml
|
144
|
+
|
145
|
+
return signed_xml
|
146
|
+
end # send_office
|
147
|
+
|
148
|
+
def encode_invoice object
|
149
|
+
generate_security_code object
|
150
|
+
invoice_request_xml = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
151
|
+
xml['tns'].RacunZahtjev(
|
152
|
+
'xmlns:tns' => @tns,
|
153
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
154
|
+
'xsi:schemaLocation' => schemaLocation,
|
155
|
+
'Id' => 'RacunZahtjev'
|
156
|
+
) {
|
157
|
+
# Header
|
158
|
+
xml['tns'].Zaglavlje {
|
159
|
+
xml['tns'].IdPoruke object.uuid
|
160
|
+
xml['tns'].DatumVrijeme object.time_sent_str
|
161
|
+
}
|
162
|
+
# Body
|
163
|
+
xml['tns'].Racun {
|
164
|
+
# Important info
|
165
|
+
xml['tns'].Oib object.pin
|
166
|
+
xml['tns'].USustPdv object.in_vat_system
|
167
|
+
xml['tns'].DatVrijeme object.time_issued_str
|
168
|
+
xml['tns'].OznSlijed object.consistance_mark
|
169
|
+
# Invoice issued numbers
|
170
|
+
xml['tns'].BrRac {
|
171
|
+
xml['tns'].BrOznRac object.issued_number.to_s
|
172
|
+
xml['tns'].OznPosPr object.issued_office.to_s
|
173
|
+
xml['tns'].OznNapUr object.issued_machine.to_s
|
174
|
+
}
|
175
|
+
# Optional info
|
176
|
+
# Tax VAT
|
177
|
+
xml['tns'].Pdv {
|
178
|
+
object.tax_vat.each do |tax|
|
179
|
+
xml['tns'].Porez {
|
180
|
+
xml['tns'].Stopa tax.rate_str
|
181
|
+
xml['tns'].Osnovica tax.base_str
|
182
|
+
xml['tns'].Iznos tax.total_str
|
183
|
+
}
|
184
|
+
end
|
185
|
+
} if object.tax_vat != nil && object.tax_vat.size != 0
|
186
|
+
# Tax Spending
|
187
|
+
xml['tns'].Pnp {
|
188
|
+
object.tax_spending.each do |tax|
|
189
|
+
xml['tns'].Porez {
|
190
|
+
xml['tns'].Stopa tax.rate_str
|
191
|
+
xml['tns'].Osnovica tax.base_str
|
192
|
+
xml['tns'].Iznos tax.total_str
|
193
|
+
}
|
194
|
+
end
|
195
|
+
} if object.tax_spending != nil && object.tax_spending.size != 0
|
196
|
+
# Tax Other
|
197
|
+
xml['tns'].OstaliPor {
|
198
|
+
object.tax_other.each do |tax|
|
199
|
+
xml['tns'].Porez {
|
200
|
+
xml['tns'].Naziv tax.name
|
201
|
+
xml['tns'].Stopa tax.rate_str
|
202
|
+
xml['tns'].Osnovica tax.base_str
|
203
|
+
xml['tns'].Iznos tax.total_str
|
204
|
+
}
|
205
|
+
end
|
206
|
+
} if object.tax_other != nil && object.tax_other.size != 0
|
207
|
+
# Tax info
|
208
|
+
xml['tns'].IznosOslobPdv object.value_tax_liberation_str if object.value_tax_liberation_str != nil
|
209
|
+
xml['tns'].IznosMarza object.value_tax_margin_str if object.value_tax_margin_str != nil
|
210
|
+
xml['tns'].IznosNePodlOpor object.value_non_taxable_str if object.value_non_taxable_str != nil
|
211
|
+
# Fees
|
212
|
+
xml['tns'].Naknade {
|
213
|
+
object.tax_other.each do |tax|
|
214
|
+
xml['tns'].Naknada {
|
215
|
+
xml['tns'].NazivN tax.name
|
216
|
+
xml['tns'].IznosN tax.rate_str
|
217
|
+
}
|
218
|
+
end
|
219
|
+
} if object.fees != nil && object.fees.size != 0
|
220
|
+
# Important info
|
221
|
+
xml['tns'].IznosUkupno object.summed_total_str
|
222
|
+
xml['tns'].NacinPlac object.payment_method
|
223
|
+
xml['tns'].OibOper object.operator_pin
|
224
|
+
xml['tns'].ZastKod object.security_code
|
225
|
+
xml['tns'].NakDost object.subsequent_delivery
|
226
|
+
# Optional info
|
227
|
+
xml['tns'].ParagonBrRac object.paragon_label if object.paragon_label != nil
|
228
|
+
xml['tns'].SpecNamj object.specific_purpose if object.specific_purpose != nil
|
229
|
+
}
|
230
|
+
xml.Signature('xmlns' => 'http://www.w3.org/2000/09/xmldsig#') {
|
231
|
+
xml.SignedInfo {
|
232
|
+
xml.CanonicalizationMethod('Algorithm' => 'http://www.w3.org/2001/10/xml-exc-c14n#')
|
233
|
+
xml.SignatureMethod('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1')
|
234
|
+
xml.Reference('URI' => '#RacunZahtjev') {
|
235
|
+
xml.Transforms {
|
236
|
+
xml.Transform('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#enveloped-signature')
|
237
|
+
xml.Transform('Algorithm' => 'http://www.w3.org/2001/10/xml-exc-c14n#')
|
238
|
+
}
|
239
|
+
xml.DigestMethod('Algorithm' => 'http://www.w3.org/2000/09/xmldsig#sha1')
|
240
|
+
xml.DigestValue
|
241
|
+
}
|
242
|
+
}
|
243
|
+
xml.SignatureValue
|
244
|
+
xml.KeyInfo {
|
245
|
+
xml.X509Data {
|
246
|
+
xml.X509Certificate key_public.to_s.gsub('-----BEGIN CERTIFICATE-----','').gsub('-----END CERTIFICATE-----','').gsub("\n",'')
|
247
|
+
xml.X509IssuerSerial {
|
248
|
+
xml.X509IssuerName @certificate_issued_by
|
249
|
+
xml.X509SerialNumber "1053520622" # Explane
|
250
|
+
}
|
251
|
+
}
|
252
|
+
}
|
253
|
+
}
|
254
|
+
}
|
255
|
+
end
|
256
|
+
|
257
|
+
body = soap_envelope(invoice_request_xml)
|
258
|
+
unsigned_document = Xmldsig::SignedDocument.new(body.doc.root.to_xml)
|
259
|
+
signed_xml = unsigned_document.sign(@key_private)
|
260
|
+
signed_xml.sub! '<?xml version="1.0"?>', ''
|
261
|
+
signed_xml = signed_xml.gsub /^$\n/, ''
|
262
|
+
|
263
|
+
object.generated_xml = signed_xml
|
264
|
+
|
265
|
+
return signed_xml
|
266
|
+
end # encode_invoice
|
267
|
+
|
268
|
+
def soap_envelope req_xml
|
269
|
+
return Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
270
|
+
xml['soapenv'].Envelope('xmlns:soapenv' => 'http://schemas.xmlsoap.org/soap/envelope/') {
|
271
|
+
xml['soapenv'].Body {
|
272
|
+
xml << req_xml.doc.root.to_xml
|
273
|
+
}
|
274
|
+
}
|
275
|
+
end
|
276
|
+
end # soap_envelope
|
277
|
+
|
278
|
+
def generate_security_code invoice
|
279
|
+
# Build data set to generate security code
|
280
|
+
unsigned_code = ""
|
281
|
+
unsigned_code += invoice.pin
|
282
|
+
unsigned_code += invoice.time_sent_str " "
|
283
|
+
unsigned_code += invoice.issued_number.to_s
|
284
|
+
unsigned_code += invoice.issued_office.to_s
|
285
|
+
unsigned_code += invoice.issued_machine.to_s
|
286
|
+
unsigned_code += invoice.summed_total_str
|
287
|
+
# Sign with my private key
|
288
|
+
signed_code = OpenSSL::PKey::RSA.new(key_private).sign(OpenSSL::Digest::SHA1.new, unsigned_code)
|
289
|
+
# Create a MD5 digest from it
|
290
|
+
md5_digest = Digest::MD5.hexdigest(signed_code)
|
291
|
+
invoice.security_code = md5_digest
|
292
|
+
return md5_digest
|
293
|
+
end # generate_security_code
|
294
|
+
|
295
|
+
end # Communication
|
296
|
+
end # Fiscalizer
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Fiscalizer
|
2
|
+
class Fee
|
3
|
+
|
4
|
+
attr_accessor :name, :value
|
5
|
+
|
6
|
+
def initialize name: "", value: 0.0
|
7
|
+
@name = name
|
8
|
+
@value = value
|
9
|
+
end # initialize
|
10
|
+
|
11
|
+
def value
|
12
|
+
return @value.to_f.round(2)
|
13
|
+
end # value
|
14
|
+
|
15
|
+
def value_str
|
16
|
+
return ("%15.2f" % value).strip
|
17
|
+
end # value_str
|
18
|
+
|
19
|
+
end # Fee
|
20
|
+
end # Fiscalizer
|