fortnox-api 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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