fortnox-api 0.1.0 → 0.2.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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -2
  3. data/.env.test +3 -0
  4. data/.rubocop.yml +32 -23
  5. data/.travis.yml +3 -2
  6. data/Guardfile +2 -2
  7. data/LICENSE.txt +165 -22
  8. data/README.md +56 -37
  9. data/fortnox-api.gemspec +9 -6
  10. data/lib/fortnox/api.rb +18 -2
  11. data/lib/fortnox/api/base.rb +5 -3
  12. data/lib/fortnox/api/environment_validation.rb +51 -6
  13. data/lib/fortnox/api/mappers.rb +49 -0
  14. data/lib/fortnox/api/mappers/base.rb +47 -0
  15. data/lib/fortnox/api/mappers/base/from_json.rb +78 -0
  16. data/lib/fortnox/api/mappers/base/to_json.rb +65 -0
  17. data/lib/fortnox/api/mappers/customer.rb +27 -0
  18. data/lib/fortnox/api/mappers/default_delivery_types.rb +13 -0
  19. data/lib/fortnox/api/mappers/default_templates.rb +15 -0
  20. data/lib/fortnox/api/mappers/edi_information.rb +22 -0
  21. data/lib/fortnox/api/mappers/email_information.rb +18 -0
  22. data/lib/fortnox/api/mappers/invoice.rb +27 -0
  23. data/lib/fortnox/api/mappers/invoice_row.rb +20 -0
  24. data/lib/fortnox/api/mappers/order.rb +23 -0
  25. data/lib/fortnox/api/mappers/order_row.rb +16 -0
  26. data/lib/fortnox/api/models.rb +2 -0
  27. data/lib/fortnox/api/models/base.rb +56 -13
  28. data/lib/fortnox/api/models/customer.rb +112 -101
  29. data/lib/fortnox/api/models/document_base.rb +189 -0
  30. data/lib/fortnox/api/models/invoice.rb +29 -195
  31. data/lib/fortnox/api/models/label.rb +17 -0
  32. data/lib/fortnox/api/models/order.rb +27 -0
  33. data/lib/fortnox/api/repositories.rb +2 -0
  34. data/lib/fortnox/api/repositories/base.rb +4 -5
  35. data/lib/fortnox/api/repositories/base/loaders.rb +22 -14
  36. data/lib/fortnox/api/repositories/base/savers.rb +30 -16
  37. data/lib/fortnox/api/repositories/customer.rb +3 -25
  38. data/lib/fortnox/api/repositories/invoice.rb +3 -22
  39. data/lib/fortnox/api/repositories/order.rb +16 -0
  40. data/lib/fortnox/api/request_handling.rb +3 -3
  41. data/lib/fortnox/api/types.rb +44 -0
  42. data/lib/fortnox/api/types/default_delivery_types.rb +20 -0
  43. data/lib/fortnox/api/types/default_templates.rb +23 -0
  44. data/lib/fortnox/api/types/defaulted.rb +11 -0
  45. data/lib/fortnox/api/types/document_row.rb +65 -0
  46. data/lib/fortnox/api/types/edi_information.rb +29 -0
  47. data/lib/fortnox/api/types/email_information.rb +26 -0
  48. data/lib/fortnox/api/types/enums.rb +75 -0
  49. data/lib/fortnox/api/types/invoice_row.rb +19 -0
  50. data/lib/fortnox/api/types/model.rb +40 -0
  51. data/lib/fortnox/api/types/nullable.rb +21 -0
  52. data/lib/fortnox/api/types/order_row.rb +16 -0
  53. data/lib/fortnox/api/types/required.rb +13 -0
  54. data/lib/fortnox/api/types/sized.rb +25 -0
  55. data/lib/fortnox/api/version.rb +1 -1
  56. data/spec/fortnox/api/base_spec.rb +85 -14
  57. data/spec/fortnox/api/mappers/base/from_json_spec.rb +70 -0
  58. data/spec/fortnox/api/mappers/base/to_json_spec.rb +76 -0
  59. data/spec/fortnox/api/mappers/base_spec.rb +156 -0
  60. data/spec/fortnox/api/mappers/contexts/json_conversion.rb +56 -0
  61. data/spec/fortnox/api/mappers/customer_spec.rb +25 -0
  62. data/spec/fortnox/api/mappers/default_delivery_types_spec.rb +12 -0
  63. data/spec/fortnox/api/mappers/edi_information_spec.rb +21 -0
  64. data/spec/fortnox/api/mappers/email_information_spec.rb +17 -0
  65. data/spec/fortnox/api/mappers/examples/mapper.rb +26 -0
  66. data/spec/fortnox/api/mappers/invoice_row_spec.rb +19 -0
  67. data/spec/fortnox/api/mappers/invoice_spec.rb +24 -0
  68. data/spec/fortnox/api/mappers/order_row_spec.rb +14 -0
  69. data/spec/fortnox/api/mappers/order_spec.rb +20 -0
  70. data/spec/fortnox/api/models/base_spec.rb +44 -22
  71. data/spec/fortnox/api/models/customer_spec.rb +9 -0
  72. data/spec/fortnox/api/models/examples/document_base.rb +13 -0
  73. data/spec/fortnox/api/models/examples/model.rb +13 -0
  74. data/spec/fortnox/api/models/invoice_spec.rb +7 -31
  75. data/spec/fortnox/api/models/order_spec.rb +13 -0
  76. data/spec/fortnox/api/repositories/customer_spec.rb +20 -76
  77. data/spec/fortnox/api/repositories/examples/all.rb +17 -0
  78. data/spec/fortnox/api/repositories/examples/find.rb +25 -0
  79. data/spec/fortnox/api/repositories/examples/only.rb +42 -0
  80. data/spec/fortnox/api/repositories/examples/save.rb +69 -0
  81. data/spec/fortnox/api/repositories/examples/save_with_nested_model.rb +32 -0
  82. data/spec/fortnox/api/repositories/examples/save_with_specially_named_attribute.rb +27 -0
  83. data/spec/fortnox/api/repositories/examples/search.rb +31 -0
  84. data/spec/fortnox/api/repositories/invoice_spec.rb +36 -5
  85. data/spec/fortnox/api/repositories/order_spec.rb +35 -0
  86. data/spec/fortnox/api/types/account_number_spec.rb +28 -0
  87. data/spec/fortnox/api/types/default_delivery_types_spec.rb +10 -0
  88. data/spec/fortnox/api/types/edi_information_spec.rb +13 -0
  89. data/spec/fortnox/api/types/email_information_spec.rb +13 -0
  90. data/spec/fortnox/api/types/email_spec.rb +29 -0
  91. data/spec/fortnox/api/types/enums_spec.rb +13 -0
  92. data/spec/fortnox/api/types/examples/document_row.rb +15 -0
  93. data/spec/fortnox/api/types/examples/enum.rb +48 -0
  94. data/spec/fortnox/api/types/examples/types.rb +9 -0
  95. data/spec/fortnox/api/types/house_work_types_spec.rb +60 -0
  96. data/spec/fortnox/api/types/invoice_row_spec.rb +9 -0
  97. data/spec/fortnox/api/types/model_spec.rb +56 -0
  98. data/spec/fortnox/api/types/nullable_spec.rb +57 -0
  99. data/spec/fortnox/api/types/order_row_spec.rb +13 -0
  100. data/spec/fortnox/api/types/required_spec.rb +42 -0
  101. data/spec/fortnox/api/types/sized_spec.rb +74 -0
  102. data/spec/fortnox/api_spec.rb +16 -15
  103. data/spec/spec_helper.rb +19 -9
  104. data/spec/support/helpers/dummy_class_helper.rb +19 -0
  105. data/spec/support/helpers/environment_helper.rb +7 -0
  106. data/spec/support/helpers/repository_helper.rb +8 -0
  107. data/spec/support/helpers/when_performing_helper.rb +5 -0
  108. data/spec/support/matchers.rb +1 -1
  109. data/spec/support/matchers/type.rb +17 -0
  110. data/spec/support/matchers/type/attribute_matcher.rb +39 -0
  111. data/spec/support/matchers/type/enum_matcher.rb +21 -0
  112. data/spec/support/matchers/type/have_account_number_matcher.rb +21 -0
  113. data/spec/support/matchers/type/have_country_code_matcher.rb +13 -0
  114. data/spec/support/matchers/type/have_currency_matcher.rb +7 -0
  115. data/spec/support/matchers/type/have_customer_type_matcher.rb +13 -0
  116. data/spec/support/matchers/type/have_default_delivery_type_matcher.rb +7 -0
  117. data/spec/support/matchers/type/have_discount_type_matcher.rb +7 -0
  118. data/spec/support/matchers/type/have_email_matcher.rb +22 -0
  119. data/spec/support/matchers/type/have_house_work_type_matcher.rb +7 -0
  120. data/spec/support/matchers/type/have_nullable_date_matcher.rb +58 -0
  121. data/spec/support/matchers/type/have_nullable_matcher.rb +52 -0
  122. data/spec/support/matchers/type/have_nullable_string_matcher.rb +49 -0
  123. data/spec/support/matchers/type/have_sized_float_matcher.rb +8 -0
  124. data/spec/support/matchers/type/have_sized_integer_matcher.rb +8 -0
  125. data/spec/support/matchers/type/have_sized_string_matcher.rb +35 -0
  126. data/spec/support/matchers/type/have_vat_type_matcher.rb +7 -0
  127. data/spec/support/matchers/type/numeric_matcher.rb +50 -0
  128. data/spec/support/matchers/type/require_attribute_matcher.rb +69 -0
  129. data/spec/support/matchers/type/type_matcher.rb +38 -0
  130. data/spec/vcr_cassettes/customers/all.yml +119 -9
  131. data/spec/vcr_cassettes/customers/find_id_1.yml +8 -9
  132. data/spec/vcr_cassettes/customers/find_new.yml +46 -0
  133. data/spec/vcr_cassettes/customers/save_new.yml +9 -11
  134. data/spec/vcr_cassettes/customers/save_old.yml +9 -12
  135. data/spec/vcr_cassettes/customers/save_with_specially_named_attribute.yml +45 -0
  136. data/spec/vcr_cassettes/customers/search_by_name.yml +66 -0
  137. data/spec/vcr_cassettes/customers/search_miss.yml +45 -0
  138. data/spec/vcr_cassettes/invoices/all.yml +104 -0
  139. data/spec/vcr_cassettes/invoices/filter_hit.yml +46 -0
  140. data/spec/vcr_cassettes/invoices/filter_invalid.yml +42 -0
  141. data/spec/vcr_cassettes/invoices/find_id_1.yml +47 -0
  142. data/spec/vcr_cassettes/invoices/find_new.yml +49 -0
  143. data/spec/vcr_cassettes/invoices/save_new.yml +48 -0
  144. data/spec/vcr_cassettes/invoices/save_old.yml +49 -0
  145. data/spec/vcr_cassettes/invoices/save_with_nested_model.yml +47 -0
  146. data/spec/vcr_cassettes/invoices/save_with_specially_named_attribute.yml +47 -0
  147. data/spec/vcr_cassettes/invoices/search_by_name.yml +48 -0
  148. data/spec/vcr_cassettes/invoices/search_miss.yml +45 -0
  149. data/spec/vcr_cassettes/orders/all.yml +144 -0
  150. data/spec/vcr_cassettes/orders/filter_hit.yml +48 -0
  151. data/spec/vcr_cassettes/orders/filter_invalid.yml +42 -0
  152. data/spec/vcr_cassettes/orders/find_id_1.yml +48 -0
  153. data/spec/vcr_cassettes/orders/find_new.yml +49 -0
  154. data/spec/vcr_cassettes/orders/house_work_type_babysitting.yml +47 -0
  155. data/spec/vcr_cassettes/orders/house_work_type_cleaning.yml +47 -0
  156. data/spec/vcr_cassettes/orders/house_work_type_construction.yml +47 -0
  157. data/spec/vcr_cassettes/orders/house_work_type_cooking.yml +43 -0
  158. data/spec/vcr_cassettes/orders/house_work_type_electricity.yml +47 -0
  159. data/spec/vcr_cassettes/orders/house_work_type_gardening.yml +47 -0
  160. data/spec/vcr_cassettes/orders/house_work_type_glassmetalwork.yml +47 -0
  161. data/spec/vcr_cassettes/orders/house_work_type_grounddrainagework.yml +47 -0
  162. data/spec/vcr_cassettes/orders/house_work_type_hvac.yml +47 -0
  163. data/spec/vcr_cassettes/orders/house_work_type_masonry.yml +47 -0
  164. data/spec/vcr_cassettes/orders/house_work_type_othercare.yml +47 -0
  165. data/spec/vcr_cassettes/orders/house_work_type_othercosts.yml +47 -0
  166. data/spec/vcr_cassettes/orders/house_work_type_paintingwallpapering.yml +47 -0
  167. data/spec/vcr_cassettes/orders/house_work_type_snowplowing.yml +47 -0
  168. data/spec/vcr_cassettes/orders/house_work_type_textileclothing.yml +47 -0
  169. data/spec/vcr_cassettes/orders/house_work_type_tutoring.yml +43 -0
  170. data/spec/vcr_cassettes/orders/save_new.yml +48 -0
  171. data/spec/vcr_cassettes/orders/save_old.yml +49 -0
  172. data/spec/vcr_cassettes/orders/save_with_nested_model.yml +47 -0
  173. data/spec/vcr_cassettes/orders/search_by_name.yml +47 -0
  174. data/spec/vcr_cassettes/orders/search_miss.yml +45 -0
  175. metadata +301 -71
  176. data/lib/fortnox/api/models/attributes/country_code.rb +0 -17
  177. data/lib/fortnox/api/models/attributes/currency.rb +0 -17
  178. data/lib/fortnox/api/models/edi_information.rb +0 -28
  179. data/lib/fortnox/api/models/email_information.rb +0 -25
  180. data/lib/fortnox/api/models/row.rb +0 -82
  181. data/lib/fortnox/api/repositories/base/json_convertion.rb +0 -68
  182. data/lib/fortnox/api/repositories/base/options.rb +0 -33
  183. data/lib/fortnox/api/validators.rb +0 -1
  184. data/lib/fortnox/api/validators/attributes/country_code.rb +0 -42
  185. data/lib/fortnox/api/validators/attributes/currency.rb +0 -38
  186. data/lib/fortnox/api/validators/base.rb +0 -70
  187. data/lib/fortnox/api/validators/constant.rb +0 -21
  188. data/lib/fortnox/api/validators/customer.rb +0 -29
  189. data/lib/fortnox/api/validators/edi_information.rb +0 -11
  190. data/lib/fortnox/api/validators/email_information.rb +0 -19
  191. data/lib/fortnox/api/validators/invoice.rb +0 -33
  192. data/lib/fortnox/api/validators/row.rb +0 -22
  193. data/spec/fortnox/api/models/attributes/country_code_spec.rb +0 -23
  194. data/spec/fortnox/api/models/attributes/currency_spec.rb +0 -23
  195. data/spec/fortnox/api/models/attributes/dummy_model_context.rb +0 -9
  196. data/spec/fortnox/api/models/row_spec.rb +0 -13
  197. data/spec/fortnox/api/repositories/context.rb +0 -10
  198. data/spec/fortnox/api/repositories/examples.rb +0 -16
  199. data/spec/fortnox/api/validators/attributes/country_code_spec.rb +0 -9
  200. data/spec/fortnox/api/validators/attributes/currency_spec.rb +0 -9
  201. data/spec/fortnox/api/validators/attributes/examples_for_validate.rb +0 -29
  202. data/spec/fortnox/api/validators/base_spec.rb +0 -61
  203. data/spec/fortnox/api/validators/constant_spec.rb +0 -12
  204. data/spec/fortnox/api/validators/context.rb +0 -102
  205. data/spec/fortnox/api/validators/customer_spec.rb +0 -31
  206. data/spec/fortnox/api/validators/edi_information_spec.rb +0 -18
  207. data/spec/fortnox/api/validators/email_information_spec.rb +0 -26
  208. data/spec/fortnox/api/validators/invoice_spec.rb +0 -36
  209. data/spec/fortnox/api/validators/row_spec.rb +0 -27
  210. data/spec/fortnox/api/validators/validator_examples.rb +0 -20
  211. data/spec/support/matchers/models.rb +0 -27
  212. data/spec/support/matchers/validators.rb +0 -36
@@ -0,0 +1,29 @@
1
+ require "fortnox/api/types"
2
+
3
+ module Fortnox
4
+ module API
5
+ module Types
6
+ class EDIInformation < Types::Model
7
+ STUB = {}.freeze
8
+
9
+ #EDIGlobalLocationNumber Invoice address GLN for EDI
10
+ attribute :edi_global_location_number, Types::Nullable::String
11
+
12
+ #EDIGlobalLocationNumberDelivery Delivery address GLN for EDI
13
+ attribute :edi_global_location_number_delivery, Types::Nullable::String
14
+
15
+ #EDIInvoiceExtra1 Extra EDI Information
16
+ attribute :edi_invoice_extra1, Types::Nullable::String
17
+
18
+ #EDIInvoiceExtra2 Extra EDI Information
19
+ attribute :edi_invoice_extra2, Types::Nullable::String
20
+
21
+ #EDIOurElectronicReference Our electronic reference for EDI
22
+ attribute :edi_our_electronic_reference, Types::Nullable::String
23
+
24
+ #EDIYourElectronicReference Your electronic reference for EDI
25
+ attribute :edi_your_electronic_reference, Types::Nullable::String
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ require "fortnox/api/types"
2
+
3
+ module Fortnox
4
+ module API
5
+ module Types
6
+ class EmailInformation < Types::Model
7
+ STUB = {}.freeze
8
+
9
+ #EmailAddressTo Customer e-mail address. Must be a valid e-mail address. 1024 characters
10
+ attribute :email_address_to, Types::Email
11
+
12
+ #EmailAddressCC Customer e-mail address – Carbon copy. Must be a valid e-mail address. 1024 characters
13
+ attribute :email_address_cc, Types::Email
14
+
15
+ #EmailAddressBCC Customer e-mail address – Blind carbon copy. Must be a valid e-mail address. 1024 characters
16
+ attribute :email_address_bcc, Types::Email
17
+
18
+ #EmailSubject Subject of the e-mail, 100 characters.
19
+ attribute :email_subject, Types::Sized::String[ 100 ]
20
+
21
+ #EmailBody Body of the e-mail, 20000 characters.
22
+ attribute :email_body, Types::Sized::String[ 20000 ]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,75 @@
1
+ module Fortnox
2
+ module API
3
+ module Types
4
+
5
+ module EnumConstructors
6
+ def self.sized( size )
7
+ -> (value){ return nil if value == ''; value.to_s.upcase[0...size] unless value.nil? }
8
+ end
9
+
10
+ def self.default
11
+ -> (value){ return nil if value == ''; value.to_s.upcase unless value.nil? }
12
+ end
13
+ end
14
+
15
+ DiscountTypes = Types::Strict::String.enum(
16
+ 'AMOUNT','PERCENT'
17
+ )
18
+ CURRENT_HOUSE_WORK_TYPES = [
19
+ 'CONSTRUCTION','ELECTRICITY','GLASSMETALWORK','GROUNDDRAINAGEWORK',
20
+ 'MASONRY','PAINTINGWALLPAPERING','HVAC','CLEANING','TEXTILECLOTHING',
21
+ 'SNOWPLOWING','GARDENING','BABYSITTING','OTHERCARE', 'OTHERCOSTS'
22
+ ].freeze
23
+ LEGACY_HOUSE_WORK_TYPES = [ 'COOKING', 'TUTORING' ].freeze
24
+ HouseWorkTypes = Types::Strict::String.enum(
25
+ *(CURRENT_HOUSE_WORK_TYPES + LEGACY_HOUSE_WORK_TYPES)
26
+ )
27
+ CountryCodes = Types::Strict::String.enum(
28
+ 'AF','AX','AL','DZ','AS','AD','AO','AI','AQ','AG','AR','AM','AW','AU',
29
+ 'AT','AZ','BS','BH','BD','BB','BY','BE','BZ','BJ','BM','BT','BO','BQ',
30
+ 'BA','BW','BV','BR','IO','BN','BG','BF','BI','CV','KH','CM','CA','KY',
31
+ 'CF','TD','CL','CN','CX','CC','CO','KM','CG','CD','CK','CR','CI','HR',
32
+ 'CU','CW','CY','CZ','DK','DJ','DM','DO','EC','EG','SV','GQ','ER','EE',
33
+ 'ET','FK','FO','FJ','FI','FR','GF','PF','TF','GA','GM','GE','DE','GH',
34
+ 'GI','GR','GL','GD','GP','GU','GT','GG','GN','GW','GY','HT','HM','VA',
35
+ 'HN','HK','HU','IS','IN','ID','IR','IQ','IE','IM','IL','IT','JM','JP',
36
+ 'JE','JO','KZ','KE','KI','KP','KR','KW','KG','LA','LV','LB','LS','LR',
37
+ 'LY','LI','LT','LU','MO','MK','MG','MW','MY','MV','ML','MT','MH','MQ',
38
+ 'MR','MU','YT','MX','FM','MD','MC','MN','ME','MS','MA','MZ','MM','NA',
39
+ 'NR','NP','NL','NC','NZ','NI','NE','NG','NU','NF','MP','NO','OM','PK',
40
+ 'PW','PS','PA','PG','PY','PE','PH','PN','PL','PT','PR','QA','RE','RO',
41
+ 'RU','RW','BL','SH','KN','LC','MF','PM','VC','WS','SM','ST','SA','SN',
42
+ 'RS','SC','SL','SG','SX','SK','SI','SB','SO','ZA','GS','SS','ES','LK',
43
+ 'SD','SR','SJ','SZ','SE','CH','SY','TW','TJ','TZ','TH','TL','TG','TK',
44
+ 'TO','TT','TN','TR','TM','TC','TV','UG','UA','AE','GB','US','UM','UY',
45
+ 'UZ','VU','VE','VN','VG','VI','WF','EH','YE','ZM','ZW'
46
+ )
47
+ Currencies = Types::Strict::String.enum(
48
+ 'AED','AFN','ALL','AMD','ANG','AOA','ARS','AUD','AWG','AZN','BAM','BBD',
49
+ 'BDT','BGN','BHD','BIF','BMD','BND','BOB','BOV','BRL','BSD','BTN','BWP',
50
+ 'BYR','BZD','CAD','CDF','CHE','CHF','CHW','CLF','CLP','CNY','COP','COU',
51
+ 'CRC','CUP','CVE','CZK','DJF','DKK','DOP','DZD','EGP','ERN','ETB','EUR',
52
+ 'FJD','FKP','GBP','GEL','GHS','GIP','GMD','GNF','GTQ','GYD','HKD','HNL',
53
+ 'HRK','HTG','HUF','IDR','ILS','INR','IQD','IRR','ISK','JMD','JOD','JPY',
54
+ 'KES','KGS','KHR','KUR','KMF','KPW','KRW','KWD','KYD','KZT','LAK','LBP',
55
+ 'LKR','LRD','LSL','LYD','MAD','MDL','MGA','MKD','MMK','MNT','MOP','MRO',
56
+ 'MUR','MVR','MWK','MXN','MXV','MYR','MZN','NAD','NGN','NIO','NOK','NPR',
57
+ 'NZD','OMR','PAB','PEN','PGK','PHP','PKR','PLN','PYG','QAR','RON','RSD',
58
+ 'RUB','RWF','SAR','SBD','SCR','SDG','SEK','SGD','SHP','SLL','SOS','SRD',
59
+ 'SSP','STD','SYP','SZL','THB','TJS','TMM','TND','TOP','TRY','TTD','TWD',
60
+ 'TZS','UAH','UGX','USD','USN','USS','UYU','UZS','VEF','VND','VUV','WST',
61
+ 'XAF','XAG','XAU','XBA','XBB','XBC','XBD','XCD','XDR','XFU','XOF','XPD',
62
+ 'XPF','XPT','XTS','XXX','YER','ZAR','ZMK','ZWD'
63
+ )
64
+ CustomerTypes = Types::Strict::String.enum(
65
+ 'PRIVATE', 'COMPANY'
66
+ )
67
+ VATTypes = Types::Strict::String.enum(
68
+ 'SEVAT', 'SEREVERSEDVAT', 'EUREVERSEDVAT', 'EUVAT', 'EXPORT'
69
+ )
70
+ DefaultDeliveryTypeValues = Types::Strict::String.enum(
71
+ 'PRINT', 'EMAIL', 'PRINTSERVICE'
72
+ )
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,19 @@
1
+ require "fortnox/api/types/document_row"
2
+
3
+ module Fortnox
4
+ module API
5
+ module Types
6
+ class InvoiceRow < Fortnox::API::Types::Model
7
+ STUB = {}.freeze
8
+
9
+ DocumentRow.ify self
10
+
11
+ #PriceExcludingVAT Price per unit excluding VAT.
12
+ attribute :price_excluding_vat, Types::Nullable::Float.with( private: true )
13
+
14
+ #TotalExcludingVAT Total amount for the row excluding VAT.
15
+ attribute :total_excluding_vat, Types::Nullable::Float.with( private: true )
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ module Fortnox
2
+ module API
3
+ module Types
4
+ class Model < Dry::Struct
5
+ constructor_type(:schema)
6
+
7
+ def initialize( input_attributes )
8
+ if (missing_key = first_missing_required_key( input_attributes ))
9
+ raise Fortnox::API::MissingAttributeError.new( "Missing attribute #{ missing_key.inspect } in attributes: #{ input_attributes }" )
10
+ end
11
+
12
+ super
13
+ end
14
+
15
+ private
16
+
17
+ def missing_keys( attributes )
18
+ non_nil_attributes = attributes.select{ |_,value| !value.nil? }
19
+
20
+ attribute_keys = non_nil_attributes.keys
21
+ schema_keys = self.class.schema.keys
22
+
23
+ schema_keys - attribute_keys
24
+ end
25
+
26
+ def first_missing_required_key( attributes )
27
+ all_missing_keys = missing_keys( attributes )
28
+ missing_required = all_missing_keys.select do |name|
29
+ attribute = self.class.schema[ name ]
30
+ next unless attribute.respond_to? :options
31
+ attribute.options[:required]
32
+ end
33
+
34
+ missing_required.first
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ require 'date'
2
+
3
+ module Fortnox
4
+ module API
5
+ module Types
6
+
7
+ module Nullable
8
+ String = Types::Strict::String.optional.constructor{ |value| value.to_s unless value.nil? }
9
+ Float = Types::Strict::Float.optional.constructor{ |value| value.to_f unless value.nil? }
10
+ Integer = Types::Strict::Int.optional.constructor{ |value| value.to_i unless value.nil? }
11
+ Boolean = Types::Bool.optional.constructor{ |value| THE_TRUTH.fetch( value ){ false } }
12
+ # TODO: Improve parsing!
13
+ # In case date parsing fails, ArgumentError is thrown. Currently, it is rescued in Repository::Loaders.find.
14
+ # That method assumes that the exception is due to invalid argument to the find method, which is not the case
15
+ # if it is raised from here!
16
+ Date = Types::Date.optional.constructor{ |value| ::Date.parse( value.to_s ) unless value.nil? || value == '' }
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require "fortnox/api/types/document_row"
2
+
3
+ module Fortnox
4
+ module API
5
+ module Types
6
+ class OrderRow < Fortnox::API::Types::Model
7
+ STUB = { ordered_quantity: 0 }.freeze
8
+
9
+ DocumentRow.ify self
10
+
11
+ #OrderedQuantity Ordered quantity
12
+ attribute :ordered_quantity, Types::Required::Float
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module Fortnox
2
+ module API
3
+ module Types
4
+
5
+ module Required
6
+ String = Types::Strict::String.constructor{ |value| value.to_s unless value.nil? }.with( required: true )
7
+ Integer = Types::Strict::Int.constructor{ |value| value.to_i unless value.nil? }.with( required: true )
8
+ Float = Types::Strict::Float.constructor{ |value| value.to_f unless value.nil? }.with( required: true )
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Fortnox
2
+ module API
3
+ module Types
4
+ module Sized
5
+ module String
6
+ def self.[]( size )
7
+ Types::Strict::String.constrained( max_size: size ).optional.constructor{ |value| value.to_s unless value.nil? }
8
+ end
9
+ end
10
+
11
+ module Integer
12
+ def self.[]( low, high )
13
+ Types::Strict::Int.constrained( gteq: low, lteq: high ).optional.constructor{ |value| value.to_i unless value.nil? }
14
+ end
15
+ end
16
+
17
+ module Float
18
+ def self.[]( low, high )
19
+ Types::Strict::Float.constrained( gteq: low, lteq: high ).optional.constructor{ |value| value.to_f unless value.nil? }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  module Fortnox
2
2
  module API
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0".freeze
4
4
  end
5
5
  end
@@ -2,20 +2,28 @@ require 'spec_helper'
2
2
  require 'fortnox/api'
3
3
 
4
4
  describe Fortnox::API::Base do
5
+ include Helpers::Environment
5
6
 
6
7
  describe 'creation' do
8
+ before do
9
+ stub_environment('FORTNOX_API_BASE_URL' => nil,
10
+ 'FORTNOX_API_CLIENT_SECRET' => nil,
11
+ 'FORTNOX_API_ACCESS_TOKEN' => nil)
12
+ end
7
13
 
8
- subject{ ->{ Fortnox::API::Base.new() } }
14
+ subject{ ->{ described_class.new() } }
9
15
 
10
16
  context 'without FORTNOX_API_BASE_URL' do
11
- before{ ENV['FORTNOX_API_BASE_URL'] = nil }
17
+ before do
18
+ stub_environment('FORTNOX_API_BASE_URL' => nil)
19
+ end
12
20
 
13
21
  it{ is_expected.to raise_error( ArgumentError, /base url/ ) }
14
22
  end
15
23
 
16
24
  context 'without FORTNOX_API_CLIENT_SECRET' do
17
25
  before do
18
- stub_const('ENV', ENV.to_hash.merge('FORTNOX_API_BASE_URL' => 'xxx'))
26
+ stub_environment('FORTNOX_API_BASE_URL' => 'xxx')
19
27
  end
20
28
 
21
29
  it{ is_expected.to raise_error( ArgumentError, /client secret/ ) }
@@ -23,8 +31,8 @@ describe Fortnox::API::Base do
23
31
 
24
32
  context 'without FORTNOX_API_ACCESS_TOKEN' do
25
33
  before do
26
- stub_const('ENV', ENV.to_hash.merge('FORTNOX_API_BASE_URL' => 'xxx',
27
- 'FORTNOX_API_CLIENT_SECRET' => 'xxx'))
34
+ stub_environment('FORTNOX_API_BASE_URL' => 'xxx',
35
+ 'FORTNOX_API_CLIENT_SECRET' => 'xxx')
28
36
  end
29
37
 
30
38
  it{ is_expected.to raise_error( ArgumentError, /access token/ ) }
@@ -34,9 +42,11 @@ describe Fortnox::API::Base do
34
42
 
35
43
  context 'making a request including the proper headers' do
36
44
  before do
37
- ENV['FORTNOX_API_BASE_URL'] = 'http://api.fortnox.se/3'
38
- ENV['FORTNOX_API_CLIENT_SECRET'] = 'P5K5vE3Kun'
39
- ENV['FORTNOX_API_ACCESS_TOKEN'] = '3f08d038-f380-4893-94a0-a08f6e60e67a'
45
+ stub_environment(
46
+ 'FORTNOX_API_BASE_URL' => 'http://api.fortnox.se/3',
47
+ 'FORTNOX_API_CLIENT_SECRET' => 'P5K5vE3Kun',
48
+ 'FORTNOX_API_ACCESS_TOKEN' => '3f08d038-f380-4893-94a0-a08f6e60e67a'
49
+ )
40
50
 
41
51
  stub_request(
42
52
  :get,
@@ -53,17 +63,78 @@ describe Fortnox::API::Base do
53
63
  )
54
64
  end
55
65
 
56
- subject{ Fortnox::API::Base.new.get( '/test', { body: '' }) }
66
+ subject{ described_class.new.get( '/test', { body: '' }) }
57
67
 
58
68
  it{ is_expected.to be_nil }
59
69
  end
60
70
 
71
+ describe 'making requests with multiple access tokens' do
72
+
73
+ before do
74
+ stub_environment(
75
+ 'FORTNOX_API_BASE_URL' => 'http://api.fortnox.se/3',
76
+ 'FORTNOX_API_CLIENT_SECRET' => 'P5K5vE3Kun',
77
+ 'FORTNOX_API_ACCESS_TOKEN' => '3f08d038-f380-4893-94a0-a08f6e60e67a,aaee8217-0bbd-2e16-441f-668931d582cd'
78
+ )
79
+
80
+ stub_request(
81
+ :get,
82
+ 'http://api.fortnox.se/3/test',
83
+ ).with(
84
+ headers: {
85
+ 'Access-Token' => '3f08d038-f380-4893-94a0-a08f6e60e67a',
86
+ 'Client-Secret' => 'P5K5vE3Kun',
87
+ 'Content-Type' => 'application/json',
88
+ 'Accept' => 'application/json',
89
+ }
90
+ ).to_return(
91
+ status: 200,
92
+ body: '1'
93
+ )
94
+
95
+ stub_request(
96
+ :get,
97
+ 'http://api.fortnox.se/3/test',
98
+ ).with(
99
+ headers: {
100
+ 'Access-Token' => 'aaee8217-0bbd-2e16-441f-668931d582cd',
101
+ 'Client-Secret' => 'P5K5vE3Kun',
102
+ 'Content-Type' => 'application/json',
103
+ 'Accept' => 'application/json',
104
+ }
105
+ ).to_return(
106
+ status: 200,
107
+ body: '2'
108
+ )
109
+ end
110
+
111
+ context 'with subsequent requests on same object' do
112
+ let!(:response1){ api.get( '/test', body: '' ) }
113
+ let!(:response2){ api.get( '/test', body: '' ) }
114
+ let!(:response3){ api.get( '/test', body: '' ) }
115
+
116
+ let(:api){ described_class.new }
117
+
118
+ # rubocop:disable RSpec/MultipleExpectations
119
+ # All these checks must be run in same it-statement because
120
+ # of the random starting index.
121
+ it 'works' do
122
+ expect(response1).not_to eq( response2 )
123
+ expect(response1).to eq( response3 )
124
+ expect(response3).not_to eq( response2 )
125
+ end
126
+ # rubocop:enable RSpec/MultipleExpectations
127
+ end
128
+ end
129
+
61
130
  context 'raising error from remote server' do
62
131
 
63
132
  before do
64
- ENV['FORTNOX_API_BASE_URL'] = 'http://api.fortnox.se/3'
65
- ENV['FORTNOX_API_CLIENT_SECRET'] = 'P5K5vE3Kun'
66
- ENV['FORTNOX_API_ACCESS_TOKEN'] = '3f08d038-f380-4893-94a0-a08f6e60e67a'
133
+ stub_environment(
134
+ 'FORTNOX_API_BASE_URL' => 'http://api.fortnox.se/3',
135
+ 'FORTNOX_API_CLIENT_SECRET' => 'P5K5vE3Kun',
136
+ 'FORTNOX_API_ACCESS_TOKEN' => '3f08d038-f380-4893-94a0-a08f6e60e67a'
137
+ )
67
138
 
68
139
  stub_request(
69
140
  :post,
@@ -75,14 +146,14 @@ describe Fortnox::API::Base do
75
146
  )
76
147
  end
77
148
 
78
- subject{ ->{ Fortnox::API::Base.new.post( '/test', { body: '' }) } }
149
+ subject{ ->{ described_class.new.post( '/test', { body: '' }) } }
79
150
 
80
151
  it{ is_expected.to raise_error( Fortnox::API::RemoteServerError ) }
81
152
  it{ is_expected.to raise_error( 'Räkenskapsår finns inte upplagt. För att kunna skapa en faktura krävs det att det finns ett räkenskapsår' ) }
82
153
 
83
154
  context 'with debugging enabled' do
84
155
 
85
- around(:each) do |example|
156
+ around do |example|
86
157
  Fortnox::API.debugging = true
87
158
  example.run
88
159
  Fortnox::API.debugging = false
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+ require 'fortnox/api/mappers/base/from_json'
3
+ require 'fortnox/api/mappers/contexts/json_conversion'
4
+
5
+ describe Fortnox::API::Mapper::FromJSON do
6
+ include_context 'JSON conversion'
7
+
8
+ before do
9
+ module Test
10
+ class BaseMapper
11
+ include Fortnox::API::Mapper::FromJSON
12
+ end
13
+ end
14
+
15
+ register_mapper( :categories, Test::CategoryMapper )
16
+ register_mapper( :designer, Test::ProductDesignerMapper )
17
+ end
18
+
19
+ let( :mapper ){ Test::ProductMapper.new }
20
+
21
+ describe 'wrapped_json_hash_to_entity_hash' do
22
+ let( :entity_hash ){ mapper.wrapped_json_hash_to_entity_hash( wrapped_json_hash ) }
23
+ let( :wrapped_json_hash ) do
24
+ {
25
+ 'Product' => {
26
+ '@url': 'someurl@example.com',
27
+ 'Name': 'Ford Mustang',
28
+ 'VAT': 30000,
29
+ 'Categories': [{ 'Name' => 'Cars', 'ID' => '1' }, { 'Name' => 'Fast cars', 'ID' => '2' }],
30
+ 'Designer': { 'Name' => 'John Najjar', 'ID' => '23' }
31
+ }
32
+ }
33
+ end
34
+
35
+ specify 'converts keys without mapping correctly' do
36
+ expect( entity_hash[:name] ).to eq 'Ford Mustang'
37
+ end
38
+
39
+ specify 'converts keys with mapping correctly' do
40
+ expect( entity_hash[:vat] ).to eq 30000
41
+ end
42
+
43
+ specify 'converts keys starting with "@" correctly'
44
+ # do
45
+ # expect( entity_hash[:url] ).to eq 'someurl@example.com'
46
+ # end
47
+
48
+ context 'with nested models' do
49
+ let( :expected_nested_model_hash ) do
50
+ [{ name: 'Cars', id: '1' }, { name: 'Fast cars', id: '2' }]
51
+ end
52
+
53
+ specify 'are converted correctly' do
54
+ expect( entity_hash[:categories] ).to eq( expected_nested_model_hash )
55
+ end
56
+ end
57
+
58
+ context 'with nested model' do
59
+ let( :expected_nested_model_hash ){ { name: 'John Najjar', id: '23' } }
60
+
61
+ specify 'is converted correctly' do
62
+ expect( entity_hash[:designer] ).to eq( expected_nested_model_hash )
63
+ end
64
+ end
65
+ end
66
+
67
+ describe 'wrapped_json_collection_to_entities_hash' do
68
+ it 'is tested'
69
+ end
70
+ end