cin7_core_api 0.2.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +17 -0
- data/LICENSE.txt +21 -0
- data/README.md +262 -0
- data/lib/cin7_core_api/client.rb +104 -0
- data/lib/cin7_core_api/connection.rb +107 -0
- data/lib/cin7_core_api/errors.rb +45 -0
- data/lib/cin7_core_api/redactor.rb +183 -0
- data/lib/cin7_core_api/resources/accounts.rb +20 -0
- data/lib/cin7_core_api/resources/bank_accounts.rb +19 -0
- data/lib/cin7_core_api/resources/base.rb +49 -0
- data/lib/cin7_core_api/resources/credit_notes.rb +34 -0
- data/lib/cin7_core_api/resources/customer_credits.rb +18 -0
- data/lib/cin7_core_api/resources/customers.rb +22 -0
- data/lib/cin7_core_api/resources/invoices.rb +33 -0
- data/lib/cin7_core_api/resources/journals.rb +30 -0
- data/lib/cin7_core_api/resources/locations.rb +19 -0
- data/lib/cin7_core_api/resources/manual_journals.rb +17 -0
- data/lib/cin7_core_api/resources/me.rb +11 -0
- data/lib/cin7_core_api/resources/orders.rb +23 -0
- data/lib/cin7_core_api/resources/payments.rb +29 -0
- data/lib/cin7_core_api/resources/sales.rb +55 -0
- data/lib/cin7_core_api/resources/tax_rules.rb +16 -0
- data/lib/cin7_core_api/resources/webhooks.rb +26 -0
- data/lib/cin7_core_api/response.rb +26 -0
- data/lib/cin7_core_api/version.rb +5 -0
- data/lib/cin7_core_api.rb +27 -0
- metadata +169 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Cin7CoreAPI
|
|
6
|
+
# Only sanitized copies leave the transport. Never mutate a caller's payload.
|
|
7
|
+
class Redactor
|
|
8
|
+
FILTERED = "[FILTERED]"
|
|
9
|
+
SENSITIVE_KEY = /authorization|authentication|password|passwd|secret|token|cookie|apikey|applicationkey|apiauthaccountid|externalusername/i
|
|
10
|
+
GUID = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
|
|
11
|
+
HTTP_URL = %r{\A(https?://)([^/?#]*)([^?#]*)(?:\?([^#]*))?(?:\#(.*))?\z}i
|
|
12
|
+
|
|
13
|
+
def initialize(secrets = [])
|
|
14
|
+
@secrets = secrets.grep(String).reject(&:empty?).uniq.sort_by { |value| -value.length }.map { |value| value.dup.freeze }.freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def with_sensitive_values(value)
|
|
18
|
+
self.class.new(@secrets + sensitive_values(value))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def filter(value)
|
|
22
|
+
case value
|
|
23
|
+
when Hash
|
|
24
|
+
value.each_with_object({}) do |(key, entry), result|
|
|
25
|
+
# Field names are schema, even when a credential happens to match one.
|
|
26
|
+
result[key.to_s] = if sensitive_key?(key) || external_headers?(key)
|
|
27
|
+
FILTERED
|
|
28
|
+
elsif callback_url?(key) && entry.is_a?(String)
|
|
29
|
+
filter_url(entry)
|
|
30
|
+
elsif structured_identifier?(key, entry)
|
|
31
|
+
# Short header values must not corrupt identifiers or schema enums.
|
|
32
|
+
@secrets.include?(entry) ? FILTERED : entry.dup
|
|
33
|
+
else
|
|
34
|
+
filter(entry)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
when Array
|
|
38
|
+
value.map { |entry| filter(entry) }
|
|
39
|
+
when String
|
|
40
|
+
filter_string(value)
|
|
41
|
+
else
|
|
42
|
+
value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def inspect
|
|
47
|
+
"#<#{self.class}>"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def sensitive_key?(key)
|
|
53
|
+
normalized = normalized_key(key)
|
|
54
|
+
# AccountID and BankAccountId identify business records, not API access.
|
|
55
|
+
# The authentication header is api-auth-accountid; known credential values
|
|
56
|
+
# are still filtered wherever they occur, including business ID fields.
|
|
57
|
+
normalized != "externalauthorizationtype" && normalized.match?(SENSITIVE_KEY)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def normalized_key(key)
|
|
61
|
+
key.to_s.gsub(/[^a-z0-9]/i, "").downcase
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def external_headers?(key)
|
|
65
|
+
normalized_key(key) == "externalheaders"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def callback_url?(key)
|
|
69
|
+
normalized_key(key) == "externalurl"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def structured_identifier?(key, value)
|
|
73
|
+
value.is_a?(String) && ((normalized_key(key).end_with?("id") && GUID.match?(value)) ||
|
|
74
|
+
%w[type externalauthorizationtype].include?(normalized_key(key)))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def sensitive_values(value)
|
|
78
|
+
case value
|
|
79
|
+
when Hash
|
|
80
|
+
value.flat_map do |key, entry|
|
|
81
|
+
if external_headers?(key)
|
|
82
|
+
Array(entry).flat_map { |header| header.is_a?(Hash) ? strings_in(header["Value"] || header[:Value] || header["value"]) : [] }
|
|
83
|
+
elsif sensitive_key?(key)
|
|
84
|
+
strings_in(entry)
|
|
85
|
+
elsif callback_url?(key) && entry.is_a?(String)
|
|
86
|
+
url_credentials(entry)
|
|
87
|
+
else
|
|
88
|
+
sensitive_values(entry)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
when Array
|
|
92
|
+
value.flat_map { |entry| sensitive_values(entry) }
|
|
93
|
+
else
|
|
94
|
+
[]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def strings_in(value)
|
|
99
|
+
case value
|
|
100
|
+
when Hash then value.values.flat_map { |entry| strings_in(entry) }
|
|
101
|
+
when Array then value.flat_map { |entry| strings_in(entry) }
|
|
102
|
+
when String then [value, value.sub(/\A(?:Bearer|Basic)\s+/i, "")]
|
|
103
|
+
else []
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def sensitive_query_key?(key)
|
|
108
|
+
sensitive_key?(key) || %w[accountid username user auth key signature sig].include?(normalized_key(key))
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def url_credentials(value)
|
|
112
|
+
url = URI.parse(value)
|
|
113
|
+
return [] unless url.is_a?(URI::HTTP) && url.host
|
|
114
|
+
|
|
115
|
+
credentials = url.userinfo.to_s.split(":", 2).flat_map { |part| [part, URI::DEFAULT_PARSER.unescape(part)] }
|
|
116
|
+
[url.query, url.fragment].compact.each do |query|
|
|
117
|
+
query.split(/[&;]/).each do |part|
|
|
118
|
+
key, separator, entry = part.partition("=")
|
|
119
|
+
if !separator.empty? && sensitive_query_key?(URI.decode_www_form_component(key))
|
|
120
|
+
credentials.concat([entry, URI.decode_www_form_component(entry)])
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
strings_in(credentials)
|
|
125
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
126
|
+
[]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def filter_url(value)
|
|
130
|
+
return FILTERED if @secrets.include?(value)
|
|
131
|
+
|
|
132
|
+
url = URI.parse(value)
|
|
133
|
+
return FILTERED unless url.is_a?(URI::HTTP) && url.host
|
|
134
|
+
|
|
135
|
+
# Keep spelling, escaping, parameter order and delimiters intact. Rebuilding
|
|
136
|
+
# a URI or substring-filtering it can change callback ownership comparisons.
|
|
137
|
+
scheme, authority, path, query, fragment = HTTP_URL.match(value).captures
|
|
138
|
+
authority = filter_authority(authority, url.host)
|
|
139
|
+
path = path.split("/", -1).map { |part| filter_url_component(part) }.join("/")
|
|
140
|
+
result = "#{scheme}#{authority}#{path}"
|
|
141
|
+
result += "?#{filter_query(query)}" unless query.nil?
|
|
142
|
+
result += "##{filter_query(fragment)}" unless fragment.nil?
|
|
143
|
+
result
|
|
144
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
145
|
+
FILTERED
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def filter_authority(authority, host)
|
|
149
|
+
_, separator, host_port = authority.rpartition("@")
|
|
150
|
+
filtered_host = filter_url_component(host)
|
|
151
|
+
if filtered_host != FILTERED
|
|
152
|
+
filtered_host = host.split(".", -1).map { |part| filter_url_component(part) }.join(".")
|
|
153
|
+
end
|
|
154
|
+
filtered = host_port.sub(host) { filtered_host }
|
|
155
|
+
separator.empty? ? filtered : "#{FILTERED}@#{filtered}"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def filter_query(query)
|
|
159
|
+
query.split(/([&;])/, -1).map do |part|
|
|
160
|
+
key, separator, entry = part.partition("=")
|
|
161
|
+
if separator.empty?
|
|
162
|
+
filter_url_component(key, form: true)
|
|
163
|
+
else
|
|
164
|
+
filtered = sensitive_query_key?(URI.decode_www_form_component(key)) ? FILTERED : filter_url_component(entry, form: true)
|
|
165
|
+
"#{filter_url_component(key, form: true)}=#{filtered}"
|
|
166
|
+
end
|
|
167
|
+
end.join
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def filter_url_component(value, form: false)
|
|
171
|
+
decoded = form ? URI.decode_www_form_component(value) : URI::DEFAULT_PARSER.unescape(value)
|
|
172
|
+
(@secrets.include?(value) || @secrets.include?(decoded)) ? FILTERED : value
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def filter_string(value)
|
|
176
|
+
filtered = @secrets.reduce(value.dup) { |text, secret| text.gsub(secret, FILTERED) }
|
|
177
|
+
filtered = filtered.gsub(%r{(https?://)[^\s/@]+:[^\s/@]+@}i, "\\1#{FILTERED}@")
|
|
178
|
+
# Non-JSON error bodies may echo credentials as header or form text.
|
|
179
|
+
filtered = filtered.gsub(/\b(Bearer|Basic)\s+[^\s,"'<>]+/i, "\\1 #{FILTERED}")
|
|
180
|
+
filtered.gsub(/([\w-]*(?:password|passwd|secret|token|authorization|api[-_]?key|application[-_]?key|account[-_]?id)[\w-]*["']?\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;&<>]+)/i, "\\1#{FILTERED}")
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Accounts < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
page: "Page",
|
|
8
|
+
limit: "Limit",
|
|
9
|
+
code: "Code",
|
|
10
|
+
name: "Name",
|
|
11
|
+
type: "Type",
|
|
12
|
+
status: "Status"
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
def list(**filters)
|
|
16
|
+
get("ref/account", params: filters, parameter_map: PARAMETERS)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class BankAccounts < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
page: "Page",
|
|
8
|
+
limit: "Limit",
|
|
9
|
+
id: "ID",
|
|
10
|
+
name: "Name",
|
|
11
|
+
bank: "Bank"
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
def list(**filters)
|
|
15
|
+
get("ref/account/bank", params: filters, parameter_map: PARAMETERS)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Base
|
|
6
|
+
attr_reader :connection
|
|
7
|
+
|
|
8
|
+
def initialize(connection)
|
|
9
|
+
@connection = connection
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def inspect
|
|
13
|
+
"#<#{self.class}>"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def get(path, params: {}, parameter_map: {})
|
|
19
|
+
connection.get(path, params: map_parameters(params, parameter_map))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def validate_payload!(payload, *identifiers)
|
|
23
|
+
unless payload.is_a?(Hash) && payload.keys.all? { |key| key.is_a?(String) }
|
|
24
|
+
raise ArgumentError, "payload must be a Hash with Cin7 JSON field names as String keys"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
identifiers.each { |name| require_parameter!(name, payload[name]) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def map_parameters(params, parameter_map)
|
|
31
|
+
unknown = params.keys - parameter_map.keys
|
|
32
|
+
unless unknown.empty?
|
|
33
|
+
raise ArgumentError, "Unknown parameters: #{unknown.sort.join(", ")}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
params.each_with_object({}) do |(key, value), mapped|
|
|
37
|
+
mapped[parameter_map.fetch(key)] = value unless value.nil?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def require_parameter!(name, value)
|
|
42
|
+
missing = value.nil? || (value.is_a?(String) ? value.strip.empty? : value.respond_to?(:empty?) && value.empty?)
|
|
43
|
+
return unless missing
|
|
44
|
+
|
|
45
|
+
raise ArgumentError, "#{name} is required"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class CreditNotes < Base
|
|
6
|
+
LIST_PARAMETERS = {
|
|
7
|
+
page: "Page",
|
|
8
|
+
limit: "Limit",
|
|
9
|
+
search: "Search",
|
|
10
|
+
created_since: "CreatedSince",
|
|
11
|
+
updated_since: "UpdatedSince",
|
|
12
|
+
updated_until: "UpdatedUntil",
|
|
13
|
+
credit_note_status: "CreditNoteStatus",
|
|
14
|
+
status: "Status"
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
FOR_SALE_PARAMETERS = {
|
|
18
|
+
sale_id: "SaleID",
|
|
19
|
+
combine_additional_charges: "CombineAdditionalCharges",
|
|
20
|
+
include_product_info: "IncludeProductInfo",
|
|
21
|
+
include_payment_info: "IncludePaymentInfo"
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
def list(**filters)
|
|
25
|
+
get("saleCreditNoteList", params: filters, parameter_map: LIST_PARAMETERS)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def for_sale(sale_id:, **options)
|
|
29
|
+
require_parameter!(:sale_id, sale_id)
|
|
30
|
+
get("sale/creditnote", params: options.merge(sale_id: sale_id), parameter_map: FOR_SALE_PARAMETERS)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class CustomerCredits < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
page: "Page",
|
|
8
|
+
limit: "Limit",
|
|
9
|
+
customer_id: "CustomerID",
|
|
10
|
+
show_used_credits: "ShowUsedCredits"
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def list(**filters)
|
|
14
|
+
get("ref/customer/credits", params: filters, parameter_map: PARAMETERS)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Customers < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
page: "Page",
|
|
8
|
+
limit: "Limit",
|
|
9
|
+
id: "ID",
|
|
10
|
+
name: "Name",
|
|
11
|
+
modified_since: "ModifiedSince",
|
|
12
|
+
include_deprecated: "IncludeDeprecated",
|
|
13
|
+
include_product_prices: "IncludeProductPrices",
|
|
14
|
+
contact_filter: "ContactFilter"
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
def list(**filters)
|
|
18
|
+
get("customer", params: filters, parameter_map: PARAMETERS)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Invoices < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
sale_id: "SaleID",
|
|
8
|
+
combine_additional_charges: "CombineAdditionalCharges",
|
|
9
|
+
include_product_info: "IncludeProductInfo"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def for_sale(sale_id:, **options)
|
|
13
|
+
require_parameter!(:sale_id, sale_id)
|
|
14
|
+
get("sale/invoice", params: options.merge(sale_id: sale_id), parameter_map: PARAMETERS)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create(payload:)
|
|
18
|
+
validate_payload!(payload, "SaleID", "TaskID")
|
|
19
|
+
connection.post("sale/invoice", payload: payload)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update(payload:)
|
|
23
|
+
validate_payload!(payload, "SaleID", "TaskID")
|
|
24
|
+
connection.put("sale/invoice", payload: payload)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def undo(task_id:)
|
|
28
|
+
require_parameter!(:task_id, task_id)
|
|
29
|
+
connection.delete("sale/invoice", params: {"TaskID" => task_id, "Void" => false})
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Journals < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
page: "Page", limit: "Limit", task_id: "TaskID", status: "Status", search: "Search"
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
def list(**filters)
|
|
11
|
+
get("journal", params: filters, parameter_map: PARAMETERS)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def retrieve(task_id:)
|
|
15
|
+
require_parameter!(:task_id, task_id)
|
|
16
|
+
list(task_id: task_id)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create(payload:)
|
|
20
|
+
validate_payload!(payload)
|
|
21
|
+
connection.post("journal", payload: payload)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def update(payload:)
|
|
25
|
+
validate_payload!(payload, "TaskID")
|
|
26
|
+
connection.put("journal", payload: payload)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Locations < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
page: "Page",
|
|
8
|
+
limit: "Limit",
|
|
9
|
+
id: "ID",
|
|
10
|
+
deprecated: "Deprecated",
|
|
11
|
+
name: "Name"
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
def list(**filters)
|
|
15
|
+
get("ref/location", params: filters, parameter_map: PARAMETERS)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class ManualJournals < Base
|
|
6
|
+
def for_sale(sale_id:)
|
|
7
|
+
require_parameter!(:sale_id, sale_id)
|
|
8
|
+
get("sale/manualJournal", params: {sale_id: sale_id}, parameter_map: {sale_id: "SaleID"})
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create(payload:)
|
|
12
|
+
validate_payload!(payload, "SaleID")
|
|
13
|
+
connection.post("sale/manualJournal", payload: payload)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Orders < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
sale_id: "SaleID",
|
|
8
|
+
combine_additional_charges: "CombineAdditionalCharges",
|
|
9
|
+
include_product_info: "IncludeProductInfo"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def for_sale(sale_id:, **options)
|
|
13
|
+
require_parameter!(:sale_id, sale_id)
|
|
14
|
+
get("sale/order", params: options.merge(sale_id: sale_id), parameter_map: PARAMETERS)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create(payload:)
|
|
18
|
+
validate_payload!(payload, "SaleID")
|
|
19
|
+
connection.post("sale/order", payload: payload)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Payments < Base
|
|
6
|
+
PARAMETERS = {sale_id: "SaleID"}.freeze
|
|
7
|
+
|
|
8
|
+
def for_sale(sale_id:)
|
|
9
|
+
require_parameter!(:sale_id, sale_id)
|
|
10
|
+
get("sale/payment", params: {sale_id: sale_id}, parameter_map: PARAMETERS)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(payload:)
|
|
14
|
+
validate_payload!(payload, "TaskID")
|
|
15
|
+
connection.post("sale/payment", payload: payload)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def update(payload:)
|
|
19
|
+
validate_payload!(payload, "ID")
|
|
20
|
+
connection.put("sale/payment", payload: payload)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def delete(id:)
|
|
24
|
+
require_parameter!(:id, id)
|
|
25
|
+
connection.delete("sale/payment", params: {"ID" => id})
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Sales < Base
|
|
6
|
+
LIST_PARAMETERS = {
|
|
7
|
+
page: "Page",
|
|
8
|
+
limit: "Limit",
|
|
9
|
+
search: "Search",
|
|
10
|
+
created_since: "CreatedSince",
|
|
11
|
+
updated_since: "UpdatedSince",
|
|
12
|
+
updated_until: "UpdatedUntil",
|
|
13
|
+
ship_by: "ShipBy",
|
|
14
|
+
quote_status: "QuoteStatus",
|
|
15
|
+
order_status: "OrderStatus",
|
|
16
|
+
combined_pick_status: "CombinedPickStatus",
|
|
17
|
+
combined_pack_status: "CombinedPackStatus",
|
|
18
|
+
combined_shipping_status: "CombinedShippingStatus",
|
|
19
|
+
combined_invoice_status: "CombinedInvoiceStatus",
|
|
20
|
+
credit_note_status: "CreditNoteStatus",
|
|
21
|
+
external_id: "ExternalID",
|
|
22
|
+
status: "Status",
|
|
23
|
+
ready_for_shipping: "ReadyForShipping",
|
|
24
|
+
order_location_id: "OrderLocationID"
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
RETRIEVE_PARAMETERS = {
|
|
28
|
+
id: "ID",
|
|
29
|
+
combine_additional_charges: "CombineAdditionalCharges",
|
|
30
|
+
hide_inventory_movements: "HideInventoryMovements",
|
|
31
|
+
include_transactions: "IncludeTransactions",
|
|
32
|
+
country_format: "CountryFormat"
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
def list(**filters)
|
|
36
|
+
get("saleList", params: filters, parameter_map: LIST_PARAMETERS)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def retrieve(id:, **options)
|
|
40
|
+
require_parameter!(:id, id)
|
|
41
|
+
get("sale", params: options.merge(id: id), parameter_map: RETRIEVE_PARAMETERS)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def update(payload:)
|
|
45
|
+
validate_payload!(payload, "ID")
|
|
46
|
+
connection.put("sale", payload: payload)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def undo(id:)
|
|
50
|
+
require_parameter!(:id, id)
|
|
51
|
+
connection.delete("sale", params: {"ID" => id, "Void" => false})
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class TaxRules < Base
|
|
6
|
+
PARAMETERS = {
|
|
7
|
+
page: "Page", limit: "Limit", id: "ID", name: "Name", is_active: "IsActive",
|
|
8
|
+
is_tax_for_sale: "IsTaxForSale", is_tax_for_purchase: "IsTaxForPurchase", account: "Account"
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
def list(**filters)
|
|
12
|
+
get("ref/tax", params: filters, parameter_map: PARAMETERS)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
module Resources
|
|
5
|
+
class Webhooks < Base
|
|
6
|
+
def list
|
|
7
|
+
get("webhooks")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def create(payload:)
|
|
11
|
+
validate_payload!(payload)
|
|
12
|
+
connection.post("webhooks", payload: payload)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def update(payload:)
|
|
16
|
+
validate_payload!(payload, "ID")
|
|
17
|
+
connection.put("webhooks", payload: payload)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def delete(id:)
|
|
21
|
+
require_parameter!(:id, id)
|
|
22
|
+
connection.delete("webhooks", params: {"ID" => id})
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cin7CoreAPI
|
|
4
|
+
class Response
|
|
5
|
+
attr_reader :status, :headers, :body
|
|
6
|
+
|
|
7
|
+
def initialize(status:, headers:, body:)
|
|
8
|
+
@status = status
|
|
9
|
+
@headers = headers.freeze
|
|
10
|
+
@body = body
|
|
11
|
+
freeze
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def success?
|
|
15
|
+
(200..299).cover?(status)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def no_content?
|
|
19
|
+
status == 204 || body.nil?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def inspect
|
|
23
|
+
"#<#{self.class} status=#{status}>"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "cin7_core_api/version"
|
|
4
|
+
require_relative "cin7_core_api/errors"
|
|
5
|
+
require_relative "cin7_core_api/response"
|
|
6
|
+
require_relative "cin7_core_api/redactor"
|
|
7
|
+
require_relative "cin7_core_api/connection"
|
|
8
|
+
require_relative "cin7_core_api/resources/base"
|
|
9
|
+
require_relative "cin7_core_api/resources/me"
|
|
10
|
+
require_relative "cin7_core_api/resources/sales"
|
|
11
|
+
require_relative "cin7_core_api/resources/credit_notes"
|
|
12
|
+
require_relative "cin7_core_api/resources/payments"
|
|
13
|
+
require_relative "cin7_core_api/resources/locations"
|
|
14
|
+
require_relative "cin7_core_api/resources/accounts"
|
|
15
|
+
require_relative "cin7_core_api/resources/bank_accounts"
|
|
16
|
+
require_relative "cin7_core_api/resources/customers"
|
|
17
|
+
require_relative "cin7_core_api/resources/customer_credits"
|
|
18
|
+
require_relative "cin7_core_api/resources/orders"
|
|
19
|
+
require_relative "cin7_core_api/resources/invoices"
|
|
20
|
+
require_relative "cin7_core_api/resources/manual_journals"
|
|
21
|
+
require_relative "cin7_core_api/resources/journals"
|
|
22
|
+
require_relative "cin7_core_api/resources/tax_rules"
|
|
23
|
+
require_relative "cin7_core_api/resources/webhooks"
|
|
24
|
+
require_relative "cin7_core_api/client"
|
|
25
|
+
|
|
26
|
+
module Cin7CoreAPI
|
|
27
|
+
end
|