moloni_api 0.1.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.
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_exceptions'
4
+ require_relative 'configuration'
5
+ require_relative 'constants'
6
+ require_relative 'http_status_codes'
7
+ require 'json'
8
+
9
+ module MoloniApi
10
+ # Core class responsible for api interface operations
11
+ class API
12
+ include ApiExceptions
13
+ include Constants
14
+ include HttpStatusCodes
15
+
16
+ attr_reader(*MoloniApi.configuration.property_names, :token, :endpoint)
17
+
18
+ attr_accessor :current_options
19
+
20
+ # Callback to update current configuration options
21
+ class_eval do
22
+ MoloniApi.configuration.property_names.each do |key|
23
+ define_method "#{key}=" do |arg|
24
+ instance_variable_set("@#{key}", arg)
25
+ current_options.merge!({ "#{key}": arg })
26
+ end
27
+ end
28
+ end
29
+
30
+ API_ENDPOINT = 'https://api.moloni.pt/sandbox/'
31
+ HTTP_STATUS_MAPPING = {
32
+ HTTP_BAD_REQUEST_CODE => BadRequestError,
33
+ HTTP_UNAUTHORIZED_CODE => UnauthorizedError,
34
+ HTTP_FORBIDDEN_CODE => ForbiddenError,
35
+ HTTP_NOT_FOUND_CODE => NotFoundError,
36
+ HTTP_UNPROCESSABLE_ENTITY_CODE => UnprocessableEntityError,
37
+ 'default' => ApiError
38
+ }.freeze
39
+
40
+ # Create new API
41
+ #
42
+ # @api public
43
+ def initialize(options = {}, &block)
44
+ opts = MoloniApi.configuration.fetch.merge(options)
45
+ @current_options = opts
46
+
47
+ MoloniApi.configuration.property_names.each do |key|
48
+ send("#{key}=", opts[key])
49
+ end
50
+ @api_access_token = opts[:access_token] # If not provided will need to ask for credentials
51
+ @api_client_id = opts[:client_id] || ENV['MOLONI_API_CLIENT_ID']
52
+ @api_client_secret = opts[:client_secret] || ENV['MOLONI_API_CLIENT_SECRET']
53
+ @api_endpoint = opts[:endpoint] || ENV['MOLONI_API_ENDPOINT'] || API_ENDPOINT
54
+
55
+ yield_or_eval(&block) if block_given?
56
+ end
57
+
58
+ # Call block with argument
59
+ #
60
+ # @api private
61
+ def yield_or_eval(&block)
62
+ return unless block
63
+
64
+ block.arity.positive? ? yield(self) : instance_eval(&block)
65
+ end
66
+
67
+ private
68
+
69
+ def client
70
+ # provide your own logger
71
+ logger = Logger.new $stderr
72
+ logger.level = Logger::DEBUG
73
+ @client ||= Faraday.new(@api_endpoint) do |client|
74
+ client.request :url_encoded
75
+ #client.request :json
76
+ #client.response :json
77
+ client.adapter Faraday.default_adapter
78
+ client.response :logger, logger
79
+ end
80
+ end
81
+
82
+ def request(http_method:, endpoint:, params: {}, query_params: {}, add_access_token: true)
83
+ if add_access_token
84
+ query_params[:access_token] = @api_access_token
85
+ query_params[:json] = true
86
+ end
87
+ response = client.public_send(http_method, endpoint) do |req|
88
+ req.headers['Content-Type'] = 'application/json'
89
+ req.params = query_params
90
+ req.body = params.to_json if params.size
91
+ end
92
+ logger = Logger.new $stderr
93
+ logger.debug('Response: ' + response.body)
94
+
95
+ if response_successful?(response)
96
+ parsed_response = Oj.load(response.body)
97
+ return parsed_response
98
+ end
99
+
100
+ raise error_class(response), "Code: #{response.status}, response: #{response.body}"
101
+ end
102
+
103
+ def error_class(response)
104
+ if HTTP_STATUS_MAPPING.include?(response.status)
105
+ HTTP_STATUS_MAPPING[response.status]
106
+ else
107
+ HTTP_STATUS_MAPPING['default']
108
+ end
109
+ end
110
+
111
+ def response_successful?(response)
112
+ response.status == HTTP_OK_CODE
113
+ end
114
+
115
+ # Responds to attribute query or attribute clear
116
+ #
117
+ # @api private
118
+ def method_missing(method_name, *args, &block)
119
+ # :nodoc:
120
+ case method_name.to_s
121
+ when /^(.*)\?$/
122
+ !!send(Regexp.last_match(1).to_s)
123
+ when /^clear_(.*)$/
124
+ send("#{Regexp.last_match(1)}=", nil)
125
+ else
126
+ super
127
+ end
128
+ end
129
+
130
+ def respond_to_missing?(method_name, include_private = false)
131
+ method_name.to_s.start_with?('clear_') || super
132
+ end
133
+ end
134
+ end
135
+ # mapi = MoloniApi.new(token: )
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoloniApi
4
+ module ApiExceptions
5
+ APIExceptionError = Class.new(StandardError)
6
+ BadRequestError = Class.new(APIExceptionError)
7
+ UnauthorizedError = Class.new(APIExceptionError)
8
+ ForbiddenError = Class.new(APIExceptionError)
9
+ ApiRequestsQuotaReachedError = Class.new(APIExceptionError)
10
+ NotFoundError = Class.new(APIExceptionError)
11
+ UnprocessableEntityError = Class.new(APIExceptionError)
12
+ ApiError = Class.new(APIExceptionError)
13
+ end
14
+ end
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'logger'
5
+ require_relative 'api'
6
+ require_relative 'models/product'
7
+
8
+ module MoloniApi
9
+ # Main client class that implements communication with the API
10
+ # other_params - view documentation for additional options:
11
+ # https://www.moloni.pt/dev/endpoints/
12
+ # - qty
13
+ # - offset
14
+ # global:
15
+ # - access_token
16
+ # - json
17
+ # - human_errors
18
+ class Client < API
19
+
20
+ attr_accessor :api_access_token, :api_refresh_token
21
+
22
+ def authenticate(user_username, user_password)
23
+ response = request(
24
+ http_method: :get,
25
+ endpoint: 'grant/',
26
+ query_params: {
27
+ grant_type: 'password',
28
+ client_id: @api_client_id,
29
+ client_secret: @api_client_secret,
30
+ username: user_username,
31
+ password: user_password
32
+ },
33
+ add_access_token: false
34
+ )
35
+ res = process_response(response)
36
+ self.api_access_token = res[:access_token]
37
+ self.api_refresh_token = res[:refresh_token]
38
+ return res
39
+ end
40
+
41
+ def renew_token(refresh_token = nil)
42
+ response = request(
43
+ http_method: :get,
44
+ endpoint: 'grant/',
45
+ query_params: {
46
+ grant_type: 'refresh_token',
47
+ client_id: @api_client_id,
48
+ client_secret: @api_client_secret,
49
+ refresh_token: refresh_token || @api_refresh_token
50
+ },
51
+ add_access_token: false
52
+ )
53
+ res = process_response(response)
54
+ @api_access_token = res[:access_token]
55
+ return res
56
+ end
57
+
58
+ def companies(other_params: {})
59
+ response = request(
60
+ http_method: :get,
61
+ endpoint: 'companies/getAll/',
62
+ query_params: other_params
63
+ )
64
+ process_response(response)
65
+ end
66
+
67
+ # MoloniApi::Client.new.calendar(38859, '2022-01-01', '2022-07-31')
68
+ def documents_getAll(company_id, customer_id: nil, your_reference: nil, our_reference: nil, other_params: {})
69
+ response = request(
70
+ http_method: :post,
71
+ endpoint: 'documents/getAll/',
72
+ params: {
73
+ company_id: company_id,
74
+ customer_id: customer_id,
75
+ your_reference: your_reference,
76
+ our_reference: our_reference,
77
+ }.merge(other_params)
78
+ )
79
+ process_response(response)
80
+ end
81
+
82
+ def simplifiedInvoices_getAll(company_id, customer_id: nil, your_reference: nil, our_reference: nil, other_params: {})
83
+ response = request(
84
+ http_method: :post,
85
+ endpoint: 'simplifiedInvoices/getAll/',
86
+ params: {
87
+ company_id: company_id,
88
+ customer_id: customer_id,
89
+ your_reference: your_reference,
90
+ our_reference: our_reference,
91
+ }.merge(other_params)
92
+ )
93
+ process_response(response)
94
+ end
95
+
96
+ def invoiceReceipts_getAll(company_id, customer_id: nil, your_reference: nil, our_reference: nil, other_params: {})
97
+ response = request(
98
+ http_method: :post,
99
+ endpoint: 'invoiceReceipts/getAll/',
100
+ params: {
101
+ company_id: company_id,
102
+ customer_id: customer_id,
103
+ your_reference: your_reference,
104
+ our_reference: our_reference,
105
+ }.merge(other_params)
106
+ )
107
+ process_response(response)
108
+ end
109
+
110
+ # documentSets/getAll
111
+ def documentSets_getAll(company_id)
112
+ response = request(
113
+ http_method: :post,
114
+ endpoint: 'documentSets/getAll/',
115
+ params: {
116
+ company_id: company_id
117
+ }
118
+ )
119
+ process_response(response)
120
+ end
121
+
122
+ # paymentMethods/getAll/
123
+ def paymentMethods_getAll(company_id)
124
+ response = request(
125
+ http_method: :post,
126
+ endpoint: 'paymentMethods/getAll/',
127
+ params: {
128
+ company_id: company_id
129
+ }
130
+ )
131
+ process_response(response)
132
+ end
133
+
134
+ # customers/getAll/
135
+ def customers_getAll(company_id)
136
+ response = request(
137
+ http_method: :post,
138
+ endpoint: 'customers/getAll/',
139
+ params: {
140
+ company_id: company_id
141
+ }
142
+ )
143
+ process_response(response)
144
+ end
145
+
146
+ # customers/insert/
147
+ def customers_insert(company_id, customer_data: {})
148
+ # mandatory customer params:
149
+ # vat, number, name, language_id, address, city, country_id, maturity_date_id, payment_method_id
150
+ # optional params:
151
+ # email, zip_code, phone
152
+ response = request(
153
+ http_method: :post,
154
+ endpoint: 'customers/insert/',
155
+ params: { company_id: company_id }.merge(customer_data)
156
+ )
157
+ process_response(response)
158
+ end
159
+
160
+ def invoices_insert(company_id, invoice_params: {}, products: [])
161
+ # mandatory invoice params:
162
+ # date, expiration_date, document_set_id, customer_id, products
163
+ # optional params:
164
+ # our_reference, your_reference
165
+ response = request(
166
+ http_method: :post,
167
+ endpoint: 'invoices/insert/',
168
+ params: { company_id: company_id, products: products }.merge(invoice_params)
169
+ )
170
+ process_response(response)
171
+ end
172
+
173
+ def simplifiedInvoices_insert(company_id, invoice_params: {}, products: [], payments: [])
174
+ # mandatory invoice params:
175
+ # date, expiration_date, document_set_id, customer_id, products, payments
176
+ # optional params:
177
+ # our_reference, your_reference...
178
+ response = request(
179
+ http_method: :post,
180
+ endpoint: 'simplifiedInvoices/insert/',
181
+ params: {
182
+ company_id: company_id,
183
+ products: products,
184
+ payments: payments
185
+ }.merge(invoice_params)
186
+ )
187
+ process_response(response)
188
+ end
189
+
190
+ # invoiceReceipts/insert/
191
+ def invoiceReceipts_insert(company_id, invoice_params: {}, products: [], payments: [])
192
+ # mandatory invoice params:
193
+ # date, expiration_date, document_set_id, customer_id, products, payments
194
+ # optional params:
195
+ # our_reference, your_reference...
196
+ response = request(
197
+ http_method: :post,
198
+ endpoint: 'invoiceReceipts/insert/',
199
+ params: {
200
+ company_id: company_id,
201
+ products: products,
202
+ payments: payments
203
+ }.merge(invoice_params)
204
+ )
205
+ process_response(response)
206
+ end
207
+
208
+ def products_getBySearch(company_id, search_str, other_params: {})
209
+ response = request(
210
+ http_method: :post,
211
+ endpoint: 'products/getBySearch/',
212
+ params: {
213
+ company_id: company_id,
214
+ search: search_str,
215
+ }.merge(other_params)
216
+ )
217
+ process_response(response)
218
+ end
219
+
220
+ def products_getByReference(company_id, reference, other_params: {})
221
+ response = request(
222
+ http_method: :post,
223
+ endpoint: 'products/getByReference/',
224
+ params: {
225
+ company_id: company_id,
226
+ reference: reference,
227
+ }.merge(other_params)
228
+ )
229
+ process_response(response)
230
+ end
231
+
232
+ protected
233
+
234
+ def process_response(response)
235
+ result = response
236
+ case result
237
+ when Hash
238
+ result.transform_keys!(&:to_sym)
239
+ result.values.each do |r|
240
+ process_response(r)
241
+ end
242
+ when Array
243
+ result.each do |r|
244
+ process_response(r)
245
+ end
246
+ end
247
+ result
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api/config'
4
+ require_relative 'version'
5
+
6
+ module MoloniApi
7
+ # Stores the configuration
8
+ class Configuration < API::Config
9
+ property :follow_redirects, default: true
10
+
11
+ # The api endpoint used to connect to MoloniApi if none is set
12
+ # prd: https://api.moloni.pt/v1/
13
+ # sandbox: https://api.moloni.pt/sandbox/
14
+ property :endpoint, default: 'https://api.moloni.pt/sandbox/'
15
+
16
+ # The value sent in the http header for 'User-Agent' if none is set
17
+ property :user_agent, default: "MoloniApi API Ruby Gem #{MoloniApi::VERSION}"
18
+
19
+ # By default uses the Faraday connection options if none is set
20
+ property :connection_options, default: {}
21
+
22
+ # By default display 30 resources
23
+ property :per_page, default: 20
24
+
25
+ # Add Faraday::RackBuilder to overwrite middleware
26
+ property :stack
27
+ end
28
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoloniApi
4
+ # Constants
5
+ module Constants
6
+ # Response headers
7
+ RATELIMIT_REMAINING = 'X-RateLimit-Remaining'
8
+
9
+ RATELIMIT_LIMIT = 'X-RateLimit-Limit'
10
+
11
+ RATELIMIT_RESET = 'X-RateLimit-Reset'
12
+
13
+ CONTENT_TYPE = 'Content-Type'
14
+
15
+ CONTENT_LENGTH = 'content-length'
16
+
17
+ CACHE_CONTROL = 'cache-control'
18
+
19
+ ETAG = 'ETag'
20
+
21
+ SERVER = 'Server'
22
+
23
+ DATE = 'Date'
24
+
25
+ LOCATION = 'Location'
26
+
27
+ USER_AGENT = 'User-Agent'
28
+
29
+ ACCEPT = 'Accept'
30
+
31
+ ACCEPT_CHARSET = 'Accept-Charset'
32
+
33
+ OAUTH_SCOPES = 'X-OAuth-Scopes'
34
+
35
+ ACCEPTED_OAUTH_SCOPES = 'X-Accepted-Oauth-Scopes'
36
+
37
+ # Link headers
38
+ HEADER_LINK = 'Link'
39
+
40
+ HEADER_NEXT = 'X-Next'
41
+
42
+ HEADER_LAST = 'X-Last'
43
+
44
+ META_REL = 'rel'
45
+
46
+ META_LAST = 'last'
47
+
48
+ META_NEXT = 'next'
49
+
50
+ META_FIRST = 'first'
51
+
52
+ META_PREV = 'prev'
53
+
54
+ PARAM_PAGE = 'page'
55
+
56
+ PARAM_PER_PAGE = 'per_page'
57
+
58
+ PARAM_START_PAGE = 'start_page'
59
+
60
+ PARAM_INCLUDE_RELATED = 'include_related_objects'
61
+ end
62
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoloniApi
4
+ module HttpStatusCodes
5
+ HTTP_OK_CODE = 200
6
+
7
+ HTTP_BAD_REQUEST_CODE = 400
8
+ HTTP_UNAUTHORIZED_CODE = 401
9
+ HTTP_FORBIDDEN_CODE = 403
10
+ HTTP_NOT_FOUND_CODE = 404
11
+ HTTP_UNPROCESSABLE_ENTITY_CODE = 429
12
+ end
13
+ end
@@ -0,0 +1,120 @@
1
+ module MoloniApi
2
+ module Models
3
+ class Product
4
+ ##
5
+ # company_id int
6
+ # Obrigatório
7
+ #
8
+ # category_id int
9
+ # Obrigatório
10
+ #
11
+ # type int
12
+ # Obrigatório
13
+ #
14
+ # name string
15
+ # Obrigatório
16
+ #
17
+ # summary string
18
+ # Facultativo
19
+ #
20
+ # reference string
21
+ # Obrigatório
22
+ #
23
+ # ean string
24
+ # Facultativo
25
+ #
26
+ # price float
27
+ # Obrigatório
28
+ #
29
+ # unit_id int
30
+ # Obrigatório
31
+ #
32
+ # has_stock int
33
+ # Obrigatório
34
+ #
35
+ # stock float
36
+ # Obrigatório
37
+ #
38
+ # minimum_stock float
39
+ # Facultativo
40
+ #
41
+ # pos_favorite int
42
+ # Facultativo
43
+ #
44
+ # at_product_category string
45
+ # Facultativo
46
+ #
47
+ # exemption_reason string
48
+ # Facultativo
49
+ #
50
+ # taxes array
51
+ # Facultativo
52
+ #
53
+ # tax_id int
54
+ # Obrigatório
55
+ #
56
+ # value float
57
+ # Obrigatório
58
+ #
59
+ # order int
60
+ # Obrigatório
61
+ #
62
+ # cumulative int
63
+ # Obrigatório
64
+ #
65
+ # suppliers array
66
+ # Facultativo
67
+ #
68
+ # supplier_id int
69
+ # Obrigatório
70
+ #
71
+ # cost_price float
72
+ # Obrigatório
73
+ #
74
+ # referenceint
75
+ # Facultativo
76
+ #
77
+ # properties array
78
+ # Facultativo
79
+ #
80
+ # property_id int
81
+ # Obrigatório
82
+ #
83
+ # value string
84
+ # Obrigatório
85
+ #
86
+ # warehouses array
87
+ # Facultativo
88
+ #
89
+ # warehouse_id int
90
+ # Obrigatório
91
+ #
92
+ # stock float
93
+ # Obrigatório
94
+ def initialize(raw_result)
95
+ @raw_result = raw_result
96
+
97
+ @company_id = raw_result[:company_id]
98
+ @product_id = raw_result[:product_id]
99
+ @category = raw_result[:category]
100
+ @type = raw_result[:type]
101
+ @name = raw_result[:name]
102
+ @summary = raw_result[:summary]
103
+ @reference = raw_result[:reference]
104
+ @ean = raw_result[:ean]
105
+ @price = raw_result[:price]
106
+ @unit_id = raw_result[:unit_id]
107
+ @has_stock = raw_result[:has_stock]
108
+ @stock = raw_result[:stock]
109
+ @minimum_stock = raw_result[:minimum_stock]
110
+ @pos_favorite = raw_result[:pos_favorite]
111
+ @at_product_category = raw_result[:at_product_category]
112
+ @exemption_reason = raw_result[:exemption_reason]
113
+ @taxes = raw_result[:taxes] # array
114
+ @suppliers = raw_result[:suppliers] # array
115
+ @properties = raw_result[:properties] # array
116
+ @warehouses = raw_result[:warehouses] # array
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoloniApi
4
+ VERSION = '0.1.0'
5
+ end