dear_inventory 0.7.5 → 1.0.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +15 -0
  3. data/README.md +70 -18
  4. data/dear_inventory.gemspec +1 -1
  5. data/docs/resources/advanced_purchase.md +25 -0
  6. data/docs/resources/customer.md +31 -0
  7. data/docs/resources/product.md +37 -0
  8. data/docs/resources/purchase.md +89 -0
  9. data/docs/resources/sale.md +89 -0
  10. data/lib/dear_inventory.rb +20 -8
  11. data/lib/dear_inventory/config.rb +17 -6
  12. data/lib/dear_inventory/errors/api_limit.rb +6 -0
  13. data/lib/dear_inventory/errors/no_more_pages.rb +6 -0
  14. data/lib/dear_inventory/lib/endpoint_class.rb +1 -1
  15. data/lib/dear_inventory/lib/request.rb +45 -8
  16. data/lib/dear_inventory/lib/strings/urlize.rb +18 -0
  17. data/lib/dear_inventory/model.rb +22 -9
  18. data/lib/dear_inventory/models/advanced_purchase.rb +176 -0
  19. data/lib/dear_inventory/models/customer.rb +1 -1
  20. data/lib/dear_inventory/models/customers.rb +1 -1
  21. data/lib/dear_inventory/models/products.rb +1 -1
  22. data/lib/dear_inventory/models/{purchase_list.rb → purchases.rb} +12 -2
  23. data/lib/dear_inventory/models/purchases/additional_charge.rb +1 -1
  24. data/lib/dear_inventory/models/purchases/advanced/credit_note.rb +21 -0
  25. data/lib/dear_inventory/models/purchases/advanced/invoice.rb +68 -0
  26. data/lib/dear_inventory/models/purchases/advanced/manual_journal.rb +21 -0
  27. data/lib/dear_inventory/models/purchases/advanced/put_away.rb +34 -0
  28. data/lib/dear_inventory/models/purchases/advanced/put_away_line.rb +21 -0
  29. data/lib/dear_inventory/models/purchases/advanced/stock.rb +30 -0
  30. data/lib/dear_inventory/models/purchases/advanced/stock_line.rb +61 -0
  31. data/lib/dear_inventory/models/purchases/credit_note.rb +1 -1
  32. data/lib/dear_inventory/models/purchases/inventory_movement.rb +1 -1
  33. data/lib/dear_inventory/models/purchases/invoice.rb +1 -1
  34. data/lib/dear_inventory/models/purchases/invoice_additional_charge.rb +1 -1
  35. data/lib/dear_inventory/models/purchases/invoice_line.rb +1 -1
  36. data/lib/dear_inventory/models/purchases/line.rb +1 -1
  37. data/lib/dear_inventory/models/purchases/manual_journal.rb +1 -1
  38. data/lib/dear_inventory/models/purchases/manual_journal_line.rb +1 -1
  39. data/lib/dear_inventory/models/purchases/order.rb +1 -1
  40. data/lib/dear_inventory/models/purchases/payment_line.rb +51 -0
  41. data/lib/dear_inventory/models/purchases/stock.rb +1 -1
  42. data/lib/dear_inventory/models/purchases/stock_line.rb +1 -1
  43. data/lib/dear_inventory/models/purchases/unstock_line.rb +1 -1
  44. data/lib/dear_inventory/models/{purchase_lists.rb → purchases_results.rb} +4 -4
  45. data/lib/dear_inventory/models/{sale_list.rb → sales.rb} +7 -2
  46. data/lib/dear_inventory/models/sales/additional_charge.rb +1 -1
  47. data/lib/dear_inventory/models/sales/credit_note.rb +1 -1
  48. data/lib/dear_inventory/models/sales/fulfilment.rb +1 -1
  49. data/lib/dear_inventory/models/sales/fulfilments/pick_pack.rb +1 -1
  50. data/lib/dear_inventory/models/sales/fulfilments/pick_pack_line.rb +1 -1
  51. data/lib/dear_inventory/models/sales/fulfilments/ship.rb +1 -1
  52. data/lib/dear_inventory/models/sales/fulfilments/ship_line.rb +1 -1
  53. data/lib/dear_inventory/models/sales/invoice.rb +1 -1
  54. data/lib/dear_inventory/models/sales/invoice_additional_charge.rb +1 -1
  55. data/lib/dear_inventory/models/sales/invoice_line.rb +1 -1
  56. data/lib/dear_inventory/models/sales/line.rb +1 -1
  57. data/lib/dear_inventory/models/sales/manual_journal.rb +1 -1
  58. data/lib/dear_inventory/models/sales/manual_journal_line.rb +1 -1
  59. data/lib/dear_inventory/models/sales/order.rb +1 -1
  60. data/lib/dear_inventory/models/sales/payment_line.rb +1 -1
  61. data/lib/dear_inventory/models/sales/quote.rb +1 -1
  62. data/lib/dear_inventory/models/{sale_lists.rb → sales_results.rb} +4 -4
  63. data/lib/dear_inventory/parameters/advanced_purchase/show.rb +25 -0
  64. data/lib/dear_inventory/parameters/purchase/index.rb +65 -6
  65. data/lib/dear_inventory/parameters/purchase/show.rb +25 -0
  66. data/lib/dear_inventory/parameters/sale/index.rb +95 -11
  67. data/lib/dear_inventory/parameters/sale/show.rb +35 -0
  68. data/lib/dear_inventory/resource.rb +8 -11
  69. data/lib/dear_inventory/resources/{purchase_list.rb → advanced_purchase.rb} +8 -5
  70. data/lib/dear_inventory/resources/customer.rb +2 -0
  71. data/lib/dear_inventory/resources/product.rb +2 -0
  72. data/lib/dear_inventory/resources/purchase.rb +38 -2
  73. data/lib/dear_inventory/resources/sale.rb +38 -2
  74. data/lib/dear_inventory/response.rb +118 -17
  75. data/lib/dear_inventory/version.rb +1 -1
  76. data/sorbet/rbi/hidden-definitions/hidden.rbi +0 -4
  77. metadata +29 -12
  78. data/lib/dear_inventory/parameters/purchase_list/index.rb +0 -84
  79. data/lib/dear_inventory/parameters/sale_list/index.rb +0 -119
  80. data/lib/dear_inventory/resources/sale_list.rb +0 -24
@@ -0,0 +1,35 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module DearInventory
5
+ class Parameters
6
+ module Sale
7
+ class Show < DearInventory::Parameters
8
+ extend T::Sig
9
+
10
+ fields(
11
+ id: {
12
+ property: :ID,
13
+ type: :String,
14
+ required: true,
15
+ },
16
+ combine_additional_charges: {
17
+ property: :CombineAdditionalCharges,
18
+ type: :Boolean,
19
+ required: false,
20
+ },
21
+ hide_inventory_movements: {
22
+ property: :HideInventoryMovements,
23
+ type: :Boolean,
24
+ required: false,
25
+ },
26
+ include_transactions: {
27
+ property: :IncludeTransactions,
28
+ type: :Boolean,
29
+ required: false,
30
+ }
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
@@ -15,28 +15,25 @@ module DearInventory
15
15
  ).returns(DearInventory::Response)
16
16
  end
17
17
  def request(action, model:, endpoint: nil, params: {})
18
- uri = resource_uri(endpoint)
19
- params = DearInventory::Parameters.convert(self.class, endpoint, params)
20
-
21
18
  request = DearInventory::Models::Request.new(
22
19
  action: action,
23
20
  model: model,
24
- params: params,
25
- uri: uri
21
+ params: DearInventory::Parameters.convert(self.class, endpoint, params),
22
+ uri: resource_uri(endpoint)
26
23
  )
27
24
  DearInventory::Request.(request)
28
25
  end
29
26
 
30
27
  private
31
28
 
32
- URI_BASE = "https://inventory.dearsystems.com/ExternalApi/v2"
29
+ URI_BASE = T.let("https://inventory.dearsystems.com/ExternalApi/v2", String)
33
30
 
34
- sig { params(endpoint: T.nilable(String)).returns(String) }
35
- def resource_uri(endpoint)
31
+ sig { params(_endpoint: T.nilable(String)).returns(String) }
32
+ def resource_uri(_endpoint)
36
33
  resource = T.must(self.class.name).split("::").last
37
- uri = "#{URI_BASE}/#{T.must(resource).downcase}"
38
- uri += "/#{endpoint}" unless endpoint.nil?
39
- uri
34
+ camel_case = Strings::Urlize.(T.must(resource))
35
+
36
+ "#{URI_BASE}/#{camel_case}"
40
37
  end
41
38
  end
42
39
  end
@@ -2,21 +2,24 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module DearInventory
5
- class PurchaseList < Resource
5
+ class AdvancedPurchase < Resource
6
6
  class << self
7
7
  extend T::Sig
8
8
 
9
- # Purchase List
9
+ # Advanced Purchase
10
10
  #
11
11
  # @param params [Hash] URL query string parameters that conform to
12
- # DearInventory::Parameters::PurchaseList::Index
12
+ # DearInventory::Parameters::Purchase::ShowAdvanced
13
13
  sig do
14
14
  params(params: T::Hash[Symbol, T.untyped]).
15
15
  returns(DearInventory::Response)
16
16
  end
17
- def index(params = {})
17
+ def show(params = {})
18
18
  new.request(
19
- :get, params: params, model: DearInventory::Models::PurchaseLists
19
+ :get,
20
+ endpoint: "show",
21
+ model: DearInventory::Models::AdvancedPurchase,
22
+ params: params
20
23
  )
21
24
  end
22
25
  end
@@ -19,6 +19,8 @@ module DearInventory
19
19
  :get, params: params, model: DearInventory::Models::Customers
20
20
  )
21
21
  end
22
+
23
+ alias call index
22
24
  end
23
25
  end
24
26
  end
@@ -19,6 +19,8 @@ module DearInventory
19
19
  :get, params: params, model: DearInventory::Models::Products
20
20
  )
21
21
  end
22
+
23
+ alias call index
22
24
  end
23
25
  end
24
26
  end
@@ -6,7 +6,7 @@ module DearInventory
6
6
  class << self
7
7
  extend T::Sig
8
8
 
9
- # Purchase
9
+ # Purchases
10
10
  #
11
11
  # @param params [Hash] URL query string parameters that conform to
12
12
  # DearInventory::Parameters::Purchase::Index
@@ -16,9 +16,45 @@ module DearInventory
16
16
  end
17
17
  def index(params = {})
18
18
  new.request(
19
- :get, params: params, model: DearInventory::Models::Purchase
19
+ :get,
20
+ endpoint: "index",
21
+ model: DearInventory::Models::PurchasesResults,
22
+ params: params
23
+ )
24
+ end
25
+
26
+ alias call index
27
+
28
+ # Purchase
29
+ #
30
+ # @param params [Hash] URL query string parameters that conform to
31
+ # DearInventory::Parameters::Purchase::Show
32
+ sig do
33
+ params(params: T::Hash[Symbol, T.untyped]).
34
+ returns(DearInventory::Response)
35
+ end
36
+ def show(params = {})
37
+ new.request(
38
+ :get,
39
+ endpoint: "show",
40
+ model: DearInventory::Models::Purchase,
41
+ params: params
20
42
  )
21
43
  end
22
44
  end
45
+
46
+ private
47
+
48
+ sig { params(endpoint: T.nilable(String)).returns(String) }
49
+ def resource_uri(endpoint)
50
+ suffix =
51
+ case endpoint
52
+ when "index"
53
+ "/purchaselist"
54
+ when "show"
55
+ "/purchase"
56
+ end
57
+ self.class.const_get(:URI_BASE) + suffix
58
+ end
23
59
  end
24
60
  end
@@ -6,7 +6,7 @@ module DearInventory
6
6
  class << self
7
7
  extend T::Sig
8
8
 
9
- # Sale
9
+ # Sales
10
10
  #
11
11
  # @param params [Hash] URL query string parameters that conform to
12
12
  # DearInventory::Parameters::Sale::Index
@@ -15,7 +15,43 @@ module DearInventory
15
15
  returns(DearInventory::Response)
16
16
  end
17
17
  def index(params = {})
18
- new.request(:get, params: params, model: DearInventory::Models::Sale)
18
+ new.request(
19
+ :get,
20
+ endpoint: "index",
21
+ model: DearInventory::Models::SalesResults,
22
+ params: params
23
+ )
24
+ end
25
+
26
+ alias call index
27
+
28
+ # Sale
29
+ #
30
+ # @param params [Hash] URL query string parameters that conform to
31
+ # DearInventory::Parameters::Sale::Show
32
+ sig do
33
+ params(params: T::Hash[Symbol, T.untyped]).
34
+ returns(DearInventory::Response)
35
+ end
36
+ def show(params = {})
37
+ new.request(
38
+ :get,
39
+ endpoint: "show",
40
+ model: DearInventory::Models::Sale,
41
+ params: params
42
+ )
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ sig { params(endpoint: T.nilable(String)).returns(String) }
49
+ def resource_uri(endpoint)
50
+ case endpoint
51
+ when "index"
52
+ self.class.const_get(:URI_BASE) + "/salelist"
53
+ when "show"
54
+ self.class.const_get(:URI_BASE) + "/sale"
19
55
  end
20
56
  end
21
57
  end
@@ -11,28 +11,54 @@ module DearInventory
11
11
  sig { returns(HTTP::Response) }
12
12
  attr_reader :response
13
13
 
14
+ # rubocop:disable Metrics/AbcSize
15
+ # rubocop:disable Metrics/MethodLength
14
16
  sig do
15
17
  params(
16
18
  request: DearInventory::Models::Request,
17
- response: HTTP::Response
19
+ response: HTTP::Response,
20
+ num_previous_records: Integer
18
21
  ).void
19
22
  end
20
- def initialize(request:, response:)
23
+ def initialize(request:, response:, num_previous_records: 0)
21
24
  @request = T.let(request, DearInventory::Models::Request)
22
25
  @response = T.let(response, HTTP::Response)
26
+ @num_previous_records = T.let(num_previous_records, Integer)
27
+
28
+ @fields = T.let(nil, T.nilable(T::Array[Symbol]))
23
29
  @http_status = T.let(nil, T.nilable(Integer))
24
- @model = T.let(nil, T.nilable(DearInventory::Model))
30
+ @load_full_record = T.let(nil, T.nilable(T::Boolean))
31
+ @num_records_paged = T.let(nil, T.nilable(Integer))
25
32
  @uri = T.let(nil, T.nilable(String))
26
33
 
27
- assign_values
28
34
  raise_error unless success?
35
+
36
+ body_copy = T.cast(body, T.nilable(T::Hash[String, T.untyped]))
37
+ @model = T.let(@request.model.new(body_copy), DearInventory::Model)
38
+ assign_values
39
+ end
40
+ # rubocop:enable Metrics/AbcSize
41
+ # rubocop:enable Metrics/MethodLength
42
+
43
+ sig { returns(T::Array[Symbol]) }
44
+ def fields
45
+ @fields ||= begin
46
+ values = @request.model.const_get(:FIELDS).values.map do |field|
47
+ field[:name]
48
+ end
49
+ values.unshift(:records) if @model.respond_to?(:records)
50
+ values
51
+ end
29
52
  end
30
53
 
31
54
  sig { returns(T.nilable(String)) }
32
55
  def error
33
- return body.fetch("Exception", nil) if body.respond_to?(:fetch)
56
+ if body.respond_to?(:fetch)
57
+ body_copy = T.cast(body, T::Hash[String, T.untyped])
58
+ return body_copy.fetch("Exception", nil)
59
+ end
34
60
 
35
- body
61
+ T.cast(body, T.nilable(String))
36
62
  end
37
63
 
38
64
  sig { returns(T::Hash[Symbol, String]) }
@@ -45,15 +71,54 @@ module DearInventory
45
71
  @http_status ||= @response.status.code
46
72
  end
47
73
 
74
+ sig { returns(T::Boolean) }
75
+ def paginated?
76
+ @model.respond_to?(:page)
77
+ end
78
+
79
+ sig { params(block: T.proc.params(arg0: DearInventory::Model).void).void }
80
+ def each(&block)
81
+ raise_not_paginated unless paginated?
82
+
83
+ response = self
84
+ loop do
85
+ iterate_over_records(response, block)
86
+ break unless response.next_page?
87
+
88
+ response = response.next_page
89
+ end
90
+ end
91
+
48
92
  sig { returns(DearInventory::Response) }
49
93
  def next_page
50
- unless T.must(@model).respond_to?(:page)
51
- raise DearInventory::NotPaginatedError.new(uri: uri)
52
- end
94
+ raise_not_paginated unless paginated?
95
+ raise DearInventory::NoMorePagesError unless next_page?
53
96
 
54
97
  request = @request.dup
55
98
  T.unsafe(request.params).page = T.unsafe(@model).page + 1
56
- DearInventory::Request.(request)
99
+
100
+ DearInventory::Request.(request, num_previous_records: num_records_paged)
101
+ end
102
+
103
+ sig { returns(T::Boolean) }
104
+ def next_page?
105
+ raise_not_paginated unless paginated?
106
+
107
+ num_records_paged < T.unsafe(self).total
108
+ end
109
+
110
+ sig { returns(Integer) }
111
+ def num_records_paged
112
+ @num_records_paged ||= begin
113
+ raise_not_paginated unless paginated?
114
+
115
+ @num_previous_records + T.unsafe(self).records.count
116
+ end
117
+ end
118
+
119
+ sig { void }
120
+ def raise_not_paginated
121
+ raise DearInventory::NotPaginatedError.new(uri: uri)
57
122
  end
58
123
 
59
124
  sig { returns(T::Boolean) }
@@ -68,31 +133,67 @@ module DearInventory
68
133
 
69
134
  protected
70
135
 
71
- sig { returns(T.untyped) }
136
+ sig { returns(T.any(T::Hash[String, T.untyped], String)) }
72
137
  def body
73
- JSON.parse(@response.body.to_s)
138
+ string_body = @response.body.to_s
139
+ JSON.parse(string_body)
140
+ rescue JSON::ParserError
141
+ string_body
74
142
  end
75
143
 
76
144
  private
77
145
 
78
146
  sig { void }
79
147
  def assign_values
80
- @model = @request.model.new(body)
81
- @request.model.const_get(:FIELDS).each do |_, specifications|
82
- define_singleton_method(specifications[:name]) do
83
- @model.public_send(specifications[:name])
84
- end
148
+ fields.each { |field| define_alias_method(field) }
149
+ end
150
+
151
+ sig { params(method_name: Symbol).void }
152
+ def define_alias_method(method_name)
153
+ define_singleton_method(method_name) do
154
+ @model.public_send(method_name)
85
155
  end
86
156
  end
87
157
 
158
+ API_LIMIT_ERROR = T.let(
159
+ "You have reached 60 calls per 60 seconds API limit.", String
160
+ )
161
+
162
+ # rubocop:disable Metrics/AbcSize
88
163
  sig { returns(DearInventory::Error) }
89
164
  def raise_error
90
165
  if http_status == 400
91
166
  raise T.unsafe(DearInventory::BadRequestError).new(error, self)
92
167
  end
93
168
 
169
+ if http_status == 503 && error == API_LIMIT_ERROR
170
+ raise T.unsafe(DearInventory::APILimitError)
171
+ end
172
+
94
173
  raise T.unsafe(DearInventory::Error).
95
174
  new("Unknown error (#{http_status}): #{error}")
96
175
  end
176
+ # rubocop:enable Metrics/AbcSize
177
+
178
+ sig do
179
+ params(
180
+ response: DearInventory::Response,
181
+ block: T.proc.params(arg0: DearInventory::Model).void
182
+ ).void
183
+ end
184
+ def iterate_over_records(response, block)
185
+ return if T.unsafe(response).records.nil?
186
+
187
+ T.unsafe(response).records.each do |record|
188
+ record = record.full_record if load_full_record?
189
+ block.call(record)
190
+ end
191
+ end
192
+
193
+ sig { returns(T::Boolean) }
194
+ def load_full_record?
195
+ @load_full_record ||=
196
+ T.unsafe(self).records.first.respond_to?(:full_record)
197
+ end
97
198
  end
98
199
  end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module DearInventory
5
- VERSION = "0.7.5"
5
+ VERSION = "1.0.0"
6
6
  end
@@ -14376,10 +14376,6 @@ module Readline
14376
14376
  def self.vi_editing_mode?(); end
14377
14377
  end
14378
14378
 
14379
- class Regexp
14380
- def match?(*_); end
14381
- end
14382
-
14383
14379
  module Reline
14384
14380
  def eof?(*args, &block); end
14385
14381
  FILENAME_COMPLETION_PROC = ::T.let(nil, ::T.untyped)