eol-client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.drone.yaml +12 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +60 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +11 -0
  8. data/Changelog.md +3 -0
  9. data/Gemfile +6 -0
  10. data/Guardfile +13 -0
  11. data/LICENSE +21 -0
  12. data/README.md +247 -0
  13. data/Rakefile +13 -0
  14. data/bin/console +16 -0
  15. data/bin/setup +7 -0
  16. data/eol.gemspec +40 -0
  17. data/jenkins.sh +33 -0
  18. data/lib/eol.rb +79 -0
  19. data/lib/eol/api.rb +32 -0
  20. data/lib/eol/client.rb +13 -0
  21. data/lib/eol/config.rb +105 -0
  22. data/lib/eol/exception.rb +21 -0
  23. data/lib/eol/oauth.rb +117 -0
  24. data/lib/eol/parser.rb +42 -0
  25. data/lib/eol/request.rb +63 -0
  26. data/lib/eol/resource.rb +104 -0
  27. data/lib/eol/resources/account.rb +43 -0
  28. data/lib/eol/resources/address.rb +36 -0
  29. data/lib/eol/resources/aging_receivables_list.rb +34 -0
  30. data/lib/eol/resources/bank_account.rb +32 -0
  31. data/lib/eol/resources/bank_entry.rb +22 -0
  32. data/lib/eol/resources/bank_entry_line.rb +20 -0
  33. data/lib/eol/resources/base_entry_line.rb +20 -0
  34. data/lib/eol/resources/cash_entry.rb +22 -0
  35. data/lib/eol/resources/cash_entry_line.rb +20 -0
  36. data/lib/eol/resources/contact.rb +27 -0
  37. data/lib/eol/resources/costcenter.rb +19 -0
  38. data/lib/eol/resources/costunit.rb +19 -0
  39. data/lib/eol/resources/division.rb +26 -0
  40. data/lib/eol/resources/document.rb +23 -0
  41. data/lib/eol/resources/document_attachment.rb +19 -0
  42. data/lib/eol/resources/general_journal_entry.rb +19 -0
  43. data/lib/eol/resources/general_journal_entry_line.rb +11 -0
  44. data/lib/eol/resources/gl_account.rb +27 -0
  45. data/lib/eol/resources/goods_delivery.rb +37 -0
  46. data/lib/eol/resources/goods_delivery_line.rb +38 -0
  47. data/lib/eol/resources/item.rb +36 -0
  48. data/lib/eol/resources/item_group.rb +23 -0
  49. data/lib/eol/resources/journal.rb +23 -0
  50. data/lib/eol/resources/layout.rb +26 -0
  51. data/lib/eol/resources/mailbox.rb +22 -0
  52. data/lib/eol/resources/payment_condition.rb +25 -0
  53. data/lib/eol/resources/printed_sales_invoice.rb +37 -0
  54. data/lib/eol/resources/project.rb +26 -0
  55. data/lib/eol/resources/purchase_entry.rb +20 -0
  56. data/lib/eol/resources/purchase_entry_line.rb +11 -0
  57. data/lib/eol/resources/receivables_list.rb +31 -0
  58. data/lib/eol/resources/sales_entry.rb +22 -0
  59. data/lib/eol/resources/sales_entry_line.rb +12 -0
  60. data/lib/eol/resources/sales_invoice.rb +25 -0
  61. data/lib/eol/resources/sales_invoice_line.rb +24 -0
  62. data/lib/eol/resources/sales_item_prices.rb +18 -0
  63. data/lib/eol/resources/sales_order.rb +23 -0
  64. data/lib/eol/resources/sales_order_line.rb +23 -0
  65. data/lib/eol/resources/shared_sales_attributes.rb +11 -0
  66. data/lib/eol/resources/time_transaction.rb +24 -0
  67. data/lib/eol/resources/transaction.rb +23 -0
  68. data/lib/eol/resources/transaction_line.rb +23 -0
  69. data/lib/eol/resources/user.rb +27 -0
  70. data/lib/eol/resources/vat_code.rb +20 -0
  71. data/lib/eol/response.rb +72 -0
  72. data/lib/eol/result_set.rb +62 -0
  73. data/lib/eol/sanitizer.rb +54 -0
  74. data/lib/eol/uri.rb +87 -0
  75. data/lib/eol/utils.rb +50 -0
  76. data/lib/eol/version.rb +15 -0
  77. metadata +356 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a9ca2c43af778808a2e4726db835f1f4f696473a100ffc3bb7b980e568df4cbd
4
+ data.tar.gz: 8ddacb7b932ff7162ef6e7a24ad14dde73e60d02944ccbcf0b6c932edfaede0b
5
+ SHA512:
6
+ metadata.gz: ea4d653b89271be4b224912da05d3481e284db13707199ff1a571af10501d165dfff85fe26581672024774150b9ed29aecca15d4033e6f0149c6fdb690ee6366
7
+ data.tar.gz: 706b7cbb0c64759e603565ef89b82c307498f2dad72161eaa1b3935cb938620a4d66b825de7341e66c3ef866404d7b70134d7d0414ce45e7c272cf297c63b6de
data/.drone.yaml ADDED
@@ -0,0 +1,12 @@
1
+ cache:
2
+ mount:
3
+ - bundler
4
+ build:
5
+ image: ruby:2.2.3
6
+ cache:
7
+ - bundler
8
+ commands:
9
+ - apt-get update -qq
10
+ - apt-get install -y -qq build-essential &>/dev/null
11
+ - bundle install --jobs $(nproc) --path bundler
12
+ - ./script/ci
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
11
+ .env
12
+ .log
13
+ *.gem
14
+ *.swp
15
+ .idea
16
+ *.iml
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,60 @@
1
+ Documentation:
2
+ Enabled: false
3
+
4
+ Lint/UriEscapeUnescape:
5
+ Enabled: false
6
+
7
+ NAming/FileName:
8
+ Enabled: false
9
+
10
+ Style/MethodMissing:
11
+ Enabled: false
12
+
13
+ StringLiterals:
14
+ EnforcedStyle: double_quotes
15
+
16
+ AllCops:
17
+ Include:
18
+ - '**/Rakefile'
19
+ - '**/config.ru'
20
+ Exclude:
21
+ - 'db/**/*'
22
+ - 'tmp/**/*'
23
+ - 'vendor/**/*'
24
+ - 'bin/**/*'
25
+ - 'log/**/*'
26
+ - 'spec/**/*'
27
+ - 'config/**/*'
28
+
29
+ Metrics/AbcSize:
30
+ Max: 30
31
+ Metrics/BlockNesting:
32
+ Max: 3
33
+ Metrics/ClassLength:
34
+ CountComments: false # count full line comments?
35
+ Max: 100
36
+ Metrics/CyclomaticComplexity:
37
+ Max: 6
38
+ Metrics/LineLength:
39
+ Max: 150
40
+ AllowURI: true
41
+ URISchemes:
42
+ - http
43
+ - https
44
+
45
+ Metrics/MethodLength:
46
+ CountComments: false # count full line comments?
47
+ Max: 13
48
+
49
+ Metrics/ParameterLists:
50
+ Max: 5
51
+ CountKeywordArgs: true
52
+
53
+ Metrics/PerceivedComplexity:
54
+ Max: 7
55
+ Style/PerlBackrefs:
56
+ Enabled: false
57
+ Lint/AmbiguousOperator:
58
+ Enabled: false
59
+ Rails/Delegate:
60
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.1
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5.1
4
+ cache: bundler
5
+ notifications:
6
+ webhooks:
7
+ urls:
8
+ - https://webhooks.gitter.im/e/fe18055a45cdf1a5ecae
9
+ on_success: change
10
+ on_failure: always
11
+ on_start: never
data/Changelog.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 1.0.0
2
+
3
+ - First release
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in eol.gemspec
6
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ guard :rspec, cmd: "rspec" do
4
+ watch(%r{^spec/.+_spec\.rb$})
5
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
6
+ watch("spec/spec_helper.rb") { "spec" }
7
+ end
8
+
9
+ guard :rubocop, all_on_start: false, cli: ["--format", "clang", "--rails"] do
10
+ watch(%r{^spec/.+_spec\.rb$})
11
+ watch(%r{^lib/(.+)\.rb$})
12
+ watch("spec/spec_helper.rb")
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) [2018] [Hoppinger]
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,247 @@
1
+ # Eol
2
+
3
+ Eol stand for ExactOnline, it's just an API wrapper for [Exact Online](https://developers.exactonline.com/).
4
+
5
+ # DISCLAIMER
6
+
7
+ __As of 25th of May, 2FA will be mandatory for all Exact Online accounts. Therefor the auto authorize method with which you could simulate an App 2 App connection will not work anymore. Hoppinger is working on a new solution. The methods will stay available but probably won't work and will show a deprecation warning. [Read more about it here](https://support.exactonline.com/community/s/knowledge-base#All-All-HNO-Concept-general-security-gen-auth-totpc)__
8
+
9
+ ### Contributors
10
+
11
+ * [Ahmad](https://github.com/ahmadhasankhan)
12
+
13
+ Thanks for helping! If you want to contribute read through this readme how to!
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'eol-client'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install Eol
30
+
31
+ ## Authorization and Setup
32
+
33
+ You have to have an Exact Online account and an app setup to connect with.
34
+
35
+ You have to set a few variables to make a connection possible. I'd suggest using environment variables set with [dotenv](https://github.com/bkeepers/dotenv) for that.
36
+
37
+ Then configure Eol like this (take these values from your app that you setup at apps.exactonline.com)
38
+
39
+ ```ruby
40
+ Eol.configure do |config|
41
+ config.client_id = ENV['CLIENT_ID']
42
+ config.client_secret = ENV['CLIENT_SECRET']
43
+ config.redirect_uri = ENV['REDIRECT_URI']
44
+ end
45
+ ```
46
+
47
+ You then have to retrieve an access token for the user you want to login with. You can setup a user facing OAUTH2 flow or set the `access_token` and/or refresh token on the go.
48
+
49
+ ```ruby
50
+ Eol.configure do |config|
51
+ config.access_token = "42"
52
+ config.refresh_token = "42"
53
+ end
54
+ ```
55
+
56
+ Once you've set your access tokens you should be able to do requests. You'll need a division number, you can retrieve that with the following line of code:
57
+
58
+ ```
59
+ get("/Current/Me", no_division: true).results.first.current_division
60
+ ```
61
+
62
+
63
+ ### Logger
64
+
65
+ The default logger is STDOUT. A custom logger can be be configured.
66
+
67
+ ```ruby
68
+ dir = File.dirname("./tmp/errors.log")
69
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
70
+
71
+ Eol.configure do |config|
72
+ config.logger = ::Logger.new("./tmp/errors.log", "daily")
73
+ end
74
+ ```
75
+
76
+ ## Accessing the API
77
+
78
+ We can retrieve data from the API using the following syntax.
79
+
80
+ The query will return an `Eol::ResultSet` which contains up to 60 records and
81
+ a method to retrieve the next page of results. Unfortunately the ExactOnline API
82
+ does not allow us to retrieve a specific page or define how many records we want
83
+ to retrieve at a time.
84
+
85
+ ```ruby
86
+ # Query the API and return an Eol::ResultSet
87
+ accounts = Eol::Account.new.find_all
88
+
89
+ # Return an array of accounts
90
+ accounts.records
91
+ ```
92
+
93
+ If the query results in more than 60 records the next set can be retrieved using
94
+ the `next_page` method.
95
+
96
+ ```ruby
97
+ # Return an Eol::ResultSet containing the next page's records
98
+ accounts.next_page
99
+ ```
100
+
101
+ ### Filter and sort results
102
+
103
+ Filtering result sets can be done by adding attributes to the initializer and then
104
+ using `find_by`. Filters accept a single value or an array of values.
105
+
106
+ ```ruby
107
+ # Find the account with code 123
108
+ accounts = Eol::Account.new(code: '123').find_by(filters: [:code])
109
+
110
+ # Find the accounts with code 123 and 345
111
+ accounts = Eol::Account.new(code: ['123', '345']).find_by(filters: [:code])
112
+ ```
113
+
114
+ You can also match on values "greater than" or "less than" by specifying `gt` or `lt`:
115
+
116
+ ```ruby
117
+ # Find all AgingReceivables with an amount greater than 0 in the third age range
118
+ Eol::AgingReceivablesList.new(age_group3_amount: { gt: 0 }).find_by(filters: [:age_group3_amount])
119
+ ```
120
+
121
+ Results can be sorted in the same way
122
+
123
+ ```ruby
124
+ # Return all accounts sorted by first name
125
+ accounts = Eol::Account.new.find_all(order_by: :first_name)
126
+ ```
127
+
128
+ Filters and sorting can also be combined
129
+
130
+ ```ruby
131
+ # Return accounts with code 123 and 345 sorted by first name
132
+ accounts = Eol::Account.new(code: ['123', '345']).find_by(filters: [:code], order_by: :first_name)
133
+ ```
134
+
135
+ To find an individual record by its ID the `find` method can be used
136
+
137
+ ```ruby
138
+ # Return the account with guid
139
+ account = Eol::Account.new(id: '9e3a078e-55dc-40f4-a490-1875400a3e10').find
140
+ ```
141
+
142
+ For more information on this way of selecting data look here http://www.odata.org/
143
+
144
+ ### Creating new records
145
+
146
+ Use the initializer method followed by 'save' to create a new record:
147
+
148
+ ```ruby
149
+ # Create a new contact
150
+ contact = Eol::Contact.new(first_name: "Karel", last_name: "Appel", account: "8d87c8c5-f1c6-495c-b6af-d5ba396873b5" )
151
+ contact.save
152
+ ```
153
+
154
+ ### Projects and Time Tracking
155
+
156
+ Project Types:
157
+
158
+ - :type=>2, :type_description=>"Fixed price"
159
+ - :type=>3, :type_description=>"Time and Material"
160
+ - :type=>4, :type_description=>"Non billable"
161
+ - :type=>5, :type_description=>"Prepaid"
162
+
163
+ To create a new project
164
+
165
+ ```ruby
166
+ project = Eol::Project.new(code: "PROJ902343", description: "Great project", account: "8d87c8c5-f1c6-495c-b6af-d5ba396873b5", type: 2 )
167
+ project.save
168
+ ```
169
+
170
+ To submit a new time transaction
171
+
172
+ ```ruby
173
+ hours = Eol::TimeTransaction.new(account: "8d87c8c5-f1c6-495c-b6af-d5ba396873b5", item: "eb73942a-53c0-4ee9-bbb2-6d985814a1b1", quantity: 3.0, notes: "")
174
+ hours.save
175
+ ```
176
+
177
+ ### SalesInvoice flow
178
+ SalesInvoices have a relationship with SalesInvoiceLines. A SalesInvoice has many
179
+ SalesInvoiceLines and a SalesInvoiceLine belongs to a SalesInvoice. To create a
180
+ valid SalesInvoice you need to embed the SalesInvoiceLines
181
+
182
+ ```ruby
183
+ sales_invoice = Eol::SalesInvoice.new(journal: "id of your journal", ordered_by: "id of customer")
184
+ ```
185
+ Now it still needs SalesInvoiceLines
186
+
187
+ ```ruby
188
+ sales_invoice_lines = []
189
+ sales_invoice_lines << Eol::SalesInvoiceLine.new(item: "id of item being sold") # do this for each item you want in the invoice.
190
+ sales_invoice.sales_invoice_lines = sales_invoice_lines
191
+ ```
192
+ Now you can save the SalesInvoice and it will be parsed to the following
193
+ ```ruby
194
+ sales_invoice.save
195
+ # Sanitized object: {"Journal"=>"id of your journal", "OrderedBy"=>"id of customer", "SalesInvoiceLines"=>[{"Item"=>"id of item being sold"}]}
196
+ ```
197
+
198
+ If you have a SalesInvoice with an id(so saved before already), you can also create invoice lines without embedding
199
+
200
+ ```ruby
201
+ sales_invoice = Eol::SalesInvoice.new({id: "1"}).first
202
+ sales_invoice_line = Eol::SalesInvoiceLine.new(invoice_ID: sales_invoice, item: "42")
203
+ sales_invoice.save
204
+ # Sanitized object: {"Item"=>"42", "InvoiceID"=>"1"}
205
+ ```
206
+
207
+ For many resources there are mandatory attributes, you can see that in the classes
208
+ for every resource. For example for Contact: https://github.com/exactonline/exactonline-api-ruby-client/blob/master/lib/Eol/resources/contact.rb
209
+
210
+ ###Divisions and Endpoints
211
+
212
+ Usually in the exact wrapper you need a division number, this one will be set on authorization checks (with `/Current/Me` endpoint). Sometimes you need to do a request without the division number, or even without the standard `/api/v1` endpoint. Like so:
213
+
214
+ ```ruby
215
+ response = Eol.get('/api/oauth2/token', no_endpoint: true, no_division: true)
216
+ response = Eol.get('/Current/Me', no_division: true)
217
+ ```
218
+
219
+ ## Development
220
+
221
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
222
+
223
+ 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` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
224
+
225
+ ## Contributing
226
+
227
+ 1. Fork it ( https://github.com/[my-github-username]/eol/fork )
228
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
229
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
230
+ 4. Push to the branch (`git push origin my-new-feature`)
231
+ 5. Create a new Pull Request
232
+
233
+ ## Testing
234
+
235
+ We use Rspec for normal unit testing. We aim for coverage above 90%. Also the current suite should succeed when you commit something.
236
+ We use Rubocop for style checking, this should also succeed before you commit anything.
237
+
238
+ We're also experimenting with Mutation testing, which alters your code to test if your specs fail when there's faulty code. This is important when you
239
+ alter a vital part of the code, make sure the mutation percentage is higher than 80%. To run a part of the code with mutant run the follwing
240
+ `mutant --include lib/Eol --require Eol --use rspec Eol::ClassYoureWorkingOn`
241
+
242
+ To test the vital classes run this
243
+ `mutant --include lib --require Eol --use rspec Eol::Response Eol::Client Eol::Utils Eol::Resource Eol::Request Eol::Parser Eol::Config`
244
+ This will take a few minutes
245
+
246
+ When you're editing code it's advised you run guard, which watches file changes and automatically runs Rspec and Rubocop.
247
+
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ task :rspec do
6
+ sh "rspec"
7
+ end
8
+
9
+ task :rubocop do
10
+ sh "rubocop"
11
+ end
12
+
13
+ task default: %i[rspec]
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "eol"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ require "dotenv"
15
+ Dotenv.load
16
+ IRB.start