moloni_api 0.1.0

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