fortnox-api 0.9.2 → 1.0.0.rc2

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 (344) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +120 -95
  3. data/{LICENSE.txt → LICENSE.md} +0 -0
  4. data/README.md +245 -248
  5. data/bin/fortnox-setup +211 -0
  6. data/bin/fortnox-update-env +38 -0
  7. data/fortnox.gemspec +29 -0
  8. data/lib/fortnox/auth/thread_local.rb +28 -0
  9. data/lib/fortnox/collection.rb +23 -0
  10. data/lib/fortnox/mappers/country_code.rb +34 -0
  11. data/lib/fortnox/mappers/date.rb +21 -0
  12. data/lib/fortnox/mappers/document_row.rb +12 -0
  13. data/lib/fortnox/mappers/edi_information.rb +15 -0
  14. data/lib/fortnox/mappers/email_information.rb +11 -0
  15. data/lib/fortnox/mappers/invoice_row.rb +14 -0
  16. data/lib/fortnox/mappers/label_references.rb +24 -0
  17. data/lib/fortnox/mappers/order_row.rb +9 -0
  18. data/lib/fortnox/mappers/struct.rb +71 -0
  19. data/lib/fortnox/mappers/struct_array.rb +31 -0
  20. data/lib/fortnox/resource.rb +131 -0
  21. data/lib/fortnox/resources/article.rb +160 -0
  22. data/lib/fortnox/resources/customer.rb +209 -0
  23. data/lib/fortnox/resources/document.rb +202 -0
  24. data/lib/fortnox/resources/invoice.rb +92 -0
  25. data/lib/fortnox/resources/label.rb +15 -0
  26. data/lib/fortnox/resources/order.rb +38 -0
  27. data/lib/fortnox/resources/project.rb +41 -0
  28. data/lib/fortnox/resources/terms_of_payment.rb +23 -0
  29. data/lib/fortnox/resources/unit.rb +26 -0
  30. data/lib/fortnox/struct.rb +24 -0
  31. data/lib/fortnox/structs/default_delivery_types.rb +16 -0
  32. data/lib/fortnox/structs/default_templates.rb +19 -0
  33. data/lib/fortnox/structs/document_row.rb +62 -0
  34. data/lib/fortnox/structs/edi_information.rb +25 -0
  35. data/lib/fortnox/structs/email_information.rb +22 -0
  36. data/lib/fortnox/structs/invoice_row.rb +13 -0
  37. data/lib/fortnox/structs/order_row.rb +10 -0
  38. data/lib/fortnox/types.rb +129 -0
  39. data/lib/fortnox/{api/version.rb → version.rb} +1 -3
  40. data/lib/fortnox.rb +111 -0
  41. metadata +55 -580
  42. data/.codeclimate.yml +0 -20
  43. data/.env.template +0 -7
  44. data/.env.test +0 -11
  45. data/.gitignore +0 -20
  46. data/.rspec +0 -2
  47. data/.rubocop.yml +0 -41
  48. data/.tool-versions +0 -1
  49. data/.travis.yml +0 -32
  50. data/CLAUDE.md +0 -79
  51. data/CONTRIBUTE.md +0 -38
  52. data/DEVELOPER_README.md +0 -69
  53. data/Gemfile +0 -6
  54. data/Guardfile +0 -16
  55. data/Rakefile +0 -140
  56. data/bin/console +0 -22
  57. data/bin/fortnox +0 -285
  58. data/bin/get_tokens +0 -79
  59. data/bin/renew_tokens +0 -28
  60. data/docs/gotchas.md +0 -146
  61. data/fortnox-api.gemspec +0 -50
  62. data/lib/fortnox/api/mappers/article.rb +0 -23
  63. data/lib/fortnox/api/mappers/base/canonical_name_sym.rb +0 -21
  64. data/lib/fortnox/api/mappers/base/from_json.rb +0 -91
  65. data/lib/fortnox/api/mappers/base/to_json.rb +0 -66
  66. data/lib/fortnox/api/mappers/base.rb +0 -30
  67. data/lib/fortnox/api/mappers/customer.rb +0 -27
  68. data/lib/fortnox/api/mappers/default_delivery_types.rb +0 -15
  69. data/lib/fortnox/api/mappers/default_templates.rb +0 -16
  70. data/lib/fortnox/api/mappers/edi_information.rb +0 -23
  71. data/lib/fortnox/api/mappers/email_information.rb +0 -19
  72. data/lib/fortnox/api/mappers/invoice.rb +0 -29
  73. data/lib/fortnox/api/mappers/invoice_row.rb +0 -25
  74. data/lib/fortnox/api/mappers/order.rb +0 -26
  75. data/lib/fortnox/api/mappers/order_row.rb +0 -23
  76. data/lib/fortnox/api/mappers/project.rb +0 -17
  77. data/lib/fortnox/api/mappers/terms_of_payment.rb +0 -17
  78. data/lib/fortnox/api/mappers/unit.rb +0 -17
  79. data/lib/fortnox/api/mappers/value/array.rb +0 -18
  80. data/lib/fortnox/api/mappers/value/country_string.rb +0 -24
  81. data/lib/fortnox/api/mappers/value/date.rb +0 -11
  82. data/lib/fortnox/api/mappers/value/hash.rb +0 -16
  83. data/lib/fortnox/api/mappers/value/identity.rb +0 -18
  84. data/lib/fortnox/api/mappers.rb +0 -21
  85. data/lib/fortnox/api/models/article.rb +0 -134
  86. data/lib/fortnox/api/models/base.rb +0 -128
  87. data/lib/fortnox/api/models/customer.rb +0 -210
  88. data/lib/fortnox/api/models/document.rb +0 -189
  89. data/lib/fortnox/api/models/invoice.rb +0 -87
  90. data/lib/fortnox/api/models/label.rb +0 -19
  91. data/lib/fortnox/api/models/order.rb +0 -27
  92. data/lib/fortnox/api/models/project.rb +0 -42
  93. data/lib/fortnox/api/models/terms_of_payment.rb +0 -28
  94. data/lib/fortnox/api/models/unit.rb +0 -24
  95. data/lib/fortnox/api/models.rb +0 -9
  96. data/lib/fortnox/api/repositories/article.rb +0 -17
  97. data/lib/fortnox/api/repositories/authentication.rb +0 -61
  98. data/lib/fortnox/api/repositories/base/loaders.rb +0 -64
  99. data/lib/fortnox/api/repositories/base/savers.rb +0 -57
  100. data/lib/fortnox/api/repositories/base.rb +0 -93
  101. data/lib/fortnox/api/repositories/customer.rb +0 -17
  102. data/lib/fortnox/api/repositories/invoice.rb +0 -17
  103. data/lib/fortnox/api/repositories/order.rb +0 -17
  104. data/lib/fortnox/api/repositories/project.rb +0 -17
  105. data/lib/fortnox/api/repositories/terms_of_payment.rb +0 -17
  106. data/lib/fortnox/api/repositories/unit.rb +0 -17
  107. data/lib/fortnox/api/repositories.rb +0 -10
  108. data/lib/fortnox/api/request_handling.rb +0 -46
  109. data/lib/fortnox/api/types/default_delivery_types.rb +0 -20
  110. data/lib/fortnox/api/types/default_templates.rb +0 -23
  111. data/lib/fortnox/api/types/defaulted.rb +0 -11
  112. data/lib/fortnox/api/types/document_row.rb +0 -64
  113. data/lib/fortnox/api/types/edi_information.rb +0 -29
  114. data/lib/fortnox/api/types/email_information.rb +0 -26
  115. data/lib/fortnox/api/types/enums.rb +0 -116
  116. data/lib/fortnox/api/types/invoice_row.rb +0 -19
  117. data/lib/fortnox/api/types/model.rb +0 -37
  118. data/lib/fortnox/api/types/nullable.rb +0 -25
  119. data/lib/fortnox/api/types/order_row.rb +0 -16
  120. data/lib/fortnox/api/types/required.rb +0 -13
  121. data/lib/fortnox/api/types/shim/country_string.rb +0 -10
  122. data/lib/fortnox/api/types/sized.rb +0 -33
  123. data/lib/fortnox/api/types.rb +0 -144
  124. data/lib/fortnox/api.rb +0 -62
  125. data/spec/fortnox/api/mappers/article_spec.rb +0 -17
  126. data/spec/fortnox/api/mappers/base/canonical_name_sym_spec.rb +0 -36
  127. data/spec/fortnox/api/mappers/base/from_json_spec.rb +0 -70
  128. data/spec/fortnox/api/mappers/base/to_json_spec.rb +0 -68
  129. data/spec/fortnox/api/mappers/base_spec.rb +0 -154
  130. data/spec/fortnox/api/mappers/contexts/json_conversion.rb +0 -62
  131. data/spec/fortnox/api/mappers/customer_spec.rb +0 -27
  132. data/spec/fortnox/api/mappers/default_delivery_types_spec.rb +0 -14
  133. data/spec/fortnox/api/mappers/edi_information_spec.rb +0 -23
  134. data/spec/fortnox/api/mappers/email_information_spec.rb +0 -19
  135. data/spec/fortnox/api/mappers/examples/mapper.rb +0 -34
  136. data/spec/fortnox/api/mappers/invoice_row_spec.rb +0 -24
  137. data/spec/fortnox/api/mappers/invoice_spec.rb +0 -27
  138. data/spec/fortnox/api/mappers/order_row_spec.rb +0 -21
  139. data/spec/fortnox/api/mappers/order_spec.rb +0 -23
  140. data/spec/fortnox/api/mappers/project_spec.rb +0 -12
  141. data/spec/fortnox/api/mappers/terms_of_payment_spec.rb +0 -16
  142. data/spec/fortnox/api/mappers/unit_spec.rb +0 -56
  143. data/spec/fortnox/api/models/article_spec.rb +0 -9
  144. data/spec/fortnox/api/models/base_spec.rb +0 -117
  145. data/spec/fortnox/api/models/customer_spec.rb +0 -9
  146. data/spec/fortnox/api/models/examples/document_base.rb +0 -15
  147. data/spec/fortnox/api/models/examples/model.rb +0 -22
  148. data/spec/fortnox/api/models/invoice_spec.rb +0 -11
  149. data/spec/fortnox/api/models/order_spec.rb +0 -12
  150. data/spec/fortnox/api/models/project_spec.rb +0 -9
  151. data/spec/fortnox/api/models/terms_of_payment_spec.rb +0 -9
  152. data/spec/fortnox/api/models/unit_spec.rb +0 -33
  153. data/spec/fortnox/api/repositories/article_spec.rb +0 -80
  154. data/spec/fortnox/api/repositories/authentication_spec.rb +0 -103
  155. data/spec/fortnox/api/repositories/base_spec.rb +0 -168
  156. data/spec/fortnox/api/repositories/customer_spec.rb +0 -119
  157. data/spec/fortnox/api/repositories/examples/all.rb +0 -17
  158. data/spec/fortnox/api/repositories/examples/find.rb +0 -84
  159. data/spec/fortnox/api/repositories/examples/only.rb +0 -34
  160. data/spec/fortnox/api/repositories/examples/save.rb +0 -76
  161. data/spec/fortnox/api/repositories/examples/save_with_nested_model.rb +0 -28
  162. data/spec/fortnox/api/repositories/examples/save_with_specially_named_attribute.rb +0 -26
  163. data/spec/fortnox/api/repositories/examples/search.rb +0 -39
  164. data/spec/fortnox/api/repositories/invoice_spec.rb +0 -297
  165. data/spec/fortnox/api/repositories/order_spec.rb +0 -53
  166. data/spec/fortnox/api/repositories/project_spec.rb +0 -36
  167. data/spec/fortnox/api/repositories/terms_of_payment_spec.rb +0 -34
  168. data/spec/fortnox/api/repositories/unit_spec.rb +0 -39
  169. data/spec/fortnox/api/types/account_number_spec.rb +0 -35
  170. data/spec/fortnox/api/types/country_code_spec.rb +0 -42
  171. data/spec/fortnox/api/types/country_spec.rb +0 -67
  172. data/spec/fortnox/api/types/default_delivery_types_spec.rb +0 -12
  173. data/spec/fortnox/api/types/edi_information_spec.rb +0 -15
  174. data/spec/fortnox/api/types/email_information_spec.rb +0 -15
  175. data/spec/fortnox/api/types/email_spec.rb +0 -56
  176. data/spec/fortnox/api/types/enums_spec.rb +0 -17
  177. data/spec/fortnox/api/types/examples/document_row.rb +0 -25
  178. data/spec/fortnox/api/types/examples/enum.rb +0 -55
  179. data/spec/fortnox/api/types/examples/types.rb +0 -11
  180. data/spec/fortnox/api/types/housework_types_spec.rb +0 -149
  181. data/spec/fortnox/api/types/invoice_row_spec.rb +0 -11
  182. data/spec/fortnox/api/types/model_spec.rb +0 -69
  183. data/spec/fortnox/api/types/nullable_spec.rb +0 -79
  184. data/spec/fortnox/api/types/order_row_spec.rb +0 -15
  185. data/spec/fortnox/api/types/required_spec.rb +0 -36
  186. data/spec/fortnox/api/types/sales_account_spec.rb +0 -57
  187. data/spec/fortnox/api/types/sized_spec.rb +0 -76
  188. data/spec/fortnox/api_spec.rb +0 -66
  189. data/spec/spec_helper.rb +0 -35
  190. data/spec/support/helpers/configuration_helper.rb +0 -39
  191. data/spec/support/helpers/repository_helper.rb +0 -10
  192. data/spec/support/helpers.rb +0 -3
  193. data/spec/support/matchers/type/attribute_matcher.rb +0 -40
  194. data/spec/support/matchers/type/enum_matcher.rb +0 -23
  195. data/spec/support/matchers/type/have_account_number_matcher.rb +0 -23
  196. data/spec/support/matchers/type/have_currency_matcher.rb +0 -9
  197. data/spec/support/matchers/type/have_customer_type_matcher.rb +0 -15
  198. data/spec/support/matchers/type/have_default_delivery_type_matcher.rb +0 -9
  199. data/spec/support/matchers/type/have_discount_type_matcher.rb +0 -9
  200. data/spec/support/matchers/type/have_email_matcher.rb +0 -24
  201. data/spec/support/matchers/type/have_housework_type_matcher.rb +0 -9
  202. data/spec/support/matchers/type/have_nullable_date_matcher.rb +0 -60
  203. data/spec/support/matchers/type/have_nullable_matcher.rb +0 -54
  204. data/spec/support/matchers/type/have_nullable_string_matcher.rb +0 -47
  205. data/spec/support/matchers/type/have_sized_float_matcher.rb +0 -10
  206. data/spec/support/matchers/type/have_sized_integer_matcher.rb +0 -10
  207. data/spec/support/matchers/type/have_sized_string_matcher.rb +0 -36
  208. data/spec/support/matchers/type/have_vat_type_matcher.rb +0 -9
  209. data/spec/support/matchers/type/numeric_matcher.rb +0 -52
  210. data/spec/support/matchers/type/require_attribute_matcher.rb +0 -68
  211. data/spec/support/matchers/type/type_matcher.rb +0 -40
  212. data/spec/support/matchers/type.rb +0 -19
  213. data/spec/support/matchers.rb +0 -3
  214. data/spec/support/vcr_setup.rb +0 -25
  215. data/spec/vcr_cassettes/articles/all.yml +0 -67
  216. data/spec/vcr_cassettes/articles/find_by_hash_failure.yml +0 -62
  217. data/spec/vcr_cassettes/articles/find_failure.yml +0 -62
  218. data/spec/vcr_cassettes/articles/find_id_1.yml +0 -63
  219. data/spec/vcr_cassettes/articles/find_new.yml +0 -63
  220. data/spec/vcr_cassettes/articles/limits/quantity_in_stock_min_value.yml +0 -63
  221. data/spec/vcr_cassettes/articles/limits/quantity_in_stock_rounding_positive_value.yml +0 -63
  222. data/spec/vcr_cassettes/articles/multi_param_find_by_hash.yml +0 -62
  223. data/spec/vcr_cassettes/articles/save_new.yml +0 -63
  224. data/spec/vcr_cassettes/articles/save_old.yml +0 -63
  225. data/spec/vcr_cassettes/articles/save_with_specially_named_attribute.yml +0 -63
  226. data/spec/vcr_cassettes/articles/search_by_name.yml +0 -65
  227. data/spec/vcr_cassettes/articles/search_miss.yml +0 -62
  228. data/spec/vcr_cassettes/articles/search_with_special_char.yml +0 -62
  229. data/spec/vcr_cassettes/articles/single_param_find_by_hash.yml +0 -62
  230. data/spec/vcr_cassettes/authentication/expired_token.yml +0 -54
  231. data/spec/vcr_cassettes/authentication/invalid_authorization.yml +0 -57
  232. data/spec/vcr_cassettes/authentication/invalid_refresh_token.yml +0 -58
  233. data/spec/vcr_cassettes/authentication/valid_request.yml +0 -63
  234. data/spec/vcr_cassettes/customers/all.yml +0 -69
  235. data/spec/vcr_cassettes/customers/find_by_hash_failure.yml +0 -62
  236. data/spec/vcr_cassettes/customers/find_failure.yml +0 -62
  237. data/spec/vcr_cassettes/customers/find_id_1.yml +0 -64
  238. data/spec/vcr_cassettes/customers/find_new.yml +0 -63
  239. data/spec/vcr_cassettes/customers/find_with_sales_account.yml +0 -63
  240. data/spec/vcr_cassettes/customers/multi_param_find_by_hash.yml +0 -63
  241. data/spec/vcr_cassettes/customers/save_new.yml +0 -63
  242. data/spec/vcr_cassettes/customers/save_new_with_country_code_SE.yml +0 -64
  243. data/spec/vcr_cassettes/customers/save_new_with_idn_email.yml +0 -67
  244. data/spec/vcr_cassettes/customers/save_new_with_sales_account.yml +0 -63
  245. data/spec/vcr_cassettes/customers/save_old.yml +0 -63
  246. data/spec/vcr_cassettes/customers/save_with_specially_named_attribute.yml +0 -63
  247. data/spec/vcr_cassettes/customers/search_by_name.yml +0 -64
  248. data/spec/vcr_cassettes/customers/search_miss.yml +0 -62
  249. data/spec/vcr_cassettes/customers/search_with_special_char.yml +0 -62
  250. data/spec/vcr_cassettes/customers/single_param_find_by_hash.yml +0 -64
  251. data/spec/vcr_cassettes/invoices/all.yml +0 -96
  252. data/spec/vcr_cassettes/invoices/filter_hit.yml +0 -64
  253. data/spec/vcr_cassettes/invoices/filter_invalid.yml +0 -60
  254. data/spec/vcr_cassettes/invoices/find_by_hash_failure.yml +0 -62
  255. data/spec/vcr_cassettes/invoices/find_failure.yml +0 -62
  256. data/spec/vcr_cassettes/invoices/find_id_1.yml +0 -65
  257. data/spec/vcr_cassettes/invoices/find_new.yml +0 -65
  258. data/spec/vcr_cassettes/invoices/multi_param_find_by_hash.yml +0 -63
  259. data/spec/vcr_cassettes/invoices/row_delivered_quantity_decimals.yml +0 -65
  260. data/spec/vcr_cassettes/invoices/row_delivered_quantity_decimals_round_up.yml +0 -65
  261. data/spec/vcr_cassettes/invoices/row_description_limit.yml +0 -65
  262. data/spec/vcr_cassettes/invoices/row_price_limit.yml +0 -65
  263. data/spec/vcr_cassettes/invoices/row_price_limit_round_up.yml +0 -65
  264. data/spec/vcr_cassettes/invoices/save_new.yml +0 -65
  265. data/spec/vcr_cassettes/invoices/save_new_with_comments.yml +0 -65
  266. data/spec/vcr_cassettes/invoices/save_new_with_country.yml +0 -65
  267. data/spec/vcr_cassettes/invoices/save_new_with_country_GB.yml +0 -66
  268. data/spec/vcr_cassettes/invoices/save_new_with_country_Norge.yml +0 -65
  269. data/spec/vcr_cassettes/invoices/save_new_with_country_Norway.yml +0 -65
  270. data/spec/vcr_cassettes/invoices/save_new_with_country_Sverige.yml +0 -65
  271. data/spec/vcr_cassettes/invoices/save_new_with_country_VA.yml +0 -66
  272. data/spec/vcr_cassettes/invoices/save_new_with_country_VI.yml +0 -66
  273. data/spec/vcr_cassettes/invoices/save_new_with_country_empty_string.yml +0 -65
  274. data/spec/vcr_cassettes/invoices/save_new_with_country_nil.yml +0 -65
  275. data/spec/vcr_cassettes/invoices/save_new_with_unsaved_parent.yml +0 -65
  276. data/spec/vcr_cassettes/invoices/save_old.yml +0 -65
  277. data/spec/vcr_cassettes/invoices/save_old_with_empty_comments.yml +0 -65
  278. data/spec/vcr_cassettes/invoices/save_old_with_empty_country.yml +0 -65
  279. data/spec/vcr_cassettes/invoices/save_old_with_nil_comments.yml +0 -65
  280. data/spec/vcr_cassettes/invoices/save_old_with_nil_country.yml +0 -65
  281. data/spec/vcr_cassettes/invoices/save_with_nested_model.yml +0 -65
  282. data/spec/vcr_cassettes/invoices/save_with_specially_named_attribute.yml +0 -65
  283. data/spec/vcr_cassettes/invoices/search_by_name.yml +0 -63
  284. data/spec/vcr_cassettes/invoices/search_miss.yml +0 -62
  285. data/spec/vcr_cassettes/invoices/search_with_special_char.yml +0 -62
  286. data/spec/vcr_cassettes/invoices/single_param_find_by_hash.yml +0 -64
  287. data/spec/vcr_cassettes/orders/all.yml +0 -69
  288. data/spec/vcr_cassettes/orders/filter_hit.yml +0 -64
  289. data/spec/vcr_cassettes/orders/filter_invalid.yml +0 -60
  290. data/spec/vcr_cassettes/orders/find_by_hash_failure.yml +0 -62
  291. data/spec/vcr_cassettes/orders/find_failure.yml +0 -62
  292. data/spec/vcr_cassettes/orders/find_id_1.yml +0 -67
  293. data/spec/vcr_cassettes/orders/find_new.yml +0 -65
  294. data/spec/vcr_cassettes/orders/housework_invalid_tax_reduction_type.yml +0 -61
  295. data/spec/vcr_cassettes/orders/housework_othercoses_invalid.yml +0 -61
  296. data/spec/vcr_cassettes/orders/housework_type_babysitting.yml +0 -65
  297. data/spec/vcr_cassettes/orders/housework_type_cleaning.yml +0 -65
  298. data/spec/vcr_cassettes/orders/housework_type_construction.yml +0 -65
  299. data/spec/vcr_cassettes/orders/housework_type_cooking.yml +0 -61
  300. data/spec/vcr_cassettes/orders/housework_type_electricity.yml +0 -65
  301. data/spec/vcr_cassettes/orders/housework_type_gardening.yml +0 -65
  302. data/spec/vcr_cassettes/orders/housework_type_glassmetalwork.yml +0 -65
  303. data/spec/vcr_cassettes/orders/housework_type_grounddrainagework.yml +0 -65
  304. data/spec/vcr_cassettes/orders/housework_type_hvac.yml +0 -65
  305. data/spec/vcr_cassettes/orders/housework_type_itservices.yml +0 -65
  306. data/spec/vcr_cassettes/orders/housework_type_majorappliancerepair.yml +0 -65
  307. data/spec/vcr_cassettes/orders/housework_type_masonry.yml +0 -65
  308. data/spec/vcr_cassettes/orders/housework_type_movingservices.yml +0 -65
  309. data/spec/vcr_cassettes/orders/housework_type_othercare.yml +0 -65
  310. data/spec/vcr_cassettes/orders/housework_type_othercosts.yml +0 -65
  311. data/spec/vcr_cassettes/orders/housework_type_paintingwallpapering.yml +0 -65
  312. data/spec/vcr_cassettes/orders/housework_type_snowplowing.yml +0 -65
  313. data/spec/vcr_cassettes/orders/housework_type_textileclothing.yml +0 -65
  314. data/spec/vcr_cassettes/orders/housework_type_tutoring.yml +0 -61
  315. data/spec/vcr_cassettes/orders/multi_param_find_by_hash.yml +0 -63
  316. data/spec/vcr_cassettes/orders/save_new.yml +0 -65
  317. data/spec/vcr_cassettes/orders/save_old.yml +0 -65
  318. data/spec/vcr_cassettes/orders/save_with_nested_model.yml +0 -65
  319. data/spec/vcr_cassettes/orders/search_by_name.yml +0 -63
  320. data/spec/vcr_cassettes/orders/search_miss.yml +0 -62
  321. data/spec/vcr_cassettes/orders/search_with_special_char.yml +0 -62
  322. data/spec/vcr_cassettes/orders/single_param_find_by_hash.yml +0 -64
  323. data/spec/vcr_cassettes/projects/all.yml +0 -64
  324. data/spec/vcr_cassettes/projects/find_by_hash_failure.yml +0 -62
  325. data/spec/vcr_cassettes/projects/find_failure.yml +0 -62
  326. data/spec/vcr_cassettes/projects/find_id_1.yml +0 -63
  327. data/spec/vcr_cassettes/projects/find_new.yml +0 -63
  328. data/spec/vcr_cassettes/projects/multi_param_find_by_hash.yml +0 -64
  329. data/spec/vcr_cassettes/projects/save_new.yml +0 -63
  330. data/spec/vcr_cassettes/projects/save_old.yml +0 -63
  331. data/spec/vcr_cassettes/projects/single_param_find_by_hash.yml +0 -63
  332. data/spec/vcr_cassettes/termsofpayments/all.yml +0 -68
  333. data/spec/vcr_cassettes/termsofpayments/find_failure.yml +0 -62
  334. data/spec/vcr_cassettes/termsofpayments/find_id_1.yml +0 -62
  335. data/spec/vcr_cassettes/termsofpayments/find_new.yml +0 -63
  336. data/spec/vcr_cassettes/termsofpayments/save_new.yml +0 -63
  337. data/spec/vcr_cassettes/termsofpayments/save_old.yml +0 -63
  338. data/spec/vcr_cassettes/units/all.yml +0 -64
  339. data/spec/vcr_cassettes/units/find_failure.yml +0 -62
  340. data/spec/vcr_cassettes/units/find_id_1.yml +0 -63
  341. data/spec/vcr_cassettes/units/find_new.yml +0 -63
  342. data/spec/vcr_cassettes/units/save_new.yml +0 -63
  343. data/spec/vcr_cassettes/units/save_old.yml +0 -63
  344. data/spec/vcr_cassettes/units/save_with_specially_named_attribute.yml +0 -63
data/bin/fortnox DELETED
@@ -1,285 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- lib = File.expand_path('../lib', __dir__)
5
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
-
7
- require 'bundler/setup'
8
- require 'dry/cli'
9
- require 'securerandom'
10
- require 'base64'
11
- require 'socket'
12
- require 'cgi'
13
- require 'uri'
14
- require 'faraday'
15
-
16
- # TODO: Not implemented yet
17
- # require "fortnox/version"
18
-
19
- module Fortnox
20
- module CLI
21
- module Server
22
- def self.start(port)
23
- socket = TCPServer.new(port)
24
- client = socket.accept
25
- request = client.gets
26
- _, path, = request.split
27
- client.puts("HTTP/1.1 200\r\n\r\n#{response_html}")
28
- client.close
29
- socket.close
30
-
31
- URI.decode_www_form(path[2..]).to_h.transform_keys(&:to_sym)
32
- end
33
-
34
- def self.response_html
35
- assets_root = 'https://accodeing.com/assets/images'
36
-
37
- favicon = "#{assets_root}/favicon-32x32.png"
38
- bkg = "#{assets_root}/background.svg"
39
- logo = "#{assets_root}/only_logo.svg"
40
-
41
- <<~HTML
42
- <html>
43
- <head>
44
- <title>Fortnox gem local server</title>
45
- <link rel="icon" type="image/png" sizes="32x32" href="#{favicon}" />
46
- <style>
47
- main{display:block;width:800px;margin:2rem auto 0}html{color:#222;font-family:sans-serif}body{margin:0;background-color:#31926f;color:#fff;font-family:'Open Sans',sans-serif;background-image:url(#{bkg});background-repeat:no-repeat;background-size:cover;font-size:1em;line-height:1.4}h1{font-family:Quicksand,sans-serif;font-size:2em;margin:.67em 0;color:#f2dfc3}img{width:100%}
48
- </style>
49
- </head>
50
- <body>
51
- <main>
52
- <img src="#{logo}" alt="According to you's logo, a happy otter.">
53
- <h1>The response from Fortnox has been caught.</h1>
54
- <p>You can safely close this tab now and continue in the terminal.</p>
55
- </main>
56
- </body>
57
- </html>
58
- HTML
59
- end
60
- end
61
-
62
- module Commands
63
- extend Dry::CLI::Registry
64
-
65
- class Version < Dry::CLI::Command
66
- desc 'Print version'
67
-
68
- def call(*)
69
- puts Fortnox::VERSION
70
- end
71
- end
72
-
73
- class Init < Dry::CLI::Command # rubocop:disable Metrics/ClassLength
74
- desc "Create initial authentication and refresh tokens using Fortnox's OAuth screen. " \
75
- 'If started without arguments it will run through a wizzard to get you set up. ' \
76
- 'If all the arguments are given it will run the process automatically, without prompting.'
77
- option :port, default: '4242', type: :integer,
78
- desc: "Port used by a local server to catch Fortnox's auth response"
79
- argument :client_id, type: :string, desc: 'Client ID'
80
- argument :client_secret, type: :string, desc: 'Client secret'
81
- argument :scopes, type: :array, desc: 'Array of scopes'
82
-
83
- def call(port: 4242, client_id: nil, client_secret: nil, scopes: [], **) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
84
- fast_track = !(client_id.nil? || client_secret.nil? || scopes.empty?)
85
-
86
- confirm_ready unless fast_track
87
-
88
- if fast_track
89
- scopes = scopes.join(' ')
90
- else
91
- client_id = get_client_id
92
- client_secret = get_client_secret
93
- scopes = get_scopes
94
- end
95
-
96
- credentials = Base64.encode64("#{client_id}:#{client_secret}")
97
- redirect_uri = "http://localhost:#{port}"
98
- nonce = SecureRandom.base64
99
- params = {
100
- client_id:,
101
- redirect_uri:,
102
- scope: scopes,
103
- state: nonce,
104
- access_type: 'offline',
105
- response_type: 'code',
106
- account_type: 'service'
107
- }
108
- url = "https://apps.fortnox.se/oauth-v1/auth?#{URI.encode_www_form(params)}"
109
-
110
- confirm_redirect_uri(redirect_uri) unless fast_track
111
- launch_fortnox_authorisation(url)
112
- auth_code = get_auth_code(port, nonce)
113
- tokens = exchange_auth_code_for_tokens(auth_code, credentials, redirect_uri)
114
- print_tokens(tokens)
115
- end
116
-
117
- private
118
-
119
- def confirm_ready
120
- puts "Before you can complete this setup you need to complete all the steps on Fortnox's side " \
121
- "as documented in #{gem_homepage}/docs/getting_set_up.md"
122
- print 'Do you have client ID, client secret and a list of scopes handy? [Y/n] '
123
-
124
- confirmation = $stdin.gets.chomp.downcase
125
-
126
- if confirmation == 'n'
127
- puts 'Ok. Go read the guide, get setup as a developer in Fortnox and come back here when you are ready.'
128
- exit 0
129
- end
130
-
131
- puts "Excellent, let's go! Input the information from Fortnox in the following prompts."
132
- end
133
-
134
- def get_client_id # rubocop:disable Naming/AccessorMethodName
135
- print 'Client ID: '
136
- $stdin.gets.chomp
137
- end
138
-
139
- def get_client_secret # rubocop:disable Naming/AccessorMethodName
140
- print 'Client secret: '
141
- $stdin.gets.chomp
142
- end
143
-
144
- def get_scopes # rubocop:disable Naming/AccessorMethodName
145
- print 'Give a space separated list of all the scopes you will need. ' \
146
- "See #{gem_homepage}/docs/scopes.md for reference.\n" \
147
- 'Scopes: '
148
- $stdin.gets.chomp
149
- end
150
-
151
- def confirm_redirect_uri(url)
152
- print "Set the redirect URL in your Fortnox application to #{url}, then press enter to continue."
153
- $stdin.gets
154
- end
155
-
156
- def get_auth_code(port, nonce) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
157
- response = Server.start(port)
158
-
159
- if response[:error]
160
- puts "An error occured. Fortnox returned \"#{response[:error]}\":\n #{response[:description]}"
161
- exit(-1)
162
- end
163
-
164
- normalized_state_response = CGI.unescape(response[:state]).gsub(' ', '+')
165
-
166
- if normalized_state_response != nonce
167
- puts 'The nonce returned from Fortnox did not match the one we sent, possible replay attack!'
168
- puts "Raw sent: #{nonce.inspect}"
169
- puts "Escaped sent: #{URI.encode_www_form({ nonce: }).inspect}"
170
- puts "Raw returned: #{response[:state].inspect}"
171
- puts "CGI escaped returned: #{CGI.unescape(response[:state]).inspect}"
172
- puts "URI escaped returned: #{URI.decode_www_form(response[:state]).first.first.inspect}"
173
- exit(-1)
174
- end
175
-
176
- response[:code]
177
- rescue SocketError
178
- puts "The local server failed to start so we can't catch the auth code automatically. " \
179
- 'If you look in the addressbar of the missing page Fortnox redirected you to after you completed ' \
180
- 'the authorisation you will see a request parameter called "code", paste the value below.'
181
- print 'Auth code:'
182
- $stdin.gets.chomp
183
- end
184
-
185
- def launch_fortnox_authorisation(url)
186
- if (cmd = system_open)
187
- system "#{cmd} \"#{url}\""
188
- else
189
- puts 'Could not identify a way to open default browser. ' \
190
- 'Please open the following url in your prefered browser:'
191
- puts url
192
- end
193
- end
194
-
195
- def system_open
196
- case RbConfig::CONFIG['host_os']
197
- when /mswin|mingw|cygwin/
198
- 'start'
199
- when /darwin/
200
- 'open'
201
- when /linux|bsd/
202
- 'xdg-open'
203
- end
204
- end
205
-
206
- def exchange_auth_code_for_tokens(auth_code, credentials, redirect_uri)
207
- headers = {
208
- 'Content-type' => 'application/x-www-form-urlencoded',
209
- 'Authorization' => "Basic #{credentials}"
210
- }
211
- body = "grant_type=authorization_code&code=#{auth_code}&redirect_uri=#{redirect_uri}"
212
-
213
- response = Faraday.post('https://apps.fortnox.se/oauth-v1/token', body, headers)
214
-
215
- JSON.parse(response.body).transform_keys(&:to_sym)
216
- end
217
-
218
- def print_tokens(tokens) # rubocop:disable Metrics/MethodLength
219
- puts 'Save these tokens in an appropriate place:'
220
- puts ''
221
- puts "refresh token: #{tokens[:refresh_token]}"
222
- puts ''
223
- puts "access token: #{tokens[:access_token]}"
224
- puts ''
225
- puts 'You can run this command with all the inputs as parameters if you want to reauthorize ' \
226
- 'without the guide in the future.'
227
- puts ''
228
- puts 'There is also a `fortnox refresh` command to run the refresh cycle manually ' \
229
- 'for use in cron jobs or other automated token refresh scenarios. ' \
230
- 'See `fortnox help` for more information.'
231
- puts ''
232
- end
233
-
234
- def gem_homepage
235
- @gem_homepage ||= gem_homepage_from_gemspec
236
- end
237
-
238
- def gem_homepage_from_gemspec
239
- gem_root = File.expand_path('..', __dir__)
240
- file = Dir.entries(gem_root).find do |f|
241
- puts f
242
- f.end_with? '.gemspec'
243
- end
244
- our_gemspec = Gem::Specification.load("#{gem_root}/#{file}")
245
- our_gemspec.homepage
246
- end
247
- end
248
-
249
- class Refresh < Dry::CLI::Command
250
- desc 'Get a new set of tokens from Fortnox given a valid refresh token. ' \
251
- 'If you do not already have a set of tokens you want `fortnox init` instead.'
252
- argument :client_id, required: true, type: :string, desc: 'Client ID'
253
- argument :client_secret, required: true, type: :string, desc: 'Client secret'
254
- argument :refresh_token, required: true, type: :string, desc: 'Valid refresh token'
255
-
256
- def call(client_id:, client_secret:, refresh_token:, **)
257
- credentials = Base64.encode64("#{client_id}:#{client_secret}")
258
- headers = {
259
- 'Content-type' => 'application/x-www-form-urlencoded',
260
- 'Authorization' => "Basic #{credentials}"
261
- }
262
- body = "grant_type=refresh_token&refresh_token=#{refresh_token}"
263
-
264
- response = Faraday.post('https://apps.fortnox.se/oauth-v1/token', body, headers)
265
-
266
- print_tokens(JSON.parse(response.body).transform_keys(&:to_sym))
267
- end
268
-
269
- def print_tokens(tokens)
270
- puts ''
271
- puts "refresh token: #{tokens[:refresh_token]}"
272
- puts ''
273
- puts "access token: #{tokens[:access_token]}"
274
- puts ''
275
- end
276
- end
277
-
278
- register 'version', Version, aliases: ['v', '-v', '--version']
279
- register 'init', Init, aliases: ['i', '-i', '--init']
280
- register 'refresh', Refresh, aliases: ['r', '-r', '--refresh']
281
- end
282
- end
283
- end
284
-
285
- Dry::CLI.new(Fortnox::CLI::Commands).call
data/bin/get_tokens DELETED
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'base64'
5
- require 'dotenv'
6
- require 'httparty'
7
-
8
- DOTENV_FILE_NAME = '.env'
9
- OAUTH_ENDPOINT = 'https://apps.fortnox.se/oauth-v1'
10
-
11
- Dotenv.load(DOTENV_FILE_NAME)
12
-
13
- ERROR_MESSAGE = 'Missing environment variabel %s! Exiting.'
14
- CLIENT_ID = ENV.fetch('FORTNOX_API_CLIENT_ID') { |key| raise(format(ERROR_MESSAGE, key)) }
15
- CLIENT_SECRET = ENV.fetch('FORTNOX_API_CLIENT_SECRET') { |key| raise(format(ERROR_MESSAGE, key)) }
16
- REDIRECT_URI = ENV.fetch('FORTNOX_API_REDIRECT_URI') { |key| raise(format(ERROR_MESSAGE, key)) }
17
- SCOPES = ENV.fetch('FORTNOX_API_SCOPES') { |key| raise(format(ERROR_MESSAGE, key)) }
18
- CREDENTIALS = Base64.encode64("#{CLIENT_ID}:#{CLIENT_SECRET}").to_s
19
-
20
- AUTH_PARAMS =
21
- "client_id=#{CGI.escape(CLIENT_ID)}" \
22
- "&redirect_uri=#{CGI.escape(REDIRECT_URI)}" \
23
- "&scope=#{CGI.escape(SCOPES)}" \
24
- '&state=not-used' \
25
- '&access_type=offline' \
26
- '&response_type=code' \
27
- '&account_type=service'
28
-
29
- AUTHORIZE_URL = "#{OAUTH_ENDPOINT}/auth?#{AUTH_PARAMS}"
30
-
31
- print "Let's get you an access token and a refresh token for Fortnox API."
32
- print "\nVisit the URL below, login with the account you want to authorize and grant the integration permission." \
33
- 'Then copy the "code" from the URL parameters.'
34
- print "\n#{AUTHORIZE_URL}"
35
-
36
- print "\nEnter authorization code: "
37
- authorization_code = $stdin.gets.chomp
38
-
39
- headers = {
40
- Authorization: "Basic #{CREDENTIALS}",
41
- 'Content-type' => 'application/x-www-form-urlencoded'
42
- }
43
-
44
- body = {
45
- grant_type: 'authorization_code',
46
- code: authorization_code,
47
- redirect_uri: REDIRECT_URI
48
- }
49
-
50
- response = HTTParty.post("#{OAUTH_ENDPOINT}/token", headers: headers, body: body)
51
-
52
- if response.code != 200
53
- print "\nSomething went wrong."
54
- print "\nResponse code #{response.code}"
55
- print "\nResponse message: #{response.message}"
56
- print "\nResponse body: #{response.body}"
57
- print "\n"
58
- exit
59
- end
60
-
61
- access_token = response.parsed_response['access_token']
62
- refresh_token = response.parsed_response['refresh_token']
63
-
64
- print "There you go, here's your access token: #{access_token}"
65
- print "\nAnd here's your refresh token: #{refresh_token}"
66
- print "\nYour access token expires in #{response.parsed_response['expires_in']} seconds"
67
- print "\nRequested scopes: #{response.parsed_response['scope']}"
68
- print "\n"
69
-
70
- print "Write tokens to #{DOTENV_FILE_NAME} [y/n]? "
71
- store_tokens_in_env = $stdin.gets.chomp
72
-
73
- exit unless store_tokens_in_env.casecmp('y').zero?
74
-
75
- text = File.read(DOTENV_FILE_NAME)
76
- updated_text = text
77
- .gsub(/FORTNOX_API_ACCESS_TOKEN=.*$/, "FORTNOX_API_ACCESS_TOKEN=#{access_token}")
78
- .gsub(/FORTNOX_API_REFRESH_TOKEN=.*$/, "FORTNOX_API_REFRESH_TOKEN=#{refresh_token}")
79
- File.write(DOTENV_FILE_NAME, updated_text)
data/bin/renew_tokens DELETED
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'fortnox/api'
6
- require 'dotenv'
7
-
8
- DOTENV_FILE_NAME = '.env'
9
- Dotenv.load(DOTENV_FILE_NAME)
10
-
11
- tokens = Fortnox::API::Repository::Authentication.new.renew_tokens(
12
- refresh_token: ENV.fetch('FORTNOX_API_REFRESH_TOKEN'),
13
- client_id: ENV.fetch('FORTNOX_API_CLIENT_ID'),
14
- client_secret: ENV.fetch('FORTNOX_API_CLIENT_SECRET')
15
- )
16
-
17
- puts tokens
18
-
19
- print "Write tokens to #{DOTENV_FILE_NAME} [y/n]? "
20
- store_tokens_in_env = $stdin.gets.chomp
21
-
22
- exit unless store_tokens_in_env.casecmp('y').zero?
23
-
24
- text = File.read(DOTENV_FILE_NAME)
25
- updated_text = text
26
- .gsub(/FORTNOX_API_ACCESS_TOKEN=.*$/, "FORTNOX_API_ACCESS_TOKEN=#{tokens[:access_token]}")
27
- .gsub(/FORTNOX_API_REFRESH_TOKEN=.*$/, "FORTNOX_API_REFRESH_TOKEN=#{tokens[:refresh_token]}")
28
- File.write(DOTENV_FILE_NAME, updated_text)
data/docs/gotchas.md DELETED
@@ -1,146 +0,0 @@
1
- # Gotchas
2
- Fortnox API is not perfect. There are a couple of things to take into consideration when using it.
3
-
4
- ## Article
5
- If you create a new Article, the `SalesAccount` will default to the default sales account set in the Fortnox settings. An interesting thing though is that this account might not exist in the chart of accounts. Therefore, this strange thing can happen (requests and responses below are the raw HTTP requests/responses):
6
-
7
- 1. You create an new Article via the API: `'{"Article":{"Description":"A value","SalesAccount":1250}}'` and you get an Article back that has `SalesAccount` set: `'{"Article":{"Description":"A Value", "SalesAccount":1250, "PurchaseAccount":4000, ...}}'`.
8
- 2. You try to update the newly created Article with `'{"Article":{"Description":"Updated description"}}'` and you get this back:
9
- `'{"ErrorInformation":{"error":1,"message":"Inköpskonto inte uppdaterat. Kontot \"4000\" existerar inte. (PurchaseAccount)", ...}}'`
10
-
11
- We haven't even touched the `SalesAccount` but you still get an error. So the account that the API says that the Article has does not necessarily need to exist in "reality"...
12
-
13
- ## HouseWorkType
14
- ### Legacy types
15
- Not all HouseWorkTypes available are possible to use when creating a new `Order` or `Invoice`. For instance, creating an `OrderRow` with `HouseWorkType` set to `COOKING` returns:
16
- > Skattereduktion för en av de valda husarbetestyperna har upphört.
17
-
18
- Unfortunately, their documentation does not tell you which types are deprecated... In fact, the deprecated types are simply removed from their documentation, so if you fetch an old `Order`/`Invoice`, you have no clue what HouseWorkType can be set to! There are two constants available in the gem: `CURRENT_HOUSEWORK_TYPES` and `LEGACY_HOUSEWORK_TYPES`, but we cannot guarantee that they are up to date and that they include all possible values.
19
-
20
- ### OTHERCOSTS
21
- Another weird thing is that the option `OTHERCOSTS` is not allowed to be combined with `HouseWork` attribute. Yes, that is true...
22
-
23
- ## VAT on Invoices and Orders rows
24
-
25
- If you create an Invoice with the following data:
26
-
27
- ```
28
- $ curl -X "POST" "https://api.fortnox.se/3/invoices" \
29
- > (credentials...)
30
- > -d $'{
31
- > "Invoice": {
32
- > "CustomerNumber": "188",
33
- > "InvoiceRows": [
34
- > {
35
- > "ArticleNumber": "1",
36
- > "DeliveredQuantity": "10.00",
37
- > "VAT": "25"
38
- > }
39
- > ]
40
- > }
41
- > }'
42
- ```
43
-
44
- You get the following when you query the created Invoice:
45
-
46
- ```
47
- {"Invoice":
48
- {...,
49
- "InvoiceRows":[
50
- {
51
- ...
52
- DeliveredQuantity":"10.00",
53
- ...,
54
- "Price":100,
55
- "PriceExcludingVAT":100,
56
- ...,
57
- "Total":1000,
58
- "TotalExcludingVAT":1000,
59
- "Unit":"",
60
- "VAT":25}
61
- ]
62
- ```
63
-
64
- Notice that the `Price` and `PriceExcludingVAT` have the exact same amount. The same goes for `Total` and `TotalExcludingVAT`. Strange, right? The `VAT` is set to `25`%.
65
-
66
- It works like this: `Invoice` (and `Order`) has a `VATIncluded` attribute. If it is set to `true`, `Price` and `Total` will be VAT included and `PriceExcludingVAT` and `TotalExcludingVAT` will obviously be VAT excluded. BUT if you set `VATIncluded` to false, all those attributes will be VAT exclusive and none inclusive. If you want to know the VAT for each row, then you have to calculate it yourself! I have suggested to Fortnox to fix this weird logic by adding a `TotalVAT` and `PriceWithVAT` that always holds the `VAT` amount. Then `Price` and `Total` can be VAT included/excluded depending on the value of `VATIncluded`. Fortnox said they will add this to their to do list...
67
-
68
- ## Dependent attributes
69
- Some attributes depends on other attributes.
70
-
71
- ### `Invoice.InvoiceRows.Discount`
72
- `Discount` have two different limits depending on `DiscountType`. Calling Fortnox with
73
- ```
74
- {
75
- "Invoice":{
76
- "CustomerNumber":"1",
77
- "InvoiceRows":[
78
- {"ArticleNumber":"0000","Discount":12.0001,"DiscountType":"PERCENT","Price":20.0}
79
- ]
80
- }
81
- }
82
- ```
83
- gives an `InvoiceRow` with `"Discount":12,"DiscountType":"PERCENT"`
84
-
85
- Calling Fortnox with
86
- ```
87
- {
88
- "Invoice":{
89
- "CustomerNumber":"1",
90
- "InvoiceRows":[
91
- {"ArticleNumber":"0000","Discount":123456.0,"DiscountType":"PERCENT","Price":20.0}
92
- ]
93
- }
94
- }
95
- ```
96
- gives
97
- ```
98
- Fortnox::API::RemoteServerError:
99
- Ogiltig rabatt. Får inte överstiga 100 %
100
- ```
101
-
102
- ## Types
103
- ### Type of attribute DocumentNumber
104
- `Customer` attribute `DocumentNumber` can be both an `Integer` and a `String` according to Fortnox... See [#78](https://github.com/my-codeworks/fortnox-api/issues/78).
105
-
106
- ### Default values for Date attributes
107
- The default values for date attributes from Fortnox API can be either an empty string or null. Fortnox is working on the issue. See [#79](https://github.com/my-codeworks/fortnox-api/issues/79).
108
-
109
- ## Strange error messages
110
- ### InternalError when creating Order without OrderRows
111
- Creating an `Order` without `OrderRows` returns `InternalError`. Fortnox is working on this issue. See [#84](https://github.com/my-codeworks/fortnox-api/issues/84).
112
-
113
- ### Kunde inte hitta konto.
114
- If you create an `Order` or `Invoice` with an `OrderRow`/`InvoiceRow` without both `ArticleNumber` and `AccountNumber` Fortnox gives you this splendid message:
115
- > Kunde inte hitta konto.
116
-
117
- I think you can set a default account number for rows.
118
-
119
- ## Fortnox rewrites attributes
120
- ### TermsOfPayments
121
- This model has a `code` attribute. `30days` is a valid attribute value, but the API rewrites this as `30DAYS` and a `GET` request with `30days` will return `Not Found`. This is not documented anywhere...
122
-
123
- ## Not documented limitations
124
- ### TermsOfPayments
125
- The `code` attribute has no limits according to the documentations, but when sending `30 days` to the API, you get an error saying that the value must be alphanumeric.
126
-
127
- ### Row description
128
- The row description for `Invoice` (and I guess for `Offer` and `Order` as well) has a limit of 255 characters.
129
-
130
- ## Consistency
131
- ### Customer's SalesAccount attribute
132
- If you create a `Customer` with the following JSON payload, with `SalesAccount` as a **string** just like the documentation says it should be
133
- ```
134
- {"Customer":{"Name":"Customer with Sales Account","SalesAccount":"3001"}}
135
- ```
136
- Fortnox returns `SalesAccount` as an **integer**...
137
- ```
138
- {"Customer":{"@url":"https:\/\/api.fortnox.se\/3\/customers\/242",..., "SalesAccount":3001}}
139
- ```
140
- When you then fetch **the same Customer you just created**, you get the `SalesAccount` as a **string**
141
- ```
142
- {"Customer": "@url":"https:\/\/api.fortnox.se\/3\/customers\/242", ..., "SalesAccount":"3001"}}
143
- ```
144
-
145
- ## Authentication
146
- You can only have one active refresh token per Fortnox account and Fortnox integration. If you want multiple refresh tokens per Fortnox account you can create multiple integrations.
data/fortnox-api.gemspec DELETED
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require_relative 'lib/fortnox/api/version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = 'fortnox-api'
9
- spec.version = Fortnox::API::VERSION
10
- spec.authors = ['Jonas Schubert Erlandsson', 'Hannes Elvemyr', 'Felix Holmgren', 'Mike Eirih']
11
- spec.email = ['info@accodeing.com']
12
- spec.summary = 'Gem to use Fortnox REST API in Ruby.'
13
- spec.description = 'This gem uses the HTTParty library to abstract away the REST calls. It gives you access to a ' \
14
- 'number of objects that behave a lot like ActiveRecord instances, giving you access to ' \
15
- 'methods like `all`, `find`, `find_by_...` and so on. And each individual instance can be' \
16
- 'easily persistable to Fortnox again using the `save` method.'
17
- spec.homepage = 'http://github.com/accodeing/fortnox-api'
18
- spec.license = 'LGPL-3.0'
19
- spec.metadata = {
20
- 'rubygems_mfa_required' => 'true'
21
- }
22
-
23
- spec.files = `git ls-files -z`.split("\x0")
24
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
- spec.require_paths = ['lib']
26
-
27
- spec.required_ruby_version = '>= 2.7'
28
- spec.add_dependency 'countries', '~> 5.0'
29
- spec.add_dependency 'dry-configurable', '~> 1.0'
30
- spec.add_dependency 'dry-container', '~> 0.10'
31
- spec.add_dependency 'dry-struct', '~> 1.6'
32
- spec.add_dependency 'dry-types', '~> 1.7'
33
- spec.add_dependency 'httparty', '~> 0.21'
34
- spec.add_dependency 'jwt', '~> 2.3'
35
-
36
- spec.add_development_dependency 'bundler', '~> 2.4'
37
- spec.add_development_dependency 'dotenv', '~> 2.8'
38
- spec.add_development_dependency 'guard', '~> 2.18'
39
- spec.add_development_dependency 'guard-rspec', '~> 4.7'
40
- spec.add_development_dependency 'guard-rubocop', '~> 1.5'
41
- spec.add_development_dependency 'pry', '~> 0'
42
- spec.add_development_dependency 'rake', '~> 13.0'
43
- spec.add_development_dependency 'rspec', '~> 3.12'
44
- spec.add_development_dependency 'rspec-collection_matchers', '~> 0'
45
- spec.add_development_dependency 'rubocop', '~> 1.39.0'
46
- spec.add_development_dependency 'rubocop-rspec', '~> 2.16.0'
47
- spec.add_development_dependency 'simplecov', '~> 0.22'
48
- spec.add_development_dependency 'vcr', '~> 6.1'
49
- spec.add_development_dependency 'webmock', '~> 3.18'
50
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base'
4
-
5
- module Fortnox
6
- module API
7
- module Mapper
8
- class Article < Fortnox::API::Mapper::Base
9
- KEY_MAP = {
10
- ean: 'EAN',
11
- eu_account: 'EUAccount',
12
- eu_vat_account: 'EUVATAccount',
13
- housework_type: 'HouseWorkType',
14
- vat: 'VAT'
15
- }.freeze
16
- JSON_ENTITY_WRAPPER = 'Article'
17
- JSON_COLLECTION_WRAPPER = 'Articles'
18
- end
19
-
20
- Registry.register(Article.canonical_name_sym, Article)
21
- end
22
- end
23
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fortnox
4
- module API
5
- module Mapper
6
- module CanonicalNameSym
7
- def canonical_name_sym(*values)
8
- klass = if values.empty?
9
- self
10
- elsif values.first.is_a? Class
11
- values.first
12
- else
13
- values.first.class
14
- end
15
-
16
- klass.name.split('::').last.downcase.to_sym
17
- end
18
- end
19
- end
20
- end
21
- end