fakturoid 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 076c161dacf951eb18acf2852ef982e0105ca544
4
+ data.tar.gz: dadf19899fa9ee0eda8bb8e96ca8e225ac166194
5
+ SHA512:
6
+ metadata.gz: 8b46b7684942b394c3bd00dcd256e5a66ff388737371be5f61441b73b10920fe1cb0197892de2ee79d2433a47a1e33c5974bdc4fbc70383209d003ff98aa5359
7
+ data.tar.gz: 68ebfce58da609f156a85657f8d05057ca7888f0ae703e654f2514e4fb4698dd7b454256faa80fa38bf534a1e1566a8f75daf2b585a2f20c3e20117b4ad47942
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .bundle/
2
+ Gemfile.lock
3
+ coverage/
4
+ doc/
5
+ pkg/
6
+ .DS_Store
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ ## 0.1.0 (Initial version)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fakturoid.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Eda Riedl
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,262 @@
1
+ # Fakturoid
2
+
3
+ The Fakturoid gem is ruby library for API communication with web based invoicing service [www.fakturoid.cz](https://fakturoid.cz).
4
+ Fakturoid [API documentation](http://docs.fakturoid.apiary.io).
5
+
6
+ [![Circle CI](https://circleci.com/gh/fakturoid/fakturoid-ruby.svg?style=svg)](https://circleci.com/gh/fakturoid/fakturoid-ruby)
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'fakturoid', git: 'https://github.com/fakturoid/fakturoid-ruby.git'
14
+ ```
15
+
16
+ And then run:
17
+
18
+ $ bundle
19
+
20
+ Gem is not officially released and is under construction. So if you want to use it please install it from this repository
21
+ and specify `:ref` option. API of the Fakturoid gem can be still changed.
22
+
23
+ ## Configuration
24
+
25
+ Fakturoid gem is configured within config block placed in `config/initializers/fakturoid.rb`:
26
+
27
+ ```ruby
28
+ Fakturoid.configure do |config|
29
+ config.email = 'yourfakturoid@email.com'
30
+ config.api_key = 'fasdff823fdasWRFKW843ladfjklasdf834'
31
+ config.account = 'applecorp' # former subdomain (first part of URL)
32
+ config.user_agent = 'Name of your app (your@email.com)'
33
+ end
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ### Account resource
39
+
40
+ To get informations about your account in Fakturoid run following code:
41
+
42
+ ```ruby
43
+ response = Fakturoid::Client::Account.current
44
+ response.status_code # returns response http code
45
+ response.body # contains hash with returned body
46
+ ```
47
+
48
+ Accessing content of returned body:
49
+
50
+ ```ruby
51
+ response.body['name'] # return name of your company
52
+ response.name # alternative way of getting the name of your company
53
+ ```
54
+
55
+ For the list of all returned account fields see the [Account API documentation](http://docs.fakturoid.apiary.io/#account)
56
+
57
+ ### User resource
58
+
59
+ For the information about current user use following code:
60
+
61
+ ```ruby
62
+ response = Fakturoid::Client::User.current
63
+ ```
64
+
65
+ For all the users which belongs to current account:
66
+
67
+ ```ruby
68
+ response = Fakturoid::Client::User.all
69
+ ```
70
+
71
+ If you want to get information about one user which belongs to account use:
72
+
73
+ ```ruby
74
+ response = Fakturoid::Client::User.find(user_id)
75
+ ```
76
+
77
+ For the list of all returned user fields see the [Users API documentation](http://docs.fakturoid.apiary.io/#users)
78
+
79
+ ### Subject resource
80
+
81
+ To get all subjects run (Subjects are paginated by 20 per page):
82
+
83
+ ```ruby
84
+ response = Fakturoid::Client::Subject.all page: 2
85
+ ```
86
+
87
+ Fulltext search subjects:
88
+
89
+ ```ruby
90
+ response = Fakturoid::Client::Subject.search 'Client name'
91
+ ```
92
+
93
+ To find one subject use:
94
+
95
+ ```ruby
96
+ response = Fakturoid::Client::Subject.find subject_id
97
+ ```
98
+
99
+ You can create new subject with:
100
+
101
+ ```ruby
102
+ response = Fakturoid::Client::Subject.create name: 'New client'
103
+ ```
104
+
105
+ To update subject use following code:
106
+
107
+ ```ruby
108
+ response = Fakturoid::Client::Subject.update subject_id, name: 'Updated client'
109
+ ```
110
+
111
+ Delete subject:
112
+
113
+ ```ruby
114
+ Fakturoid::Client::Subject.delete subject_id
115
+ ```
116
+
117
+ For the list of all subject fields and options see the [Subjects API documentation](http://docs.fakturoid.apiary.io/#subjects)
118
+
119
+ ### Invoice resource
120
+
121
+ To get all invoices run (Invoices are paginated by 20 per page):
122
+
123
+ ```ruby
124
+ response = Fakturoid::Client::Invoice.all page: 2
125
+ ```
126
+
127
+ Fulltext search invoices:
128
+
129
+ ```ruby
130
+ response = Fakturoid::Client::Invoice.search 'Client name'
131
+ ```
132
+
133
+ To find one invoice use:
134
+
135
+ ```ruby
136
+ response = Fakturoid::Client::Invoice.find invoice_id
137
+ ```
138
+
139
+ To download invoice in PDF format you can use following code:
140
+
141
+ ```ruby
142
+ response = Fakturoid::Client::Invoice.download_pdf invoice_id
143
+
144
+ File.open '/path/to/file.pdf', 'w' do |f|
145
+ f.write response.body
146
+ end
147
+ ```
148
+
149
+ You can create new invoice with:
150
+
151
+ ```ruby
152
+ invoice = {
153
+ subject_id: 123,
154
+ lines: [
155
+ {
156
+ quantity: 5,
157
+ unit_name: 'kg',
158
+ name: 'Sand',
159
+ unit_price: '100',
160
+ vat_rate: 21
161
+ }
162
+ ]
163
+ }
164
+ response = Fakturoid::Client::Invoice.create invoice
165
+ ```
166
+
167
+ Invoice actions (eg. pay invoice):
168
+
169
+ ```ruby
170
+ response = Fakturoid::Client::Invoice.fire invoice_id, 'pay'
171
+ ```
172
+
173
+ Send invoice with customized message (for more information see [the API Documentation](http://docs.fakturoid.apiary.io/#messages)):
174
+
175
+ ```ruby
176
+ message = {
177
+ email: 'testemail@testemail.cz',
178
+ email_copy: 'some@emailcopy.cz',
179
+ subject: 'I have an invoice for you',
180
+ message: "Hi,\n\nyou can find invoice no. #no# on the following page #link#\n\nHave a nice day"
181
+ }
182
+
183
+ response = Fakturoid::Client::Invoice.deliver_message(181, message)
184
+ response.status_code # => 201
185
+ ```
186
+
187
+ To update invoice use following code:
188
+
189
+ ```ruby
190
+ response = Fakturoid::Client::Invoice.update invoice_id, number: '2015-0015'
191
+ ```
192
+
193
+ Delete invoice:
194
+
195
+ ```ruby
196
+ response = Fakturoid::Client::Invoice.delete invoice_id
197
+ ```
198
+
199
+ For the list of all invoice fields and options see the [Invoices API documentation](http://docs.fakturoid.apiary.io/#invoices)
200
+
201
+ ## Handling error responses
202
+
203
+ The Fakturoid gem raises exceptions if error response is returned from the servers. All exceptions contains following attributes:
204
+
205
+ - `message` - Error description
206
+ - `response_code` - http code of error (only number)
207
+ - `response_body` - response body parsed in the hash
208
+
209
+ <table>
210
+ <thead>
211
+ <tr>
212
+ <th>Error class</th><th>Response code</th><th>Description</th>
213
+ </tr>
214
+ </thead>
215
+ <tbody>
216
+ <tr>
217
+ <td>ContentTypeError</td><td>415 Unsupported Media Type</td><td>Wrong content type</td>
218
+ </tr>
219
+ <tr>
220
+ <td>UserAgentError</td><td>400 Bad Request</td><td>Missing `user_agent` configuration</td>
221
+ </tr>
222
+ <tr>
223
+ <td>PaginationError</td><td>400 Bad Request</td><td>Page with given number does not exist</td>
224
+ </tr>
225
+ <tr>
226
+ <td>AuthenticationError</td><td>401 Unauthorized</td><td>Wrong authentication `email` or `api_key` configuration</td>
227
+ </tr>
228
+ <tr>
229
+ <td>BlockedAccountError</td><td>402 Payment Required</td><td>Fakturoid account is blocked</td>
230
+ </tr>
231
+ <tr>
232
+ <td>RateLimitError</td><td>429 Too Many Requests</td><td>Too many request sent during last 5 minutes</td>
233
+ </tr>
234
+ <tr>
235
+ <td>ReadOnlySiteError</td><td>503 Service Unavailable</td><td>Fakturoid is read only</td>
236
+ </tr>
237
+ <tr>
238
+ <td>RecordNotFoundError</td><td>404 Not Found</td><td>Document with given ID does not exists or current account has read only permission and trying to edit something</td>
239
+ </tr>
240
+ <tr>
241
+ <td>InvalidRecordError</td><td>422 Unprocessable Entity</td><td>Invalid data sent to server</td>
242
+ </tr>
243
+ <tr>
244
+ <td>DestroySubjectError</td><td>403 Forbidden</td><td>Subject has invoices or expenses and cannot be deleted</td>
245
+ </tr>
246
+ <tr>
247
+ <td>SubjectLimitError</td><td>403 Forbidden</td><td>Subject quota reached for adding more subjects upgrade to higher plan</td>
248
+ </tr>
249
+ <tr>
250
+ <td>GeneratorLimitError</td><td>403 Forbidden</td><td>Generator quota reached for adding more recurring generators upgrade to higher plan</td>
251
+ </tr>
252
+ <tr>
253
+ <td>UnsupportedFeatureError</td><td>403 Forbidden</td><td>Feature is not supported in your plan to use this feature upgrade to higher plan</td>
254
+ </tr>
255
+ <tr>
256
+ <td>ClientError</td><td>4XX</td><td>Server returns response code which is not specified above</td>
257
+ </tr>
258
+ <tr>
259
+ <td>ServerError</td><td>5XX</td><td>Server returns response code which is not specified above</td>
260
+ </tr>
261
+ </tbody>
262
+ </table>
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task default: :test
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['test/*_test.rb']
9
+ t.verbose = true
10
+ end
data/fakturoid.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fakturoid/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "fakturoid"
8
+ s.version = Fakturoid::VERSION
9
+ s.authors = ["Eda Riedl", "Lukáš Konarovský"]
10
+ s.email = ["podpora@fakturoid.cz"]
11
+ s.summary = %q{Ruby client for web based invoicing service www.fakturoid.cz}
12
+ s.description = %q{Ruby client for web based invoicing service www.fakturoid.cz}
13
+ s.homepage = "https://github.com/fakturoid/fakturoid-ruby"
14
+ s.license = "MIT"
15
+
16
+ s.files = `git ls-files -z`.split("\x0")
17
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'multi_json'
22
+ s.add_dependency 'faraday'
23
+
24
+ s.add_development_dependency "bundler", "> 1"
25
+ s.add_development_dependency "rake"
26
+ s.add_development_dependency "minitest"
27
+ s.add_development_dependency "shoulda-context"
28
+ s.add_development_dependency "mocha"
29
+ end
data/lib/fakturoid.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'uri'
2
+ require 'multi_json'
3
+ require 'faraday'
4
+
5
+ require 'fakturoid/config'
6
+ require 'fakturoid/connection'
7
+ require 'fakturoid/request'
8
+ require 'fakturoid/response'
9
+ require 'fakturoid/api'
10
+ require 'fakturoid/client'
11
+ require 'fakturoid/version'
12
+ require 'fakturoid/railtie' if defined?(::Rails)
13
+
14
+ module Fakturoid
15
+
16
+ class ApiError < StandardError
17
+ attr_accessor :response_code, :response_body
18
+
19
+ def initialize(message = nil, response_code = nil, response_body = nil)
20
+ super(message)
21
+ self.response_code = response_code
22
+ self.response_body = response_body
23
+ end
24
+ end
25
+
26
+ class ContentTypeError < ApiError; end
27
+ class UserAgentError < ApiError; end
28
+ class AuthenticationError < ApiError; end
29
+ class BlockedAccountError < ApiError; end
30
+ class RateLimitError < ApiError; end
31
+ class ReadOnlySiteError < ApiError; end
32
+ class PaginationError < ApiError; end
33
+
34
+ class RecordNotFoundError < ApiError; end
35
+ class InvalidRecordError < ApiError; end
36
+ class DestroySubjectError < ApiError; end
37
+ class SubjectLimitError < ApiError; end
38
+ class GeneratorLimitError < ApiError; end
39
+ class UnsupportedFeatureError < ApiError; end
40
+
41
+ class ClientError < ApiError; end
42
+ class ServerError < ApiError; end
43
+
44
+ def self.configure(&block)
45
+ Fakturoid::Api.configure(&block)
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ require 'fakturoid/api/arguments'
2
+ require 'fakturoid/api/http_methods'
3
+
4
+ module Fakturoid
5
+ class Api
6
+ extend Arguments
7
+ extend HttpMethods
8
+
9
+ def self.configure(&block)
10
+ @config ||= Fakturoid::Config.new(&block)
11
+ end
12
+
13
+ def self.config
14
+ @config
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ module Fakturoid
2
+ class Api
3
+ module Arguments
4
+ def permit_params(params_hash, *permitted_params)
5
+ params_hash.select { |param, value| permitted_params.include?(param.to_sym) }
6
+ end
7
+
8
+ def validate_numerical_id(id)
9
+ raise ArgumentError, "Wrong ID given: #{id}" if !id.is_a?(Integer) && !(id.is_a?(String) && id =~ /\A\d+\z/)
10
+ true
11
+ end
12
+
13
+ def validate_search_query(query)
14
+ raise ArgumentError, 'Query parameter is required' if query.nil? || query.empty?
15
+ true
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module Fakturoid
2
+ class Api
3
+ module HttpMethods
4
+ def get_request(path, params = {})
5
+ Request.new(:get, path, self).call(params)
6
+ end
7
+
8
+ def post_request(path, params = {})
9
+ Request.new(:post, path, self).call(params)
10
+ end
11
+
12
+ def patch_request(path, params = {})
13
+ Request.new(:patch, path, self).call(params)
14
+ end
15
+
16
+ def delete_request(path, params = {})
17
+ Request.new(:delete, path, self).call(params)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ require 'fakturoid/client/account'
2
+ require 'fakturoid/client/bank_account'
3
+ require 'fakturoid/client/user'
4
+ require 'fakturoid/client/subject'
5
+ require 'fakturoid/client/invoice'
6
+ require 'fakturoid/client/expense'
7
+ require 'fakturoid/client/generator'
8
+ require 'fakturoid/client/event'
9
+ require 'fakturoid/client/todo'
@@ -0,0 +1,9 @@
1
+ module Fakturoid
2
+ module Client
3
+ class Account < Fakturoid::Api
4
+ def self.current
5
+ get_request('account.json')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Fakturoid
2
+ module Client
3
+ class BankAccount < Fakturoid::Api
4
+ def self.all
5
+ get_request('bank_accounts.json')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module Fakturoid
2
+ module Client
3
+ class Event < Fakturoid::Api
4
+ def self.all(params = {})
5
+ request_params = permit_params(params, :page, :since, :subject_id) || {}
6
+
7
+ get_request('events.json', request_params: request_params)
8
+ end
9
+
10
+ def self.paid(params = {})
11
+ request_params = permit_params(params, :page, :since, :subject_id) || {}
12
+
13
+ get_request('events/paid.json', request_params: request_params)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ module Fakturoid
2
+ module Client
3
+ class Expense < Fakturoid::Api
4
+ def self.all(params = {})
5
+ request_params = permit_params(params, :page, :since, :number, :variable_symbol, :status, :subject_id) || {}
6
+
7
+ get_request('expenses.json', request_params: request_params)
8
+ end
9
+
10
+ def self.find(id)
11
+ validate_numerical_id(id)
12
+ get_request("expenses/#{id}.json")
13
+ end
14
+
15
+ def self.search(query)
16
+ validate_search_query(query)
17
+ get_request('expenses/search.json', request_params: { query: query })
18
+ end
19
+
20
+ def self.fire(id, event)
21
+ validate_numerical_id(id)
22
+ post_request("expenses/#{id}/fire.json", request_params: { event: event })
23
+ end
24
+
25
+ def self.create(payload = {})
26
+ post_request('expenses.json', payload: payload)
27
+ end
28
+
29
+ def self.update(id, payload = {})
30
+ validate_numerical_id(id)
31
+ patch_request("expenses/#{id}.json", payload: payload)
32
+ end
33
+
34
+ def self.delete(id)
35
+ validate_numerical_id(id)
36
+ delete_request("expenses/#{id}.json")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ module Fakturoid
2
+ module Client
3
+ class Generator < Fakturoid::Api
4
+ def self.all(params = {})
5
+ request_params = permit_params(params, :page, :since, :subject_id) || {}
6
+
7
+ get_request('generators.json', request_params: request_params)
8
+ end
9
+
10
+ def self.recurring(params = {})
11
+ request_params = permit_params(params, :page, :since, :subject_id) || {}
12
+
13
+ get_request('generators/recurring.json', request_params: request_params)
14
+ end
15
+
16
+ def self.template(params = {})
17
+ request_params = permit_params(params, :page, :since, :subject_id) || {}
18
+
19
+ get_request('generators/template.json', request_params: request_params)
20
+ end
21
+
22
+ def self.find(id)
23
+ validate_numerical_id(id)
24
+ get_request("generators/#{id}.json")
25
+ end
26
+
27
+ def self.create(payload = {})
28
+ post_request('generators.json', payload: payload)
29
+ end
30
+
31
+ def self.update(id, payload = {})
32
+ validate_numerical_id(id)
33
+ patch_request("generators/#{id}.json", payload: payload)
34
+ end
35
+
36
+ def self.delete(id)
37
+ validate_numerical_id(id)
38
+ delete_request("generators/#{id}.json")
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,62 @@
1
+ module Fakturoid
2
+ module Client
3
+ class Invoice < Fakturoid::Api
4
+ def self.all(params = {})
5
+ request_params = permit_params(params, :page, :since, :number, :status, :subject_id) || {}
6
+
7
+ get_request('invoices.json', request_params: request_params)
8
+ end
9
+
10
+ def self.regular(params = {})
11
+ request_params = permit_params(params, :page, :since, :number, :status, :subject_id) || {}
12
+
13
+ get_request('invoices/regular.json', request_params: request_params)
14
+ end
15
+
16
+ def self.proforma(params = {})
17
+ request_params = permit_params(params, :page, :since, :number, :status, :subject_id) || {}
18
+
19
+ get_request('invoices/proforma.json', request_params: request_params)
20
+ end
21
+
22
+ def self.find(id)
23
+ validate_numerical_id(id)
24
+ get_request("invoices/#{id}.json")
25
+ end
26
+
27
+ def self.search(query)
28
+ validate_search_query(query)
29
+ get_request('invoices/search.json', request_params: { query: query })
30
+ end
31
+
32
+ def self.download_pdf(id)
33
+ validate_numerical_id(id)
34
+ get_request("invoices/#{id}/download.pdf", headers: { content_type: 'application/pdf' })
35
+ end
36
+
37
+ def self.fire(id, event)
38
+ validate_numerical_id(id)
39
+ post_request("invoices/#{id}/fire.json", request_params: { event: event })
40
+ end
41
+
42
+ def self.deliver_message(invoice_id, payload = {})
43
+ validate_numerical_id(invoice_id)
44
+ post_request("invoices/#{invoice_id}/message.json", payload: payload)
45
+ end
46
+
47
+ def self.create(payload = {})
48
+ post_request('invoices.json', payload: payload)
49
+ end
50
+
51
+ def self.update(id, payload = {})
52
+ validate_numerical_id(id)
53
+ patch_request("invoices/#{id}.json", payload: payload)
54
+ end
55
+
56
+ def self.delete(id)
57
+ validate_numerical_id(id)
58
+ delete_request("invoices/#{id}.json")
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,35 @@
1
+ module Fakturoid
2
+ module Client
3
+ class Subject < Fakturoid::Api
4
+ def self.all(params = {})
5
+ request_params = permit_params(params, :page, :since, :custom_id) || {}
6
+
7
+ get_request('subjects.json', request_params: request_params)
8
+ end
9
+
10
+ def self.find(id)
11
+ validate_numerical_id(id)
12
+ get_request("subjects/#{id}.json")
13
+ end
14
+
15
+ def self.search(query)
16
+ validate_search_query(query)
17
+ get_request('subjects/search.json', request_params: { query: query })
18
+ end
19
+
20
+ def self.create(payload = {})
21
+ post_request('subjects.json', payload: payload)
22
+ end
23
+
24
+ def self.update(id, payload = {})
25
+ validate_numerical_id(id)
26
+ patch_request("subjects/#{id}.json", payload: payload)
27
+ end
28
+
29
+ def self.delete(id)
30
+ validate_numerical_id(id)
31
+ delete_request("subjects/#{id}.json")
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ module Fakturoid
2
+ module Client
3
+ class Todo < Fakturoid::Api
4
+ def self.all(params = {})
5
+ request_params = permit_params(params, :page, :since) || {}
6
+
7
+ get_request('todos.json', request_params: request_params)
8
+ end
9
+
10
+ def self.toggle_completion(id)
11
+ validate_numerical_id(id)
12
+ post_request("todos/#{id}/toggle_completion.json")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Fakturoid
2
+ module Client
3
+ class User < Api
4
+ def self.current
5
+ get_request('user.json', url: Fakturoid::Api.config.endpoint_without_account)
6
+ end
7
+
8
+ def self.find(id)
9
+ validate_numerical_id(id)
10
+ get_request("users/#{id}.json")
11
+ end
12
+
13
+ def self.all
14
+ get_request('users.json')
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ module Fakturoid
2
+ class Config
3
+ attr_accessor :email, :api_key, :account
4
+ attr_writer :user_agent
5
+
6
+ ENDPOINT = 'https://app.fakturoid.cz/api/v2'
7
+
8
+ def initialize(&block)
9
+ yield self
10
+ end
11
+
12
+ def user_agent
13
+ if @user_agent.nil? || @user_agent.empty?
14
+ "Fakturoid ruby gem (#{email})"
15
+ else
16
+ @user_agent
17
+ end
18
+ end
19
+
20
+ def endpoint
21
+ "#{ENDPOINT}/accounts/#{account}"
22
+ end
23
+
24
+ def endpoint_without_account
25
+ ENDPOINT
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ module Fakturoid
2
+ module Connection
3
+
4
+ def default_options(options = {})
5
+ content_type = options[:headers] && options[:headers][:content_type]
6
+ {
7
+ headers: {
8
+ content_type: content_type || 'application/json',
9
+ user_agent: Fakturoid::Api.config.user_agent
10
+ },
11
+ url: options[:url] || Fakturoid::Api.config.endpoint
12
+ }
13
+ end
14
+
15
+ def connection(options = {})
16
+ @connection = Faraday.new default_options(options)
17
+ @connection.basic_auth(Fakturoid::Api.config.email, Fakturoid::Api.config.api_key)
18
+
19
+ @connection
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ module Fakturoid
2
+ class Railtie < Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,28 @@
1
+ module Fakturoid
2
+ class Request
3
+ include Connection
4
+
5
+ attr_reader :method, :path, :caller
6
+ HTTP_METHODS = [:get, :post, :patch, :delete]
7
+
8
+ def initialize(method, path, caller)
9
+ @method = method
10
+ @path = path
11
+ @caller = caller
12
+ end
13
+
14
+ def call(params = {})
15
+ unless HTTP_METHODS.include?(method.to_sym)
16
+ raise ArgumentError, "Unknown http method: #{method}"
17
+ end
18
+ request_params = params[:request_params] || {}
19
+
20
+ http_connection = connection(params)
21
+ response = http_connection.send(method) do |req|
22
+ req.url path, request_params
23
+ req.body = MultiJson.dump(params[:payload]) if params.key?(:payload)
24
+ end
25
+ Response.new(response, caller, method)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,70 @@
1
+ module Fakturoid
2
+ class Response
3
+ attr_reader :response, :caller, :env, :body, :request_method
4
+
5
+ def initialize(faraday_response, caller, request_method)
6
+ @response = faraday_response
7
+ @caller = caller
8
+ @env = faraday_response.env
9
+ @request_method = request_method.to_sym
10
+
11
+ if !(env.body.nil? || env.body.empty? || (json? && env.body =~ /\A\s+\z/))
12
+ @body = json? ? MultiJson.load(env.body) : env.body
13
+ end
14
+ handle_response
15
+ end
16
+
17
+ def method_missing(method, *args, &block)
18
+ if body && body.is_a?(Hash) && body.key?(method.to_s)
19
+ body[method.to_s]
20
+ else
21
+ super
22
+ end
23
+ end
24
+
25
+ def status_code
26
+ env['status']
27
+ end
28
+
29
+ def json?
30
+ env.request_headers['Content-Type'] == 'application/json'
31
+ end
32
+
33
+ def headers
34
+ env.response_headers
35
+ end
36
+
37
+ def inspect
38
+ "#<#{self.class.name}:#{object_id} @body=\"#{self.body}\" @status_code=\"#{status_code}\">"
39
+ end
40
+
41
+ private
42
+
43
+ def handle_response
44
+ case status_code
45
+ when 400
46
+ raise error(UserAgentError, 'User-Agent header missing') if env.request_headers['User-Agent'].nil? || env.request_headers['User-Agent'].empty?
47
+ raise error(PaginationError, 'Page does not exist')
48
+ when 401 then raise error(AuthenticationError, 'Authentification failed')
49
+ when 402 then raise error(BlockedAccountError, 'Account is blocked')
50
+ when 403 then
51
+ raise error(DestroySubjectError, 'Cannot destroy subject with invoices') if caller == Client::Subject && request_method == :delete
52
+ raise error(SubjectLimitError, 'Subject limit for account reached') if caller == Client::Subject && request_method == :post
53
+ raise error(GeneratorLimitError, 'Recurring generator limit for account reached') if caller == Client::Generator
54
+ raise error(UnsupportedFeatureError, 'Feature unavailable for account plan')
55
+ when 404 then raise error(RecordNotFoundError, 'Record not found')
56
+ when 415 then raise error(ContentTypeError, 'Unsupported Content-Type')
57
+ when 422 then raise error(InvalidRecordError, 'Invalid record')
58
+ when 429 then raise error(RateLimitError, 'Rate limit reached')
59
+ when 503 then raise error(ReadOnlySiteError, 'Fakturoid is in read only state')
60
+ else
61
+ raise error(ServerError, 'Server error') if status_code >= 500
62
+ raise error(ClientError, 'Client error') if status_code >= 400
63
+ end
64
+ end
65
+
66
+ def error(klass, message = nil)
67
+ klass.new message, status_code, body
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ module Fakturoid
2
+ VERSION = '0.1.0'
3
+ end
data/test/api_test.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class Fakturoid::ApiTest < Fakturoid::TestCase
4
+ should "permit only required arguments" do
5
+ hash = { page: 4, number: '2015-0015', account: 15 }
6
+ permitted_params = Fakturoid::Api.permit_params(hash, :page, :number, :status)
7
+ assert_equal({ page: 4, number: '2015-0015' }, permitted_params)
8
+ end
9
+
10
+ should "raise argument error if id is in wrong format" do
11
+ assert_raises(ArgumentError) { Fakturoid::Api.validate_numerical_id(nil) }
12
+ assert_raises(ArgumentError) { Fakturoid::Api.validate_numerical_id("nil") }
13
+ assert Fakturoid::Api.validate_numerical_id(15)
14
+ assert Fakturoid::Api.validate_numerical_id("15")
15
+ end
16
+
17
+ should "raise argument error if search query is not given" do
18
+ assert_raises(ArgumentError) { Fakturoid::Api.validate_search_query(nil) }
19
+ assert_raises(ArgumentError) { Fakturoid::Api.validate_search_query("") }
20
+ assert Fakturoid::Api.validate_search_query("Company name")
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ require 'test_helper'
2
+
3
+ class Fakturoid::ConfigTest < Fakturoid::TestCase
4
+ should "configure with block param" do
5
+ config = Fakturoid::Config.new do |config|
6
+ config.email = 'test@email.cz'
7
+ config.api_key = 'XXXXXXXXXXX'
8
+ config.account = 'testaccount'
9
+ config.user_agent = 'My test app (test@email.cz)'
10
+ end
11
+
12
+ assert_equal 'test@email.cz', config.email
13
+ assert_equal 'XXXXXXXXXXX', config.api_key
14
+ assert_equal 'testaccount', config.account
15
+ assert_equal 'My test app (test@email.cz)', config.user_agent
16
+ end
17
+
18
+ should "use default user agent" do
19
+ config = Fakturoid::Config.new do |config|
20
+ config.email = 'test@email.cz'
21
+ config.api_key = 'XXXXXXXXXXX'
22
+ config.account = 'testaccount'
23
+ end
24
+
25
+ assert_equal 'Fakturoid ruby gem (test@email.cz)', config.user_agent
26
+ end
27
+
28
+ should "return correct endpoints" do
29
+ config = Fakturoid::Config.new do |config|
30
+ config.email = 'test@email.cz'
31
+ config.api_key = 'XXXXXXXXXXX'
32
+ config.account = 'testaccount'
33
+ end
34
+
35
+ assert_equal 'https://app.fakturoid.cz/api/v2/accounts/testaccount', config.endpoint
36
+ assert_equal 'https://app.fakturoid.cz/api/v2', config.endpoint_without_account
37
+ end
38
+ end
@@ -0,0 +1,81 @@
1
+ {
2
+ "id": 5,
3
+ "proforma": false,
4
+ "number": "2012-0004",
5
+ "variable_symbol": "20120004",
6
+ "your_name": "Alexandr Hejsek",
7
+ "your_street": "Hopsinkov\u00e1 14",
8
+ "your_street2": null,
9
+ "your_city": "Praha",
10
+ "your_zip": "10000",
11
+ "your_country": "CZ",
12
+ "your_registration_no": "87654321",
13
+ "your_vat_no": "CZ87654321",
14
+ "client_name": "Microsoft a. s.",
15
+ "client_street": "Trojanova 1216/46",
16
+ "client_street2": null,
17
+ "client_city": "Praha",
18
+ "client_zip": "11000",
19
+ "client_country": "CZ",
20
+ "client_registration_no": "28444501",
21
+ "client_vat_no": "CZ28444501",
22
+ "subject_id": 4,
23
+ "generator_id": 4,
24
+ "related_id": null,
25
+ "correction": false,
26
+ "correction_id": null,
27
+ "token": "udDTG8Q88M",
28
+ "status": "paid",
29
+ "order_number": null,
30
+ "issued_on": "2011-10-13",
31
+ "taxable_fulfillment_due": "2011-10-13",
32
+ "due": 10,
33
+ "due_on": "2011-10-23",
34
+ "sent_at": null,
35
+ "paid_at": "2011-10-20T12:11:37+02:00",
36
+ "reminder_sent_at": null,
37
+ "accepted_at": null,
38
+ "cancelled_at": null,
39
+ "note": null,
40
+ "footer_note": null,
41
+ "bank_account": "1234/1234",
42
+ "iban": null,
43
+ "swift_bic": null,
44
+ "payment_method": "bank",
45
+ "currency": "CZK",
46
+ "exchange_rate": "1.0",
47
+ "language": "cz",
48
+ "transferred_tax_liability": false,
49
+ "vat_price_mode": "without_vat",
50
+ "subtotal": "40000.0",
51
+ "total": "48400.0",
52
+ "native_subtotal": "40000.0",
53
+ "native_total": "48400.0",
54
+ "lines": [
55
+ {
56
+ "id": 1234,
57
+ "name": "PC",
58
+ "quantity": "1.0",
59
+ "unit_name": "",
60
+ "unit_price": "20000.0",
61
+ "vat_rate": 21,
62
+ "unit_price_without_vat": "20000.0",
63
+ "unit_price_with_vat": "24200.0"
64
+ },
65
+ {
66
+ "id": 1235,
67
+ "name": "Notebook",
68
+ "quantity": "1.0",
69
+ "unit_name": "",
70
+ "unit_price": "20000.0",
71
+ "vat_rate": 21,
72
+ "unit_price_without_vat": "20000.0",
73
+ "unit_price_with_vat": "24200.0"
74
+ }
75
+ ],
76
+ "html_url": "https://app.fakturoid.cz/applecorp/invoices/9",
77
+ "public_html_url": "https://app.fakturoid.cz/applecorp/p/udDTG8Q88M/2012-0004",
78
+ "url": "https://app.fakturoid.cz/api/v2/accounts/applecorp/invoices/9.json",
79
+ "subject_url": "https://app.fakturoid.cz/api/v2/accounts/applecorp/subjects/4.json",
80
+ "updated_at": "2012-05-13T12:11:37+02:00"
81
+ }
Binary file
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ class Fakturoid::RequestTest < Fakturoid::TestCase
4
+ should "should return pdf" do
5
+ pdf = load_fixture('invoice.pdf')
6
+ test_connection = Faraday.new do |builder|
7
+ builder.adapter :test do |stub|
8
+ stub.get('invoices/5/download.pdf') { |env| [ 200, {content_type: 'application/pdf'}, pdf ]}
9
+ end
10
+ builder.headers = { content_type: 'applicatoin/pdf' }
11
+ end
12
+ Fakturoid::Request.any_instance.stubs(:connection).returns(test_connection)
13
+
14
+ response = Fakturoid::Request.new(:get, 'invoices/5/download.pdf', Fakturoid::Client::Invoice).call
15
+ assert !response.json?
16
+ assert_raises(NoMethodError) { response.name }
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ require 'minitest/autorun'
2
+ require 'mocha/setup'
3
+ require 'shoulda-context'
4
+ require 'pathname'
5
+ require 'fakturoid'
6
+
7
+ module Fakturoid
8
+ class TestCase < Minitest::Test
9
+ def test_path
10
+ Pathname.new(File.dirname(__FILE__))
11
+ end
12
+
13
+ def load_fixture(file_name)
14
+ File.read(test_path.join('fixtures', file_name))
15
+ end
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fakturoid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eda Riedl
8
+ - Lukáš Konarovský
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-02-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: multi_json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: faraday
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">"
47
+ - !ruby/object:Gem::Version
48
+ version: '1'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">"
54
+ - !ruby/object:Gem::Version
55
+ version: '1'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: minitest
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: shoulda-context
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: mocha
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ description: Ruby client for web based invoicing service www.fakturoid.cz
113
+ email:
114
+ - podpora@fakturoid.cz
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".ruby-version"
121
+ - CHANGELOG.md
122
+ - Gemfile
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - fakturoid.gemspec
127
+ - lib/fakturoid.rb
128
+ - lib/fakturoid/api.rb
129
+ - lib/fakturoid/api/arguments.rb
130
+ - lib/fakturoid/api/http_methods.rb
131
+ - lib/fakturoid/client.rb
132
+ - lib/fakturoid/client/account.rb
133
+ - lib/fakturoid/client/bank_account.rb
134
+ - lib/fakturoid/client/event.rb
135
+ - lib/fakturoid/client/expense.rb
136
+ - lib/fakturoid/client/generator.rb
137
+ - lib/fakturoid/client/invoice.rb
138
+ - lib/fakturoid/client/subject.rb
139
+ - lib/fakturoid/client/todo.rb
140
+ - lib/fakturoid/client/user.rb
141
+ - lib/fakturoid/config.rb
142
+ - lib/fakturoid/connection.rb
143
+ - lib/fakturoid/railtie.rb
144
+ - lib/fakturoid/request.rb
145
+ - lib/fakturoid/response.rb
146
+ - lib/fakturoid/version.rb
147
+ - test/api_test.rb
148
+ - test/config_test.rb
149
+ - test/fixtures/invoice.json
150
+ - test/fixtures/invoice.pdf
151
+ - test/request_test.rb
152
+ - test/test_helper.rb
153
+ homepage: https://github.com/fakturoid/fakturoid-ruby
154
+ licenses:
155
+ - MIT
156
+ metadata: {}
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ requirements: []
172
+ rubyforge_project:
173
+ rubygems_version: 2.4.5
174
+ signing_key:
175
+ specification_version: 4
176
+ summary: Ruby client for web based invoicing service www.fakturoid.cz
177
+ test_files:
178
+ - test/api_test.rb
179
+ - test/config_test.rb
180
+ - test/fixtures/invoice.json
181
+ - test/fixtures/invoice.pdf
182
+ - test/request_test.rb
183
+ - test/test_helper.rb