cin7_api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7da8b2f7f6c38eaccde0b704581dde8fab9337b7f12a41faef7f12070bbfd1df
4
+ data.tar.gz: 6483ede66b64cb1cb877adb3b9412bcfedb4d3f91a2e87421155a12b9f51ea19
5
+ SHA512:
6
+ metadata.gz: c5a0fd863152ab5699888436345bc278f7de43b54761ae89546b7a08af8c0bddbdf6aaeddb4bf1d68bdac0dadee3ac5bead32d7fdb4ef905db8e6223a352e4d9
7
+ data.tar.gz: 78764e32b7388deea6c9ac51aa55dad9ed8601fdf6147f2fb217cddbbd32e52db7e3b842b22c51d532f989fc8a062856c1c0adb7833e79b513155e6886beca9d
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/.streerc ADDED
@@ -0,0 +1 @@
1
+ --print-width=100
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-09-13
4
+
5
+ - Initial release
@@ -0,0 +1,84 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or
22
+ advances of any kind
23
+ * Trolling, insulting or derogatory comments, and personal or political attacks
24
+ * Public or private harassment
25
+ * Publishing others' private information, such as a physical or email
26
+ address, without their explicit permission
27
+ * Other conduct which could reasonably be considered inappropriate in a
28
+ professional setting
29
+
30
+ ## Enforcement Responsibilities
31
+
32
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
+
34
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
+
36
+ ## Scope
37
+
38
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
+
40
+ ## Enforcement
41
+
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at tech@weizheheng.com. All complaints will be reviewed and investigated promptly and fairly.
43
+
44
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
+
46
+ ## Enforcement Guidelines
47
+
48
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
+
50
+ ### 1. Correction
51
+
52
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
+
54
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
+
56
+ ### 2. Warning
57
+
58
+ **Community Impact**: A violation through a single incident or series of actions.
59
+
60
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
+
62
+ ### 3. Temporary Ban
63
+
64
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
+
66
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
+
68
+ ### 4. Permanent Ban
69
+
70
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
+
72
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
73
+
74
+ ## Attribution
75
+
76
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
+ available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
+
79
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
+
81
+ [homepage]: https://www.contributor-covenant.org
82
+
83
+ For answers to common questions about this code of conduct, see the FAQ at
84
+ https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in cin7_api.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
11
+
12
+ gem "rubocop", "~> 1.21"
data/Gemfile.lock ADDED
@@ -0,0 +1,88 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cin7_api (0.1.0)
5
+ activesupport
6
+ faraday (~> 2.5)
7
+ zeitwerk
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (7.0.4)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 1.6, < 2)
15
+ minitest (>= 5.1)
16
+ tzinfo (~> 2.0)
17
+ ast (2.4.2)
18
+ coderay (1.1.3)
19
+ concurrent-ruby (1.1.10)
20
+ diff-lcs (1.5.0)
21
+ dotenv (2.8.1)
22
+ faraday (2.5.2)
23
+ faraday-net_http (>= 2.0, < 3.1)
24
+ ruby2_keywords (>= 0.0.4)
25
+ faraday-net_http (3.0.0)
26
+ i18n (1.12.0)
27
+ concurrent-ruby (~> 1.0)
28
+ json (2.6.2)
29
+ method_source (1.0.0)
30
+ minitest (5.16.3)
31
+ parallel (1.22.1)
32
+ parser (3.1.2.1)
33
+ ast (~> 2.4.1)
34
+ pry (0.14.1)
35
+ coderay (~> 1.1)
36
+ method_source (~> 1.0)
37
+ rainbow (3.1.1)
38
+ rake (13.0.6)
39
+ regexp_parser (2.5.0)
40
+ rexml (3.2.5)
41
+ rspec (3.11.0)
42
+ rspec-core (~> 3.11.0)
43
+ rspec-expectations (~> 3.11.0)
44
+ rspec-mocks (~> 3.11.0)
45
+ rspec-core (3.11.0)
46
+ rspec-support (~> 3.11.0)
47
+ rspec-expectations (3.11.1)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.11.0)
50
+ rspec-mocks (3.11.1)
51
+ diff-lcs (>= 1.2.0, < 2.0)
52
+ rspec-support (~> 3.11.0)
53
+ rspec-support (3.11.1)
54
+ rubocop (1.36.0)
55
+ json (~> 2.3)
56
+ parallel (~> 1.10)
57
+ parser (>= 3.1.2.1)
58
+ rainbow (>= 2.2.2, < 4.0)
59
+ regexp_parser (>= 1.8, < 3.0)
60
+ rexml (>= 3.2.5, < 4.0)
61
+ rubocop-ast (>= 1.20.1, < 2.0)
62
+ ruby-progressbar (~> 1.7)
63
+ unicode-display_width (>= 1.4.0, < 3.0)
64
+ rubocop-ast (1.21.0)
65
+ parser (>= 3.1.1.0)
66
+ ruby-progressbar (1.11.0)
67
+ ruby2_keywords (0.0.5)
68
+ tzinfo (2.0.5)
69
+ concurrent-ruby (~> 1.0)
70
+ unicode-display_width (2.3.0)
71
+ vcr (6.1.0)
72
+ zeitwerk (2.6.0)
73
+
74
+ PLATFORMS
75
+ x86_64-darwin-21
76
+ x86_64-linux
77
+
78
+ DEPENDENCIES
79
+ cin7_api!
80
+ dotenv
81
+ pry
82
+ rake (~> 13.0)
83
+ rspec (~> 3.0)
84
+ rubocop (~> 1.21)
85
+ vcr
86
+
87
+ BUNDLED WITH
88
+ 2.3.8
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 PostCo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # Cin7Api
2
+
3
+ This is a Cin7 API wrapper gem to perform CRUD actions to the following resources:
4
+ - SalesOrders
5
+ - CreditNotes
6
+ - Payments
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'cin7_api'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install cin7_api
23
+
24
+ ## Usage
25
+
26
+ ### Prerequisite
27
+ This Gem requires the following Cin7 API permission:
28
+ 1. SalesOrder: READ and UPDATE
29
+ 2. CreditNote: READ and UPDATE
30
+ 3. Payment: READ, UPDATE and WRITE
31
+
32
+ ### SalesOrder
33
+
34
+ ```ruby
35
+ client = Cin7API::Client.new(username: ENV["CIN7_USERNAME"], api_key: ENV["CIN7_API_KEY"])
36
+
37
+ # Retrieve a list of SalesOrders with filter params
38
+ # NOTE: Cin7 does a binary OR check on the filter params
39
+ # This query will return SalesOrders with reference "#1050" OR email "postco360@example.com"
40
+ client.sales_order.where(reference: "#1050", email: "postco360@example.com")
41
+
42
+ # Find SalesOrder by filter params
43
+ # NOTE: This will do a array#find for SalesOrder matching all the filter params
44
+ # Return nil if none is found
45
+ client.sales_order.find_by(reference: "#1050", email: "postco360@example.com")
46
+
47
+ # Find SalesOrder by id
48
+ # Return SalesOrder or raise Cin7API::Error
49
+ client.sales_order.find(2218)
50
+
51
+ # Update SalesOrders
52
+ # NOTE: Cin7 only allow bulk updates
53
+ # NOTE: The key has to be in lower camelCase
54
+ attributes_array = [
55
+ {
56
+ id: 2221,
57
+ lineItems: [{ id: 8140, unitPrice: 100 }],
58
+ dispatchedDate: "2022-09-16T09:00:00Z",
59
+ invoiceDate: "2022-09-16T09:00:00Z"
60
+ }
61
+ ]
62
+ # => [#<Cin7API::Response index=0, success=true, id=2221, code="#1052", errors=[]>]
63
+ client.sales_order.update(attributes_array)
64
+ ```
65
+
66
+ ### CreditNote
67
+
68
+ ```ruby
69
+ client = Cin7API::Client.new(username: ENV["CIN7_USERNAME"], api_key: ENV["CIN7_API_KEY"])
70
+
71
+ # Retrieve a list of CreditNotes with filter params
72
+ # NOTE: Cin7 does a binary OR check on the filter params
73
+ # This query will return CreditNotes with sales_reference "#1050" OR email "postco360@example.com"
74
+ client.credit_note.where(sales_reference: "#1050", email: "postco360@example.com")
75
+
76
+ # Find CreditNote by filter params
77
+ # NOTE: This will do a array#find for CreditNote matching all the filter params
78
+ # Return nil if none is found
79
+ client.credit_note.find_by(reference: "#1050", email: "postco360@example.com")
80
+
81
+ # Find CreditNote by id
82
+ # Return CreditNote or raise Cin7API::Error
83
+ client.credit_note.find(2223)
84
+
85
+ # Update CreditNote
86
+ # NOTE: Cin7 only allow bulk updates
87
+ # NOTE: The key has to be in lower camelCase
88
+ attributes_array = [
89
+ {
90
+ id: 2223,
91
+ lineItems: [{ id: 8142, unitPrice: 10 }, {id: 8143, unitPrice: 10}],
92
+ completedDate: "2022-09-10T09:00:00Z",
93
+ creditNoteDate: "2022-09-10T09:00:00Z",
94
+ }
95
+ ]
96
+ # => [#<Cin7API::Response index=0, success=true, id=2223, code="CRN-2223", errors=[]>]
97
+ client.credit_note.update(attributes_array)
98
+ ```
99
+
100
+ ### Payment
101
+
102
+ ```ruby
103
+ client = Cin7API::Client.new(username: ENV["CIN7_USERNAME"], api_key: ENV["CIN7_API_KEY"])
104
+
105
+ # Retrieve a list of Payments with filter params
106
+ # NOTE: Cin7 does a binary OR check on the filter params
107
+ # This query will return Payments with order_id 2223 OR order_ref "CRN-2223"
108
+ client.payment.where(order_id: 2223, order_ref: "CRN-2223")
109
+
110
+ # Find Payment by filter params
111
+ # NOTE: This will do a array#find for Payment matching all the filter params
112
+ # Return nil if none is found
113
+ client.payment.find_by(order_id: 2223, order_ref: "CRN-2223")
114
+
115
+ # Find Payment by id
116
+ # Return Payment or raise Cin7API::Error
117
+ client.payment.find(698)
118
+
119
+ # Create Payment
120
+ # NOTE: Cin7 only allow bulk creates
121
+ # NOTE: The key has to be in lower camelCase
122
+ attributes_array = [
123
+ {
124
+ orderType: "CreditNote",
125
+ orderRef: "CRN-2223",
126
+ orderId: 2223,
127
+ method: "Not Paid",
128
+ amount: 10,
129
+ paymentDate: "2022-09-10T09:00:00Z"
130
+ }
131
+ ]
132
+ # => [#<Cin7API::Response index=0, success=true, id=713, code=nil, errors=[]>]
133
+ client.payment.create(attributes_array)
134
+
135
+ # Update Payment
136
+ # NOTE: Cin7 only allow bulk updates
137
+ # NOTE: The key has to be in lower camelCase
138
+ attributes_array = [{ id: 713, orderId: 2223, method: "manual", amount: 0 }]
139
+ # => [#<Cin7API::Response index=0, success=true, id=713, code=nil, errors=[]>]
140
+ client.payment.update(attributes_array)
141
+ ```
142
+
143
+ ## Development
144
+
145
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
146
+
147
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
148
+
149
+ ## Contributing
150
+
151
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/cin7_api. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/cin7_api/blob/main/CODE_OF_CONDUCT.md).
152
+
153
+ ## License
154
+
155
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
156
+
157
+ ## Code of Conduct
158
+
159
+ Everyone interacting in the Cin7Api project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/cin7_api/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: %i[spec]
data/cin7_api.gemspec ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/cin7_api/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "cin7_api"
7
+ spec.version = Cin7API::VERSION
8
+ spec.authors = ["Wei Zhe Heng"]
9
+ spec.email = ["marcusheng@postco.co"]
10
+
11
+ spec.summary = "Cin7 API Wrapper"
12
+ spec.homepage = "https://github.com/PostCo/cin7_api"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://github.com/PostCo/cin7_api"
20
+ spec.metadata["changelog_uri"] = "https://github.com/PostCo/cin7_api/releases"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
27
+ end
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ # Uncomment to register a new dependency of your gem
34
+ # spec.add_dependency "example-gem", "~> 1.0"
35
+
36
+ # For more information and examples about making a new gem, check out our
37
+ # guide at: https://bundler.io/guides/creating_gem.html
38
+ spec.add_dependency "activesupport"
39
+ spec.add_dependency "zeitwerk"
40
+ spec.add_development_dependency("dotenv")
41
+ spec.add_dependency "faraday", "~> 2.5"
42
+ spec.metadata["rubygems_mfa_required"] = "true"
43
+ spec.add_development_dependency("pry")
44
+ spec.add_development_dependency("vcr")
45
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+
5
+ module Cin7API
6
+ class Client
7
+ BASE_URL = "https://api.cin7.com/api/v1/"
8
+
9
+ attr_reader :username, :api_key, :adapter
10
+
11
+ def initialize(username:, api_key:, adapter: Faraday.default_adapter)
12
+ @username = username
13
+ @api_key = api_key
14
+ @adapter = adapter
15
+ end
16
+
17
+ def sales_order
18
+ SalesOrderResource.new(self)
19
+ end
20
+
21
+ def credit_note
22
+ CreditNoteResource.new(self)
23
+ end
24
+
25
+ def payment
26
+ PaymentResource.new(self)
27
+ end
28
+
29
+ def connection
30
+ @connection ||= Faraday.new do |conn|
31
+ conn.url_prefix = BASE_URL
32
+ conn.request :json
33
+ conn.response :json, content_type: "application/json"
34
+ conn.adapter adapter
35
+ conn.request :authorization, :basic, username, api_key
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string"
4
+ require "ostruct"
5
+
6
+ module Cin7API
7
+ class Object < OpenStruct
8
+ def initialize(attributes)
9
+ super to_ostruct(attributes)
10
+ end
11
+
12
+ def to_ostruct(obj)
13
+ if obj.is_a?(Hash)
14
+ OpenStruct.new(obj.map { |key, val| [key.underscore, to_ostruct(val)] }.to_h)
15
+ elsif obj.is_a?(Array)
16
+ obj.map { |o| to_ostruct(o) }
17
+ else # Assumed to be a primitive value
18
+ obj
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ class CreditNote < Object
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ class Payment < Object
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ class Response < Object
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ class SalesOrder < Object
5
+ end
6
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string"
4
+
5
+ module Cin7API
6
+ class Resource
7
+ attr_reader :client
8
+
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def get_request(url, params: {}, headers: {})
14
+ handle_response client.connection.get(url, parsed_params(params), headers)
15
+ end
16
+
17
+ def post_request(url, body:, headers: {})
18
+ handle_response client.connection.post(url, body, headers)
19
+ end
20
+
21
+ def put_request(url, body:, headers: {})
22
+ handle_response client.connection.put(url, body, headers)
23
+ end
24
+
25
+ def handle_response(response)
26
+ error_message = response.body
27
+
28
+ case response.status
29
+ when 400
30
+ raise Error, "A bad request or a validation exception has occurred. #{error_message}"
31
+ when 401
32
+ raise Error, "Invalid authorization credentials. #{error_message}"
33
+ when 403
34
+ raise Error, "Connection doesn't have permission to access the resource. #{error_message}"
35
+ when 404
36
+ raise Error, "The resource you have specified cannot be found. #{error_message}"
37
+ when 429
38
+ raise Error, "The API rate limit for your application has been exceeded. #{error_message}"
39
+ when 500
40
+ raise Error,
41
+ "An unhandled error with the Cin7 API. Contact the Cin7 API team if problems persist. #{error_message}"
42
+ when 503
43
+ raise Error,
44
+ "API is currently unavailable – typically due to a scheduled outage – try again soon. #{error_message}"
45
+ end
46
+
47
+ response
48
+ end
49
+
50
+ private
51
+
52
+ def parsed_params(params)
53
+ # E.g: Need to convert to where: ["OrderId=2223", "SalesRefernce='CRN-2223'"]
54
+ parsed_params = params.each_with_object([]) do |(key, value), array|
55
+ # Cin7 requires string value filter to be wrapped in single-quote
56
+ parsed_value = value.is_a?(String) ? %('#{value}') : value
57
+ array << "#{key.to_s.camelcase}=#{parsed_value}"
58
+ end
59
+
60
+ # Only handle for Cin7 where query for now
61
+ { where: parsed_params }
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ class CreditNoteResource < Resource
5
+ # NOTE: Cin7 is doing a binary OR check for the filter params
6
+ # @client.credit_note.where(sales_reference: "#1050", email: "postco360@example.com")
7
+ def where(**params)
8
+ credit_notes = get_request("CreditNotes", params: params).body
9
+ credit_notes.map { |attributes| CreditNote.new(attributes) }
10
+ end
11
+
12
+ # @client.credit_note.find_by(sales_reference: "#1050", email: "postco360@example.com")
13
+ def find_by(**params)
14
+ credit_note =
15
+ where(**params).find do |p|
16
+ params.reduce(true) { |acc, (key, value)| acc && p[key] == value }
17
+ end
18
+ credit_note ? CreditNote.new(credit_note) : nil
19
+ end
20
+
21
+ # @client.credit_note.find(2223)
22
+ def find(id)
23
+ CreditNote.new(get_request("CreditNotes/#{id}").body)
24
+ end
25
+
26
+ # attributes_array = [
27
+ # {
28
+ # id: 2223,
29
+ # lineItems: [{ id: 8142, unitPrice: 10 }, {id: 8143, unitPrice: 10}],
30
+ # completedDate: "2022-09-10T09:00:00Z",
31
+ # creditNoteDate: "2022-09-10T09:00:00Z",
32
+ # }
33
+ # ]
34
+ # @client.credit_note.update(attributes_array)
35
+ def update(attributes_array)
36
+ responses = put_request("CreditNotes", body: attributes_array).body
37
+ responses.map { |attributes| Response.new(attributes) }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ class PaymentResource < Resource
5
+ # @client.payment.where(order_id: 2223)
6
+ def where(**params)
7
+ payments = get_request("Payments", params: params).body
8
+ payments.map { |attributes| Payment.new(attributes) }
9
+ end
10
+
11
+ # @client.payment.find_by(order_id: 2223, order_ref: "CRN-2223")
12
+ def find_by(**params)
13
+ payment =
14
+ where(**params).find do |p|
15
+ params.reduce(true) { |acc, (key, value)| acc && p[key] == value }
16
+ end
17
+ payment.present? ? payment : nil
18
+ end
19
+
20
+ # @client.payment.find(698)
21
+ def find(id)
22
+ Payment.new(get_request("Payments/#{id}").body)
23
+ end
24
+
25
+ # attributes_array = [
26
+ # {
27
+ # orderType: "CreditNote",
28
+ # orderRef: "CRN-2223",
29
+ # orderId: 2223,
30
+ # method: "Not Paid",
31
+ # amount: 10,
32
+ # paymentDate: "2022-09-10T09:00:00Z",
33
+ # }
34
+ # ]
35
+ # @client.payment.create(attributes_array)
36
+ def create(attributes_array)
37
+ responses = post_request("Payments", body: attributes_array).body
38
+ responses.map { |attributes| Response.new(attributes) }
39
+ end
40
+
41
+ # attributes_array = [
42
+ # {
43
+ # id: 712,
44
+ # orderId: 2223,
45
+ # method: "manual",
46
+ # amount: 0,
47
+ # }
48
+ # ]
49
+ # @client.payment.update(attributes_array)
50
+ def update(attributes_array)
51
+ responses = put_request("Payments", body: attributes_array).body
52
+ responses.map { |attributes| Response.new(attributes) }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ class SalesOrderResource < Resource
5
+ # NOTE: Cin7 is doing a binary OR check for the filter params
6
+ # @client.sales_order.where(reference: "#1050", email: "postco360@example.com")
7
+ def where(**params)
8
+ sales_orders = get_request("SalesOrders", params: params).body
9
+ sales_orders.map { |attributes| SalesOrder.new(attributes) }
10
+ end
11
+
12
+ # @client.sales_order.find_by(reference: "#1050", email: "postco360@example.com")
13
+ def find_by(**params)
14
+ sales_order =
15
+ where(**params).find do |p|
16
+ params.reduce(true) { |acc, (key, value)| acc && p[key] == value }
17
+ end
18
+ sales_order ? SalesOrder.new(sales_order) : nil
19
+ end
20
+
21
+ # @client.sales_order.find(2218)
22
+ def find(id)
23
+ SalesOrder.new(get_request("SalesOrders/#{id}").body)
24
+ end
25
+
26
+ # attributes_array = [
27
+ # {
28
+ # id: 2221,
29
+ # lineItems: [{ id: 8140, unitPrice: 100 }],
30
+ # dispatchedDate: "2022-09-16T09:00:00Z",
31
+ # invoiceDate: "2022-09-16T09:00:00Z"
32
+ # }
33
+ # ]
34
+ # @client.sales_order.update(attributes_array)
35
+ def update(attributes_array)
36
+ responses = put_request("SalesOrders", body: attributes_array).body
37
+ responses.map { |attributes| Response.new(attributes) }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cin7API
4
+ VERSION = "0.1.0"
5
+ end
data/lib/cin7_api.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "cin7_api/version"
4
+ require "zeitwerk"
5
+
6
+ loader = Zeitwerk::Loader.for_gem
7
+ loader.inflector.inflect(
8
+ "cin7_api" => "Cin7API"
9
+ )
10
+ loader.collapse("./lib/cin7_api/resources")
11
+ loader.collapse("./lib/cin7_api/objects")
12
+ loader.setup # ready!
13
+
14
+ module Cin7Api
15
+ end
data/sig/cin7_api.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Cin7Api
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cin7_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Wei Zhe Heng
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-09-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: zeitwerk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dotenv
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.5'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: vcr
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email:
99
+ - marcusheng@postco.co
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".rspec"
105
+ - ".rubocop.yml"
106
+ - ".streerc"
107
+ - CHANGELOG.md
108
+ - CODE_OF_CONDUCT.md
109
+ - Gemfile
110
+ - Gemfile.lock
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - cin7_api.gemspec
115
+ - lib/cin7_api.rb
116
+ - lib/cin7_api/client.rb
117
+ - lib/cin7_api/error.rb
118
+ - lib/cin7_api/object.rb
119
+ - lib/cin7_api/objects/credit_note.rb
120
+ - lib/cin7_api/objects/payment.rb
121
+ - lib/cin7_api/objects/response.rb
122
+ - lib/cin7_api/objects/sales_order.rb
123
+ - lib/cin7_api/resource.rb
124
+ - lib/cin7_api/resources/credit_note_resource.rb
125
+ - lib/cin7_api/resources/payment_resource.rb
126
+ - lib/cin7_api/resources/sales_order_resource.rb
127
+ - lib/cin7_api/version.rb
128
+ - sig/cin7_api.rbs
129
+ homepage: https://github.com/PostCo/cin7_api
130
+ licenses:
131
+ - MIT
132
+ metadata:
133
+ allowed_push_host: https://rubygems.org/
134
+ homepage_uri: https://github.com/PostCo/cin7_api
135
+ source_code_uri: https://github.com/PostCo/cin7_api
136
+ changelog_uri: https://github.com/PostCo/cin7_api/releases
137
+ rubygems_mfa_required: 'true'
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: 2.6.0
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubygems_version: 3.3.22
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Cin7 API Wrapper
157
+ test_files: []