dear_inventory 0.7.5 → 1.0.0

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