saft 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,493 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-struct"
4
+
5
+ # This file contains the xml intermediate structure
6
+ # The node names are generated from the XSD located in
7
+ # ./vendor/SAF-T_Financial_Schema1.10.xsd. There are at least a couple of nodes
8
+ # which are ignored since it has the comment "Not in use.". I don't look at it as
9
+ # a bug since we don't need then but we can implement when/if thay are ever
10
+ # needed.
11
+
12
+ module SAFT::V2::Types
13
+ module Types
14
+ include Dry.Types
15
+
16
+ Date = Params::Date
17
+ DateTime = Params::DateTime
18
+ Integer = Coercible::Integer
19
+ Decimal = Coercible::Decimal
20
+ Bool = Params::Bool
21
+ String = Strict::String
22
+ PositiveInteger = Types::Integer.constrained(gteq: 0)
23
+ end
24
+
25
+ # not Ideal as we are extending the global scope
26
+ Dry::Logic::Predicates.predicate(:digits?) do |num, input|
27
+ _sign, significant_digits, _base, exponent = input.split
28
+ n_significant_digits = significant_digits.length
29
+ n_significant_digits += -exponent if exponent.negative?
30
+ n_significant_digits <= num
31
+ end
32
+
33
+ Dry::Logic::Predicates.predicate(:fraction_digitst?) do |num, input|
34
+ digits?(num, input.frac)
35
+ end
36
+
37
+ # Aka base
38
+ module ObjectStrutures
39
+ def self.extended(base)
40
+ base.class_eval do
41
+ # not how I wanted to write this but this is how it turn out then I
42
+ # wanted to have different level of strictness for types like
43
+ # SAFmiddle1textType
44
+
45
+ klass = Class.new(Dry::Struct) do
46
+ attribute(:amount, base::SAFmonetaryType)
47
+
48
+ attribute(:currency_code, base::ISOCurrencyCode.optional.meta(omittable: true))
49
+ attribute(:currency_amount, base::SAFmonetaryType.optional.meta(omittable: true))
50
+ attribute(:exchange_rate, base::SAFexchangerateType.optional.meta(omittable: true))
51
+ end
52
+
53
+ base.const_set(:AmountStructure, klass)
54
+
55
+ klass = Class.new(Dry::Struct) do
56
+ attribute(:analysis_type, base::SAFcodeType)
57
+ attribute(:analysis_id, base::SAFlongtextType)
58
+ attribute(:analysis_amount, base::AmountStructure.optional.meta(omittable: true))
59
+ end
60
+
61
+ base.const_set(:AnalysisStructure, klass)
62
+
63
+ klass = Class.new(Dry::Struct) do
64
+ attribute(:analysis_type, base::SAFcodeType)
65
+ attribute(:analysis_id, base::SAFlongtextType)
66
+ end
67
+
68
+ base.const_set(:AnalysisPartyInfoStructure, klass)
69
+
70
+ klass = Class.new(Dry::Struct) do
71
+ attribute(:tax_type, base::SAFcodeType.optional.meta(omittable: true))
72
+ attribute(:tax_code, base::SAFmiddle1textType.optional.meta(omittable: true))
73
+ attribute(:tax_percentage, Types::Decimal.optional.meta(omittable: true))
74
+ attribute(:country, base::ISOCountryCode.optional.meta(omittable: true))
75
+ attribute(:tax_base, Types::Decimal.optional.meta(omittable: true))
76
+ attribute(:tax_base_description, base::SAFmiddle2textType.optional.meta(omittable: true))
77
+ attribute(:tax_amount, base::AmountStructure)
78
+ attribute(:tax_exemption_reason, base::SAFmiddle2textType.optional.meta(omittable: true))
79
+ attribute(:tax_declaration_period, base::SAFmiddle1textType.optional.meta(omittable: true))
80
+ end
81
+
82
+ base.const_set(:TaxInformationStructure, klass)
83
+
84
+ klass = Class.new(Dry::Struct) do
85
+ attribute(:tax_registration_number, base::SAFmiddle1textType)
86
+ # ignored since xsd states "Not in use."
87
+ # attribute :tax_type, base::SAFcodeType.optional.meta(omittable: true)
88
+ # attribute :tax_number, base::SAFmiddle1textType.optional.meta(omittable: true)
89
+ attribute(:tax_authority, base::SAFmiddle1textType.optional.meta(omittable: true))
90
+ attribute(:tax_verification_date, Types::Date.optional.meta(omittable: true))
91
+ end
92
+
93
+ base.const_set(:TaxIDStructure, klass)
94
+
95
+ klass = Class.new(Dry::Struct) do
96
+ attribute(:tax_reporting_jurisdiction, base::SAFmiddle1textType.optional.meta(omittable: true))
97
+ attribute(:company_entity, base::SAFmiddle2textType.optional.meta(omittable: true))
98
+ attribute(:selection_start_date, Types::Date.optional.meta(omittable: true))
99
+ attribute(:selection_end_date, Types::Date.optional.meta(omittable: true))
100
+ attribute(:period_start, Types::PositiveInteger.optional.meta(omittable: true))
101
+ attribute(:period_start_year, Types::Integer.constrained(gteq: 1970).constrained(lteq: 2100).optional.meta(omittable: true))
102
+ attribute(:period_end, Types::PositiveInteger.optional.meta(omittable: true))
103
+ attribute(:period_end_year, Types::Integer.constrained(gteq: 1970).constrained(lteq: 2100).optional.meta(omittable: true))
104
+ attribute(:document_type, base::SAFlongtextType.optional.meta(omittable: true))
105
+ attribute(:other_criterias, Types::Array.of(base::SAFlongtextType).optional.meta(omittable: true))
106
+ end
107
+
108
+ base.const_set(:SelectionCriteriaStructure, klass)
109
+
110
+ klass = Class.new(Dry::Struct) do
111
+ attribute(:title, base::SAFcodeType.optional.meta(omittable: true))
112
+ attribute(:first_name, base::SAFmiddle1textType)
113
+ attribute(:initials, base::SAFshorttextType.optional.meta(omittable: true))
114
+ attribute(:last_name_prefix, base::SAFshorttextType.optional.meta(omittable: true))
115
+ attribute(:last_name, base::SAFmiddle2textType)
116
+ attribute(:birth_name, base::SAFmiddle2textType.optional.meta(omittable: true))
117
+ attribute(:salutation, base::SAFshorttextType.optional.meta(omittable: true))
118
+ attribute(:other_titles, Types::Array.of(base::SAFshorttextType).optional.meta(omittable: true))
119
+ end
120
+
121
+ base.const_set(:PersonNameStructure, klass)
122
+
123
+ klass = Class.new(Dry::Struct) do
124
+ attribute(:days, Types::PositiveInteger.optional.meta(omittable: true))
125
+ attribute(:months, Types::PositiveInteger.optional.meta(omittable: true))
126
+ attribute(:cash_discount_days, Types::PositiveInteger.optional.meta(omittable: true))
127
+ attribute(:cash_discount_rate, Types::Decimal.constrained(gteq: 0).constrained(lteq: 100).optional.meta(omittable: true))
128
+ attribute(:free_billing_month, Types::Bool.optional.meta(omittable: true))
129
+ end
130
+
131
+ base.const_set(:PaymentTerms, klass)
132
+
133
+ klass = Class.new(Dry::Struct) do
134
+ attribute(:payment_terms, base::PaymentTerms.optional.meta(omittable: true))
135
+ attribute(:nace_code, base::SAFshorttextType.optional.meta(omittable: true))
136
+ attribute(:currency_code, base::ISOCurrencyCode.optional.meta(omittable: true))
137
+ attribute(:type, base::SAFmiddle1textType.optional.meta(omittable: true))
138
+ attribute(:status, base::SAFmiddle1textType.optional.meta(omittable: true))
139
+ attribute(:analyses, Types::Array.of(base::AnalysisPartyInfoStructure).optional.meta(omittable: true))
140
+ attribute(:notes, Types::String.optional.meta(omittable: true))
141
+ end
142
+
143
+ base.const_set(:PartyInfoStructure, klass)
144
+
145
+ klass = Class.new(Dry::Struct) do
146
+ attribute(:street_name, base::SAFmiddle2textType.optional.meta(omittable: true))
147
+ attribute(:number, base::SAFshorttextType.optional.meta(omittable: true))
148
+ attribute(:additional_address_detail, base::SAFmiddle2textType.optional.meta(omittable: true))
149
+ attribute(:building, base::SAFmiddle1textType.optional.meta(omittable: true))
150
+ attribute(:city, base::SAFmiddle1textType.optional.meta(omittable: true))
151
+ attribute(:postal_code, base::SAFshorttextType.optional.meta(omittable: true))
152
+ attribute(:region, base::SAFmiddle1textType.optional.meta(omittable: true))
153
+ attribute(:country, base::ISOCountryCode.optional.meta(omittable: true))
154
+ attribute(:address_type, base::AddressType.optional.meta(omittable: true))
155
+ end
156
+
157
+ base.const_set(:AddressStructure, klass)
158
+
159
+ klass = Class.new(Dry::Struct) do
160
+ attribute(:delivery_id, base::SAFmiddle1textType.optional.meta(omittable: true))
161
+ attribute(:delivery_date, Types::Date.optional.meta(omittable: true))
162
+ attribute(:warehouse_id, base::SAFmiddle1textType.optional.meta(omittable: true))
163
+ attribute(:location_id, base::SAFshorttextType.optional.meta(omittable: true))
164
+ attribute(:ucr, base::SAFmiddle1textType.optional.meta(omittable: true))
165
+ attribute(:address, base::AddressStructure.optional.meta(omittable: true))
166
+ end
167
+
168
+ base.const_set(:ShippingPointStructure, klass)
169
+
170
+ klass = Class.new(Dry::Struct) do
171
+ attribute(:contact_person, base::PersonNameStructure)
172
+ attribute(:telephone, base::SAFshorttextType.optional.meta(omittable: true))
173
+ attribute(:fax, base::SAFshorttextType.optional.meta(omittable: true))
174
+ attribute(:email, base::SAFmiddle2textType.optional.meta(omittable: true))
175
+ attribute(:website, Types::String.optional.meta(omittable: true))
176
+ attribute(:mobile_phone, base::SAFshorttextType.optional.meta(omittable: true))
177
+ end
178
+
179
+ base.const_set(:ContactInformationStructure, klass)
180
+
181
+ klass = Class.new(Dry::Struct) do
182
+ attribute(:iban_number, base::SAFmiddle1textType.optional.meta(omittable: true))
183
+ attribute(:bank_account_number, base::SAFmiddle1textType.optional.meta(omittable: true))
184
+ attribute(:bank_account_name, base::SAFmiddle2textType.optional.meta(omittable: true))
185
+ attribute(:sort_code, base::SAFshorttextType.optional.meta(omittable: true))
186
+ attribute(:bic, base::SAFshorttextType.optional.meta(omittable: true))
187
+ attribute(:currency_code, base::ISOCurrencyCode.optional.meta(omittable: true))
188
+ attribute(:general_ledger_account_id, base::SAFmiddle2textType.optional.meta(omittable: true))
189
+ end
190
+
191
+ base.const_set(:BankAccountStructure, klass)
192
+
193
+ klass = Class.new(Dry::Struct) do
194
+ attribute(:registration_number, base::SAFmiddle1textType.optional.meta(omittable: true))
195
+ attribute(:name, base::SAFmiddle2textType)
196
+ attribute(:addresses, Types::Array.of(base::AddressStructure))
197
+ attribute(:contacts, Types::Array.of(base::ContactInformationStructure).optional.meta(omittable: true))
198
+ attribute(:tax_registrations, Types::Array.of(base::TaxIDStructure).optional.meta(omittable: true))
199
+ attribute(:bank_accounts, Types::Array.of(base::BankAccountStructure).optional.meta(omittable: true))
200
+ end
201
+
202
+ base.const_set(:CompanyStructure, klass)
203
+
204
+ klass = Class.new(base::CompanyStructure) do
205
+ attribute(:registration_number, base::SAFmiddle1textType)
206
+ attribute(:name, base::SAFmiddle2textType)
207
+ attribute(:addresses, Types::Array.of(base::AddressStructure))
208
+ attribute(:contacts, Types::Array.of(base::ContactInformationStructure))
209
+ attribute(:tax_registrations, Types::Array.of(base::TaxIDStructure).optional.meta(omittable: true))
210
+ attribute(:bank_accounts, Types::Array.of(base::BankAccountStructure).optional.meta(omittable: true))
211
+ end
212
+
213
+ base.const_set(:CompanyHeaderStructure, klass)
214
+
215
+ klass = Class.new(Dry::Struct) do
216
+ attribute(:audit_file_version, base::SAFcodeType)
217
+ attribute(:audit_file_country, base::ISOCountryCode)
218
+ # ignored sine XSD said "not in use."
219
+ # attribute :audit_file_region, base::SAFcodeType.optional.meta(omittable: true)
220
+ attribute(:audit_file_date_created, Types::Date)
221
+ attribute(:software_company_name, base::SAFmiddle2textType)
222
+ attribute(:software_id, base::SAFlongtextType)
223
+ attribute(:software_version, base::SAFshorttextType)
224
+ attribute(:company, base::CompanyHeaderStructure)
225
+ attribute(:default_currency_code, base::ISOCurrencyCode)
226
+ attribute(:selection_criteria, base::SelectionCriteriaStructure.optional.meta(omittable: true))
227
+ attribute(:header_comment, base::SAFlongtextType.optional.meta(omittable: true))
228
+ end
229
+
230
+ base.const_set(:HeaderStructure, klass)
231
+
232
+ klass = Class.new(base::HeaderStructure) do
233
+ attribute(:tax_accounting_basis, base::SAFshorttextType)
234
+ attribute(:tax_entity, base::SAFmiddle2textType.optional.meta(omittable: true))
235
+ attribute(:user_id, base::SAFmiddle1textType.optional.meta(omittable: true))
236
+ attribute(:audit_file_sender, base::CompanyStructure.optional.meta(omittable: true))
237
+ end
238
+
239
+ base.const_set(:Header, klass)
240
+
241
+ klass = Class.new(Dry::Struct) do
242
+ attribute(:account_id, base::SAFmiddle2textType)
243
+ attribute(:account_description, base::SAFlongtextType)
244
+ attribute(:standard_account_id, base::SAFmiddle1textType.optional.meta(omittable: true))
245
+ attribute(:grouping_category, base::SAFmiddle1textType.optional.meta(omittable: true))
246
+ attribute(:grouping_code, base::SAFmiddle1textType.optional.meta(omittable: true))
247
+ attribute(:account_type, base::SAFshorttextType)
248
+ attribute(:account_creation_date, Types::Date.optional.meta(omittable: true))
249
+ attribute(:opening_debit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
250
+ attribute(:opening_credit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
251
+ attribute(:closing_debit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
252
+ attribute(:closing_credit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
253
+ end
254
+
255
+ base.const_set(:Account, klass)
256
+
257
+ klass = Class.new(base::CompanyStructure) do
258
+ attribute(:customer_id, base::SAFmiddle1textType)
259
+ attribute(:self_billing_indicator, base::SAFcodeType.optional.meta(omittable: true))
260
+ attribute(:account_id, base::SAFmiddle2textType.optional.meta(omittable: true))
261
+ attribute(:opening_debit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
262
+ attribute(:opening_credit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
263
+ attribute(:closing_debit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
264
+ attribute(:closing_credit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
265
+ attribute(:party_info, base::PartyInfoStructure.optional.meta(omittable: true))
266
+ end
267
+
268
+ base.const_set(:Customer, klass)
269
+
270
+ klass = Class.new(base::CompanyStructure) do
271
+ attribute(:supplier_id, base::SAFmiddle1textType)
272
+ attribute(:self_billing_indicator, base::SAFcodeType.optional.meta(omittable: true))
273
+ attribute(:account_id, base::SAFmiddle2textType.optional.meta(omittable: true))
274
+ attribute(:opening_debit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
275
+ attribute(:opening_credit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
276
+ attribute(:closing_debit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
277
+ attribute(:closing_credit_balance, base::SAFmonetaryType.optional.meta(omittable: true))
278
+ attribute(:party_info, base::PartyInfoStructure.optional.meta(omittable: true))
279
+ end
280
+
281
+ base.const_set(:Supplier, klass)
282
+
283
+ klass = Class.new(Dry::Struct) do
284
+ attribute(:tax_code, base::SAFmiddle1textType)
285
+ attribute(:effective_date, Types::Date.optional.meta(omittable: true))
286
+ attribute(:expiration_date, Types::Date.optional.meta(omittable: true))
287
+ attribute(:description, base::SAFlongtextType.optional.meta(omittable: true))
288
+ attribute(:tax_percentage, Types::Decimal.optional.meta(omittable: true))
289
+ # ignored because XSD states "Not in Use."
290
+ # attribute :flat_tax_rate, AmountStructure.optional.meta(omittable: true)
291
+ attribute(:country, base::ISOCountryCode)
292
+ # ignored because XSD states "Not in Use."
293
+ # attribute :region, base::SAFcodeType.optional.meta(omittable: true)
294
+ attribute(:standard_tax_code, base::SAFmiddle1textType)
295
+ attribute(:compensation, Types::Bool.optional.meta(omittable: true))
296
+ attribute(:base_rates, Types::Array.of(Types::Decimal))
297
+ end
298
+
299
+ base.const_set(:TaxCodeDetails, klass)
300
+
301
+ klass = Class.new(Dry::Struct) do
302
+ attribute(:tax_type, base::SAFcodeType)
303
+ attribute(:description, base::SAFlongtextType)
304
+ attribute(:tax_code_details, Types::Array.of(base::TaxCodeDetails).constrained(min_size: 1))
305
+ end
306
+
307
+ base.const_set(:TaxTableEntry, klass)
308
+
309
+ klass = Class.new(Dry::Struct) do
310
+ attribute(:analysis_type, base::SAFcodeType)
311
+ attribute(:analysis_type_description, base::SAFlongtextType)
312
+ attribute(:analysis_id, base::SAFmiddle1textType)
313
+ attribute(:analysis_id_description, base::SAFlongtextType)
314
+ attribute(:start_date, Types::Date.optional.meta(omittable: true))
315
+ attribute(:end_date, Types::Date.optional.meta(omittable: true))
316
+ attribute(:status, base::SAFmiddle1textType.optional.meta(omittable: true))
317
+ attribute(:analyses, Types::Array.of(base::AnalysisPartyInfoStructure).optional.meta(omittable: true))
318
+ end
319
+
320
+ base.const_set(:AnalysisTypeTableEntry, klass)
321
+
322
+ klass = Class.new(base::CompanyStructure) do
323
+ attribute(:owner_id, base::SAFmiddle1textType.optional.meta(omittable: true))
324
+ attribute(:account_id, base::SAFmiddle2textType.optional.meta(omittable: true))
325
+ end
326
+
327
+ base.const_set(:Owner, klass)
328
+
329
+ klass = Class.new(Dry::Struct) do
330
+ attribute(:general_ledger_accounts, Types::Array.of(base::Account).optional.meta(omittable: true))
331
+ # ignored since xsd said "Not in use."
332
+ # attribute :taxonomies, Taxonomies
333
+ attribute(:customers, Types::Array.of(base::Customer).optional.meta(omittable: true))
334
+ attribute(:suppliers, Types::Array.of(base::Supplier).optional.meta(omittable: true))
335
+ attribute(:tax_table, Types::Array.of(base::TaxTableEntry).optional.meta(omittable: true))
336
+ # ignored since xsd said "Not in use."
337
+ # attribute :uom_table, UOMTable
338
+ attribute(:analysis_type_table, Types::Array.of(base::AnalysisTypeTableEntry).optional.meta(omittable: true))
339
+ # ignored since xsd said "Not in use."
340
+ # attribute :movement_type_table, MovementTypeTable
341
+ # ignored since xsd said "Not in use."
342
+ # attribute :products, Products
343
+ # ignored since xsd said "Not in use."
344
+ # attribute :physical_stock, PhysicalStock
345
+ attribute(:owners, Types::Array.of(base::Owner).optional.meta(omittable: true))
346
+ # ignored since xsd said "Not in use."
347
+ # attribute :assets, Assets
348
+ end
349
+
350
+ base.const_set(:MasterFiles, klass)
351
+
352
+ klass = Class.new(Dry::Struct) do
353
+ attribute(:record_id, base::SAFshorttextType)
354
+ attribute(:account_id, base::SAFmiddle2textType)
355
+ attribute(:analyses, Types::Array.of(base::AnalysisStructure).optional.meta(omittable: true))
356
+ attribute(:value_date, Types::Date.optional.meta(omittable: true))
357
+ attribute(:source_document_id, base::SAFmiddle1textType.optional.meta(omittable: true))
358
+ attribute(:customer_id, base::SAFmiddle1textType.optional.meta(omittable: true))
359
+ attribute(:supplier_id, base::SAFmiddle1textType.optional.meta(omittable: true))
360
+ attribute(:description, base::SAFlongtextType)
361
+ attribute(:debit_amount, base::AmountStructure.optional.meta(omittable: true))
362
+ attribute(:credit_amount, base::AmountStructure.optional.meta(omittable: true))
363
+ attribute(:tax_information, Types::Array.of(base::TaxInformationStructure).optional.meta(omittable: true))
364
+ attribute(:reference_number, base::SAFmiddle1textType.optional.meta(omittable: true))
365
+ attribute(:cid, base::SAFmiddle1textType.optional.meta(omittable: true))
366
+ attribute(:due_date, Types::Date.optional.meta(omittable: true))
367
+ attribute(:quantity, base::SAFquantityType.optional.meta(omittable: true))
368
+ attribute(:cross_reference, base::SAFmiddle1textType.optional.meta(omittable: true))
369
+ attribute(:system_entry_time, Types::DateTime.optional.meta(omittable: true))
370
+ attribute(:owner_id, base::SAFmiddle1textType.optional.meta(omittable: true))
371
+ end
372
+
373
+ base.const_set(:Line, klass)
374
+
375
+ klass = Class.new(Dry::Struct) do
376
+ attribute(:transaction_id, base::SAFmiddle2textType)
377
+ attribute(:period, Types::Integer)
378
+ attribute(:period_year, Types::Integer)
379
+ attribute(:transaction_date, Types::Date)
380
+ attribute(:source_id, base::SAFmiddle1textType.optional.meta(omittable: true))
381
+ attribute(:transaction_type, base::SAFshorttextType.optional.meta(omittable: true))
382
+ attribute(:description, base::SAFlongtextType)
383
+ attribute(:batch_id, base::SAFmiddle1textType.optional.meta(omittable: true))
384
+ attribute(:system_entry_date, Types::Date)
385
+ attribute(:gl_posting_date, Types::Date)
386
+ # ignored since xsd said "Not in use."
387
+ # attribute :CustomerID, base::SAFmiddle1textType.optional.meta(omittable: true)
388
+ # ignored since xsd said "Not in use."
389
+ # attribute :SupplierID, base::SAFmiddle1textType.optional.meta(omittable: true)
390
+ attribute(:system_id, base::SAFshorttextType.optional.meta(omittable: true))
391
+ attribute(:lines, Types::Array.of(base::Line))
392
+ end
393
+
394
+ base.const_set(:Transaction, klass)
395
+
396
+ klass = Class.new(Dry::Struct) do
397
+ attribute(:journal_id, base::SAFshorttextType)
398
+ attribute(:description, base::SAFlongtextType)
399
+ attribute(:type, base::SAFcodeType)
400
+ attribute(:transactions, Types::Array.of(base::Transaction).optional.meta(omittable: true))
401
+ end
402
+
403
+ base.const_set(:Journal, klass)
404
+
405
+ klass = Class.new(Dry::Struct) do
406
+ attribute(:number_of_entries, Types::Integer)
407
+ attribute(:total_debit, base::SAFmonetaryType)
408
+ attribute(:total_credit, base::SAFmonetaryType)
409
+ attribute(:journals, Types::Array.of(base::Journal).optional.meta(omittable: true))
410
+ end
411
+
412
+ base.const_set(:GeneralLedgerEntries, klass)
413
+
414
+ klass = Class.new(Dry::Struct) do
415
+ attribute(:header, base::Header)
416
+ attribute(:master_files, base::MasterFiles.optional.meta(omittable: true))
417
+ attribute(:general_ledger_entries, base::GeneralLedgerEntries.optional.meta(omittable: true))
418
+ # ignored since xsd said "Not in use."
419
+ # attribute :source_documents, base::SourceDocuments
420
+ end
421
+
422
+ base.const_set(:AuditFile, klass)
423
+ end
424
+ end
425
+ end
426
+
427
+ module Relaxed
428
+ ISOCurrencyCode = Types::String
429
+ ISOCountryCode = Types::String
430
+ SAFcodeType = Types::String
431
+ SAFshorttextType = Types::String
432
+ SAFmiddle1textType = Types::String
433
+ SAFmiddle2textType = Types::String
434
+ SAFlongtextType = Types::String
435
+ SAFmonetaryType = Types::Decimal
436
+ SAFexchangerateType = Types::Decimal
437
+ SAFquantityType = Types::Decimal
438
+ SAFweightType = Types::Decimal
439
+ AddressType = Types::String
440
+
441
+ extend ObjectStrutures
442
+ end
443
+
444
+ module Strict
445
+ ISOCurrencyCode = Types::String.constrained(size: 3)
446
+ ISOCountryCode = Types::String.constrained(size: 2)
447
+ SAFcodeType = Types::String.constrained(max_size: 9)
448
+ SAFshorttextType = Types::String.constrained(max_size: 18)
449
+ SAFmiddle1textType = Types::String.constrained(max_size: 35)
450
+ SAFmiddle2textType = Types::String.constrained(max_size: 70)
451
+ SAFlongtextType = Types::String.constrained(max_size: 256)
452
+ SAFmonetaryType = Types::Decimal.constrained(digits: 18, fraction_digitst: 2)
453
+ SAFexchangerateType = Types::Decimal.constrained(digits: 18, fraction_digitst: 8)
454
+ SAFquantityType = Types::Decimal.constrained(digits: 22, fraction_digitst: 6)
455
+ SAFweightType = Types::Decimal.constrained(digits: 14, fraction_digitst: 3)
456
+ AddressType = Types::String.enum("StreetAddress", "PostalAddress", "BillingAddress", "ShipToAddress", "ShipFromAddress")
457
+
458
+ extend ObjectStrutures
459
+ end
460
+
461
+ module Sliced
462
+ def self.slice_string(length)
463
+ proc do |value|
464
+ if value.is_a?(::String)
465
+ value.slice(0, length)
466
+ else
467
+ value
468
+ end
469
+ end
470
+ end
471
+
472
+ ISOCurrencyCode = Types.Constructor(Strict::ISOCurrencyCode, &slice_string(3))
473
+ ISOCountryCode = Types.Constructor(Strict::ISOCountryCode, &slice_string(2))
474
+ SAFcodeType = Types.Constructor(Strict::SAFcodeType, &slice_string(9))
475
+ SAFshorttextType = Types.Constructor(Strict::SAFshorttextType, &slice_string(18))
476
+ SAFmiddle1textType = Types.Constructor(Strict::SAFmiddle1textType, &slice_string(35))
477
+ SAFmiddle2textType = Types.Constructor(Strict::SAFmiddle2textType, &slice_string(70))
478
+ SAFlongtextType = Types.Constructor(Strict::SAFlongtextType, &slice_string(256))
479
+ SAFmonetaryType = Types::Decimal.constrained(digits: 18, fraction_digitst: 2)
480
+ SAFexchangerateType = Types::Decimal.constrained(digits: 18, fraction_digitst: 8)
481
+ SAFquantityType = Types::Decimal.constrained(digits: 22, fraction_digitst: 6)
482
+ SAFweightType = Types::Decimal.constrained(digits: 14, fraction_digitst: 3)
483
+
484
+ AddressType = Types::String.enum("StreetAddress", "PostalAddress", "BillingAddress", "ShipToAddress", "ShipFromAddress")
485
+
486
+ extend ObjectStrutures
487
+ end
488
+
489
+ # Want to keep developer experience where you can call
490
+ # SAFT::V2::Types::AuditFile and get a Audit file. We have three different
491
+ # AuditFile. We have Strict/Relaxed/Sliced.
492
+ include Strict
493
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require "pathname"
5
+
6
+ XSD_PATH = Pathname.new(__dir__) + ".." + ".." + ".." + "vendor" + "SAF-T_Financial_Schema_NO_1.10.xsd"
7
+
8
+ module SAFT::V2
9
+ class XsdValidate
10
+ def initialize(content)
11
+ @xml_errors = []
12
+ doc = Nokogiri::XML(content)
13
+ @xml_errors.push(*doc.errors)
14
+ xsd = Nokogiri::XML::Schema(XSD_PATH)
15
+ @xml_errors.push(*xsd.validate(doc))
16
+ # All error are of type Nokogiri::XML::SyntaxError
17
+ # https://nokogiri.org/rdoc/Nokogiri/XML/SyntaxError.html
18
+ end
19
+
20
+ def valid?
21
+ @xml_errors.none?
22
+ end
23
+
24
+ def errors
25
+ @xml_errors
26
+ end
27
+ end
28
+ end
data/lib/saft/v2.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require "dry-struct"
5
+
6
+ module SAFT::V2
7
+ module Types
8
+ include Dry.Types
9
+ end
10
+
11
+ def self.parse(xml_content)
12
+ doc = Nokogiri::XML(xml_content)
13
+ doc.remove_namespaces!
14
+ Parse.call(doc).then { Types::AuditFile.call(_1) }
15
+ end
16
+
17
+ def self.scribe(audit_file)
18
+ rule = Types.Instance(Types::Relaxed::AuditFile) |
19
+ Types.Instance(Types::Strict::AuditFile) |
20
+ Types.Instance(Types::Sliced::AuditFile)
21
+
22
+ raise ArgumentError unless rule.valid?(audit_file)
23
+
24
+ Scribe.write_xml(audit_file)
25
+ end
26
+
27
+ def self.validate(xml_content)
28
+ XsdValidate.new(xml_content)
29
+ end
30
+
31
+ def self.to_html(audit_file)
32
+ raise ArgumentError unless audit_file.is_a?(Types::AuditFile)
33
+
34
+ HTML.render(audit_file)
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module SAFT
2
+ VERSION = "0.1.1"
3
+ end
data/lib/saft.rb ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+ require "nokogiri"
5
+
6
+ loader = Zeitwerk::Loader.for_gem
7
+ loader.inflector.inflect("saft" => "SAFT", "html" => "HTML")
8
+ loader.setup
9
+
10
+ loader.do_not_eager_load("#{__dir__}/saft/v2/html.rb")
11
+
12
+ module SAFT
13
+ def self.gem_root
14
+ Pathname(__dir__).parent
15
+ end
16
+
17
+ def self.nokogiri_save_setting=(value)
18
+ @nokogiri_save_setting = value
19
+ end
20
+
21
+ def self.nokogiri_save_setting
22
+ return @nokogiri_save_setting if defined?(@nokogiri_save_setting)
23
+
24
+ @nokogiri_save_setting = Nokogiri::XML::Node::SaveOptions::AS_XML
25
+ end
26
+ end
data/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "saft_gem",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "tailwindcss": "^3.2.4"
14
+ }
15
+ }