fortnox-api 1.0.0.rc2 → 1.0.0.rc3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 355563e337538c18df2917f19aee3e0d91a1f7763fbb3a01bad441bd3956f496
4
- data.tar.gz: 4898bf90e631edd152c4c44b1fda68c2284cc2f24e38cd712a8a0057e27a6c40
3
+ metadata.gz: 2297de9b35ce0c8d7f3f0e3b84f0a44a443062d257f843876657a6d26a4da160
4
+ data.tar.gz: b328a297acaa35278c2b943b10bc2dc04b7e8dfbf2dd65642346f35c572bb950
5
5
  SHA512:
6
- metadata.gz: 5acaea5d925b3c8f60f817ff39d167f0e078e5ec32f7f86568cf853935ac4955431dd037c81b561ec2be081e09d46b1dbae9b3c3d204bbfbcfd2615bc4d19d50
7
- data.tar.gz: 14ddc8dbdc2da42f8c09d6bb5ee11c47ceb6fa8faba6cd51da7c97d1fc4bbf4c3adcba67e2bbc831c2783a2841d4f292b3f358d608e31d84cae8e25222fa711b
6
+ metadata.gz: a73b124f68428feaaccbfea406a025bb24995b7dce7e88959dee7aca21ab449e4c92cbae11808b33c50cf8f8e498d6039399157cdcc5e68c3bcb6cfce5a35b30
7
+ data.tar.gz: aa5b472af7edc87d4f808581ae99545e1b99f33d7319890843d442d9f2c7d1afe7db6fb04d509a5524d5b7f405337e0b6043d8155be1e5b3ebf2b8e83d3e75e9
data/CHANGELOG.md CHANGED
@@ -6,6 +6,29 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to
7
7
  [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [1.0.0.rc3] - 2026-05-08
10
+
11
+ ### Changed
12
+
13
+ - `Fortnox::RequestError#message` now includes the API's
14
+ `ErrorInformation.Message` and code from the response body when present,
15
+ normalising Fortnox's inconsistent key casing (PascalCase vs lowercase)
16
+ across endpoints. Previously the message was only `"Request failed: <status>"`,
17
+ hiding the cause from logs and uncaught backtraces.
18
+ - `Article#commodity_code`, `Customer#phone`, `Invoice#accounting_method`,
19
+ `Invoice#invoice_period_reference`, `Invoice#invoice_reference`,
20
+ `Document#time_basis_reference`, `Document#total_to_pay`, and
21
+ `Document#warehouse_ready` are now flagged read-only to match the Fortnox
22
+ API. Values set on these attributes are silently excluded from save
23
+ requests; previously they were sent and rejected by the API.
24
+
25
+ ### Fixed
26
+
27
+ - `Order` and `Invoice` rows now serialise the VAT field as `VAT` instead of
28
+ `Vat`. Saving rows with a `vat` value previously failed with Fortnox
29
+ rejecting the request as `"Felaktigt fältnamn"`. This bug was introduced in the 1.0.0.rc1,
30
+ it did not exist in 0.x.
31
+
9
32
  ## [1.0.0.rc2] - 2026-05-05
10
33
 
11
34
  ### Added
@@ -22,16 +45,22 @@ and this project adheres to
22
45
  prescriptive default that didn't reflect the actual resource set. Other
23
46
  Fortnox scopes (`salary`, `bookkeeping`, etc.) can still be entered
24
47
  manually.
25
- - `TermsOfPayment.code` is now `Sized::String[25]` and required, matching
26
- Fortnox API documentation. The rest-easy rewrite for rc1 briefly lost
27
- the required flag.
28
- - `Unit.code` is now `Sized::String[20]` and required, matching Fortnox
29
- API documentation. The rest-easy rewrite for rc1 briefly lost the
30
- required flag.
48
+ - `TermsOfPayment.code` now has a 25-character limit, matching Fortnox
49
+ API documentation.
50
+ - `Unit.code` now has a 20-character limit, matching Fortnox API
51
+ documentation.
31
52
  - `Unit.description` is now `Sized::String[100]` and required, matching
32
53
  Fortnox API documentation. In 0.x and rc1 this was nullable client-side,
33
54
  but the Fortnox API rejected unset descriptions anyway.
34
55
 
56
+ ### Fixed
57
+
58
+ - `TermsOfPayment.code` is required again, matching Fortnox API
59
+ documentation. The rest-easy rewrite for rc1 briefly lost the required
60
+ flag.
61
+ - `Unit.code` is required again, matching Fortnox API documentation. The
62
+ rest-easy rewrite for rc1 briefly lost the required flag.
63
+
35
64
  ## [1.0.0.rc1] - 2026-05-04
36
65
 
37
66
  Version 1.0 is a complete rewrite of the gem and is **not** a drop-in
@@ -139,5 +168,6 @@ for the full list of breaking changes.
139
168
  For changes prior to the 1.0 rewrite, see the
140
169
  [0.x changelog](https://github.com/accodeing/fortnox-api/blob/v0.9.2/CHANGELOG.md).
141
170
 
171
+ [1.0.0.rc3]: https://github.com/accodeing/fortnox-api/compare/v1.0.0.rc2...v1.0.0.rc3
142
172
  [1.0.0.rc2]: https://github.com/accodeing/fortnox-api/compare/v1.0.0.rc1...v1.0.0.rc2
143
173
  [1.0.0.rc1]: https://github.com/accodeing/fortnox-api/releases/tag/v1.0.0.rc1
data/README.md CHANGED
@@ -16,7 +16,7 @@ Adding more resources is quick and easy, see the
16
16
  ## Status
17
17
 
18
18
  Version 1.0 is a complete rewrite, currently in release candidate
19
- (`1.0.0.rc2`). It is built on
19
+ (`1.0.0.rc3`). It is built on
20
20
  [rest-easy](https://github.com/accodeing/rest-easy), replacing the old
21
21
  HTTParty + Data Mapper architecture with a single resource class per entity.
22
22
  Authorization uses the Fortnox client credentials flow.
@@ -68,8 +68,10 @@ The gem raises the following exceptions:
68
68
 
69
69
  - `Fortnox::Error` — base class for everything below. Rescue this to catch
70
70
  any error raised by the gem.
71
- - `Fortnox::RequestError` — 4xx/5xx responses from the Fortnox API. Carries
72
- the response object as `.response`.
71
+ - `Fortnox::RequestError` — 4xx/5xx responses from the Fortnox API. The
72
+ exception message includes the API's `ErrorInformation.Message` and code
73
+ when present (Fortnox is inconsistent about the key casing — the gem
74
+ normalises both). The full response is on `.response`.
73
75
  - `Fortnox::AttributeError` — base for attribute validation failures.
74
76
  - `Fortnox::ConstraintError` — an attribute value violates a type
75
77
  constraint (max size, format, etc.). Carries `.attribute_name` and
@@ -6,7 +6,8 @@ module Fortnox
6
6
  struct Structs::DocumentRow
7
7
  overrides housework: 'HouseWork',
8
8
  housework_hours_to_report: 'HouseWorkHoursToReport',
9
- housework_type: 'HouseWorkType'
9
+ housework_type: 'HouseWorkType',
10
+ vat: 'VAT'
10
11
  end
11
12
  end
12
13
  end
@@ -155,6 +155,6 @@ module Fortnox
155
155
  attr :default_stock_location, Coercible::String.optional
156
156
 
157
157
  # CommodityCode Commodity code of the article.
158
- attr :commodity_code, Coercible::String.optional
158
+ attr :commodity_code, Coercible::String.optional, :read_only
159
159
  end
160
160
  end
@@ -131,6 +131,9 @@ module Fortnox
131
131
  # Our reference
132
132
  attr :our_reference, Sized::String[50]
133
133
 
134
+ # Phone number of the customer. Only present in collection responses.
135
+ attr :phone, Coercible::String.optional, :read_only
136
+
134
137
  # First phone number of the customer
135
138
  attr :phone1, Sized::String[1024]
136
139
 
@@ -191,9 +194,6 @@ module Fortnox
191
194
  # Active If the customer is active.
192
195
  attr :active, Bool.optional, Boolean
193
196
 
194
- # Phone number of the customer. Only present in collection responses.
195
- attr :phone, Coercible::String.optional
196
-
197
197
  # External reference
198
198
  attr :external_reference, Sized::String[1024]
199
199
 
@@ -191,12 +191,12 @@ module Fortnox
191
191
  attr :outbound_date, Date.optional, Mappers::Date
192
192
 
193
193
  # TimeBasisReference Reference to time basis.
194
- attr :time_basis_reference, Coercible::Integer.optional
194
+ attr :time_basis_reference, Coercible::Integer.optional, :read_only
195
195
 
196
196
  # TotalToPay Total amount to pay.
197
- attr :total_to_pay, Coercible::Float.optional
197
+ attr :total_to_pay, Coercible::Float.optional, :read_only
198
198
 
199
199
  # WarehouseReady If the document is warehouse ready.
200
- attr :warehouse_ready, Bool.optional, Boolean
200
+ attr :warehouse_ready, Bool.optional, :read_only, Boolean
201
201
  end
202
202
  end
@@ -12,7 +12,7 @@ module Fortnox
12
12
  end
13
13
 
14
14
  # AccountingMethod Accounting Method.
15
- attr :accounting_method, AccountingMethods
15
+ attr :accounting_method, AccountingMethods, :read_only
16
16
 
17
17
  # Balance Balance of the invoice.
18
18
  attr :balance, Coercible::Float.optional, :read_only
@@ -51,10 +51,10 @@ module Fortnox
51
51
  attr :invoice_period_end, Date.optional, :read_only, Mappers::Date
52
52
 
53
53
  # InvoicePeriodReference Reference to the invoice period.
54
- attr :invoice_period_reference, Coercible::String.optional
54
+ attr :invoice_period_reference, Coercible::String.optional, :read_only
55
55
 
56
56
  # InvoiceReference Reference to another invoice.
57
- attr :invoice_reference, Coercible::String.optional
57
+ attr :invoice_reference, Coercible::String.optional, :read_only
58
58
 
59
59
  # InvoiceRows Separate object
60
60
  attr :invoice_rows, Strict::Array.of(Structs::InvoiceRow), Mappers::StructArray.for(Mappers::InvoiceRow)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fortnox
4
- VERSION = '1.0.0.rc2'
4
+ VERSION = '1.0.0.rc3'
5
5
  end
data/lib/fortnox.rb CHANGED
@@ -23,11 +23,49 @@ module Fortnox
23
23
  def initialize(arg = nil)
24
24
  if arg.respond_to?(:status)
25
25
  @response = arg
26
- super("Request failed: #{arg.status}")
26
+ super("Request failed: #{arg.status}#{format_body(arg.body)}")
27
27
  else
28
28
  super
29
29
  end
30
30
  end
31
+
32
+ BODY_FALLBACK_LIMIT = 500
33
+ private_constant :BODY_FALLBACK_LIMIT
34
+
35
+ private
36
+
37
+ def format_body(body)
38
+ return '' if body.nil? || body.empty?
39
+
40
+ " - #{error_details(body) || truncate(body.to_s)}"
41
+ end
42
+
43
+ # Fortnox have at least in the past sometimes returned HTML responses on error,
44
+ # for instance 503 Service Temporarily Unavailable.
45
+ # In that case, the body might be long and useful for debugging,
46
+ # so let's truncate it to a reasonable length if we end up here.
47
+ def truncate(string)
48
+ string.length > BODY_FALLBACK_LIMIT ? "#{string[0, BODY_FALLBACK_LIMIT]}…" : string
49
+ end
50
+
51
+ def error_details(body)
52
+ info = error_information(body)
53
+ message = info && info['message']
54
+ return nil unless message
55
+
56
+ code = info['code']
57
+ code ? "#{message} (#{code})" : message
58
+ end
59
+
60
+ def error_information(body)
61
+ parsed = body.is_a?(String) ? JSON.parse(body) : body
62
+ info = parsed['ErrorInformation'] if parsed.is_a?(Hash)
63
+ # Fortnox responds with inconsistently-cased error keys (see tests),
64
+ # so let's normalise to lowercase before reading.
65
+ info.is_a?(Hash) ? info.transform_keys(&:downcase) : nil
66
+ rescue JSON::ParserError
67
+ nil
68
+ end
31
69
  end
32
70
 
33
71
  class AttributeError < Error; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fortnox-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc2
4
+ version: 1.0.0.rc3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Schubert Erlandsson
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2026-05-05 00:00:00.000000000 Z
14
+ date: 2026-05-08 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: countries