jortt 4.2.0 → 5.0.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.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -3
  4. data/README.md +45 -151
  5. data/Rakefile +0 -1
  6. data/jortt.gemspec +6 -6
  7. data/lib/jortt.rb +0 -1
  8. data/lib/jortt/client.rb +65 -16
  9. data/lib/jortt/client/customers.rb +56 -49
  10. data/lib/jortt/client/error.rb +33 -0
  11. data/lib/jortt/client/invoices.rb +41 -38
  12. data/lib/jortt/client/ledger_accounts.rb +27 -0
  13. data/lib/jortt/client/version.rb +1 -2
  14. data/spec/fixtures/vcr_cassettes/Jortt/_client/1_1_1.yml +64 -0
  15. data/spec/fixtures/vcr_cassettes/Jortt_Client/configured/_customers/1_1_1_1.yml +64 -0
  16. data/spec/fixtures/vcr_cassettes/Jortt_Client/configured/_invoices/1_1_2_1.yml +64 -0
  17. data/spec/fixtures/vcr_cassettes/Jortt_Client/configured/_ledger_accounts/1_1_3_1.yml +64 -0
  18. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_create/faulty_payload/shows_a_nice_error.yml +413 -0
  19. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_create/valid_payload/creates_the_customer.yml +505 -0
  20. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_delete/deletes_the_customer.yml +505 -0
  21. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_direct_debit_mandate/sends_direct_debit_mandate_to_the_customer_or_responds_with_an_error_when_not_possible.yml +470 -0
  22. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_index/query/returns_the_queried_customers.yml +464 -0
  23. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_index/without_params/returns_customers.yml +415 -0
  24. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_show/returns_the_customer.yml +464 -0
  25. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_update/updates_the_customer.yml +603 -0
  26. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_create/creates_the_invoice.yml +170 -0
  27. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_download/returns_the_invoice_download_link.yml +168 -0
  28. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_index/invoice_status/returns_those_invoices.yml +776 -0
  29. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_index/query/returns_the_queried_invoices.yml +315 -0
  30. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_show/returns_the_invoice.yml +421 -0
  31. data/spec/fixtures/vcr_cassettes/Jortt_Client_LedgerAccounts/_index/returns_invoices.yml +118 -0
  32. data/spec/jortt/client/customers_spec.rb +82 -37
  33. data/spec/jortt/client/invoices_spec.rb +105 -26
  34. data/spec/jortt/client/ledger_accounts_spec.rb +19 -0
  35. data/spec/jortt/client_spec.rb +5 -27
  36. data/spec/jortt_spec.rb +2 -3
  37. data/spec/spec_helper.rb +18 -4
  38. metadata +72 -37
  39. data/.rubocop.yml +0 -26
  40. data/lib/jortt/client/invoice.rb +0 -50
  41. data/spec/jortt/client/invoice_spec.rb +0 -23
@@ -0,0 +1,118 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://app.jortt.nl/oauth-provider/oauth/token
6
+ body:
7
+ encoding: UTF-8
8
+ string: grant_type=client_credentials&scope=invoices%3Aread+invoices%3Awrite+customers%3Aread+customers%3Awrite
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v1.0.1
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Thu, 24 Sep 2020 15:03:35 GMT
25
+ Content-Type:
26
+ - application/json; charset=utf-8
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Connection:
30
+ - keep-alive
31
+ Status:
32
+ - 200 OK
33
+ Cache-Control:
34
+ - private, no-store
35
+ Referrer-Policy:
36
+ - strict-origin-when-cross-origin
37
+ X-Permitted-Cross-Domain-Policies:
38
+ - none
39
+ Pragma:
40
+ - no-cache
41
+ X-Xss-Protection:
42
+ - 1; mode=block
43
+ X-Request-Id:
44
+ - d83ae9ab-c629-46a3-8536-3d48345bbc24
45
+ X-Download-Options:
46
+ - noopen
47
+ Etag:
48
+ - W/"0fb39157c79bce6700240fe1ea97876e"
49
+ X-Frame-Options:
50
+ - SAMEORIGIN
51
+ X-Runtime:
52
+ - '0.333943'
53
+ X-Content-Type-Options:
54
+ - nosniff
55
+ X-Powered-By:
56
+ - Phusion Passenger
57
+ Server:
58
+ - nginx + Phusion Passenger
59
+ body:
60
+ encoding: UTF-8
61
+ string: '{"access_token":"-OQjJQikSmMhA9USfhfgESp3DKCWUUOGBfclpyF0oic","token_type":"Bearer","expires_in":7200,"scope":"invoices:read
62
+ invoices:write customers:read customers:write","created_at":1600959815}'
63
+ recorded_at: Thu, 24 Sep 2020 15:03:35 GMT
64
+ - request:
65
+ method: get
66
+ uri: https://api.jortt.nl/ledger_accounts/invoices
67
+ body:
68
+ encoding: US-ASCII
69
+ string: ''
70
+ headers:
71
+ User-Agent:
72
+ - Faraday v1.0.1
73
+ Accept-Encoding:
74
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
75
+ Accept:
76
+ - "*/*"
77
+ response:
78
+ status:
79
+ code: 200
80
+ message: OK
81
+ headers:
82
+ Date:
83
+ - Thu, 24 Sep 2020 15:03:35 GMT
84
+ Content-Type:
85
+ - application/json
86
+ Content-Length:
87
+ - '1011'
88
+ Connection:
89
+ - keep-alive
90
+ Server:
91
+ - Apache
92
+ Status:
93
+ - 200 OK
94
+ Content-Security-Policy:
95
+ - 'default-src ''self'' ''unsafe-inline'' blob: data: *.jortt.nl; script-src
96
+ ''self'' ''unsafe-eval'' blob: ''unsafe-inline'' files.jortt.nl *.googletagmanager.com
97
+ *.uservoice.com inlinemanual.com *.google-analytics.com *.googleadservices.com
98
+ bat.bing.com tapfiliate.com static.ads-twitter.com tagmanager.google.com analytics.twitter.com
99
+ https://connect.facebook.net https://app.inlinemanual.com; connect-src
100
+ ''self'' https://*.jortt.nl wss://*.jortt.nl analytics.inlinemanual.com files.jortt.nl
101
+ www.google-analytics.com stats.g.doubleclick.net file-storage-app-production.s3.eu-central-1.amazonaws.com
102
+ https://app.inlinemanual.com; style-src ''self'' ''unsafe-inline'' fonts.googleapis.com
103
+ files.jortt.nl tagmanager.google.com https://app.inlinemanual.com; font-src
104
+ ''self'' data: fonts.gstatic.com files.jortt.nl https://app.inlinemanual.com; frame-src
105
+ ''self'' *.jortt.nl files.jortt.nl b.frstre.com beacon.tapfiliate.com jortt.uservoice.com
106
+ *.vimeo.com https://connect.facebook.net; img-src ''self'' blob: data:
107
+ *.jortt.nl https://www.facebook.com files.jortt.nl www.google-analytics.com
108
+ stats.g.doubleclick.net www.google.nl www.google.com bat.bing.com googleads.g.doubleclick.net
109
+ https://www.googletagmanager.com https://widget.uservoice.com www.google.de
110
+ t.co *.gstatic.com https://ssl.google-analytics.com https://app.inlinemanual.com'
111
+ body:
112
+ encoding: UTF-8
113
+ string: '{"data":[{"ledger_account_id":"05a59e27-489f-466d-adf7-fc06f576d4ec","parent_ledger_account_id":"105ea7d7-8bb5-499e-9823-8324826b6563","name":"Omzet","selectable":false},{"ledger_account_id":"fcabf6bf-cccc-4a6d-b04e-7b2369d04a79","parent_ledger_account_id":"05a59e27-489f-466d-adf7-fc06f576d4ec","name":"Diverse
114
+ omzet","selectable":true},{"ledger_account_id":"05ba2a61-a0cc-4736-9000-89fb361e85c8","parent_ledger_account_id":"05a59e27-489f-466d-adf7-fc06f576d4ec","name":"Omzet
115
+ uit facturen","selectable":true},{"ledger_account_id":"92cb5ad4-e2e7-4c41-8e17-bf7db69c7b9a","parent_ledger_account_id":"986e5b45-dae6-48cc-b4c9-a2f30fbd7bfe","name":"Inkoopkosten","selectable":false},{"ledger_account_id":"424d222d-f8cb-4d38-82c0-24674a2dcf75","parent_ledger_account_id":"92cb5ad4-e2e7-4c41-8e17-bf7db69c7b9a","name":"Inkoop","selectable":true},{"ledger_account_id":"a5c08fb4-0a22-4f53-9ee2-d7682bba10f7","parent_ledger_account_id":"92cb5ad4-e2e7-4c41-8e17-bf7db69c7b9a","name":"Uitbesteed
116
+ werk","selectable":true}]}'
117
+ recorded_at: Thu, 24 Sep 2020 15:03:35 GMT
118
+ recorded_with: VCR 6.0.0
@@ -1,59 +1,104 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
- describe Jortt::Client::Customers do
5
- let(:customers) do
6
- described_class.new(
7
- double(base_url: 'foo', app_name: 'app', api_key: 'secret'),
8
- )
3
+ describe Jortt::Client::Customers, :vcr do
4
+ let(:client) { Jortt.client(ENV['JORTT_CLIENT_ID'], ENV['JORTT_CLIENT_SECRET']) }
5
+
6
+ let(:params) do
7
+ {
8
+ is_private: false,
9
+ customer_name: 'Nuka-Cola Corporation',
10
+ address_street: 'Vault 11',
11
+ address_postal_code: '1111AA',
12
+ address_city: 'Mojave Wasteland'
13
+ }
14
+ end
15
+
16
+ let!(:jane) { client.customers.create(is_private: true, customer_name: 'Jane Doe')['id'] }
17
+ let!(:john) { client.customers.create(is_private: true, customer_name: 'John Doe')['id'] }
18
+
19
+ after do
20
+ client.customers.delete(jane)
21
+ client.customers.delete(john)
9
22
  end
10
23
 
11
- describe '#all' do
24
+ describe '#index' do
12
25
  context 'without params' do
13
- subject { customers.all }
26
+ subject { client.customers.index.to_a }
14
27
 
15
- before do
16
- url = 'http://app:secret@foo/customers/all?page=1&per_page=50'
17
- stub_request(:get, url).
18
- to_return(status: 200, body: '{"customers": ["foo"]}')
28
+ it "returns customers" do
29
+ expect(subject.count).to eq(3)
30
+ expect(subject[0]['customer_name']).to eq('Jane Doe')
31
+ expect(subject[1]['customer_name']).to eq('John Doe')
32
+ expect(subject[2]['customer_name']).to eq('Search target')
19
33
  end
20
-
21
- it { should eq('customers' => ['foo']) }
22
34
  end
23
35
 
24
- context 'with params' do
25
- subject { customers.all(page: page, per_page: per_page) }
26
- let(:page) { 3 }
27
- let(:per_page) { 25 }
36
+ context 'query' do
37
+ subject { client.customers.index(query: 'Search target') }
28
38
 
29
- before do
30
- url = 'http://app:secret@foo/customers/all?page=3&per_page=25'
31
- stub_request(:get, url).
32
- to_return(status: 200, body: '{"customers": ["bar"]}')
39
+ it "returns the queried customers" do
40
+ expect(subject.count).to eq(1)
41
+ expect(subject.first['customer_name']).to eq("Search target")
33
42
  end
43
+ end
44
+ end
45
+
46
+ describe '#show' do
47
+ subject { client.customers.show(jane) }
34
48
 
35
- it { should eq('customers' => ['bar']) }
49
+ it "returns the customer" do
50
+ expect(subject['customer_name']).to eq("Jane Doe")
36
51
  end
37
52
  end
38
53
 
39
54
  describe '#create' do
40
- let(:request_body) { JSON.generate(customer: {line_items: []}) }
41
- let(:response_body) { JSON.generate(customer_id: 'abc') }
42
- subject { customers.create(line_items: []) }
43
- before do
44
- stub_request(:post, 'http://app:secret@foo/customers').
45
- with(body: request_body).
46
- to_return(status: 200, body: response_body)
55
+ context "valid payload" do
56
+ subject { client.customers.create(params) }
57
+ after { client.customers.delete(subject['id']) }
58
+
59
+ it "creates the customer" do
60
+ uuid_length = 36
61
+ expect(subject['id'].length).to eq(uuid_length)
62
+ end
63
+ end
64
+
65
+ context "faulty payload" do
66
+ subject { client.customers.create({}) }
67
+
68
+ it "shows a nice error" do
69
+ expect { subject }.to raise_error(Jortt::Client::Error)
70
+ end
71
+ end
72
+ end
73
+
74
+ describe '#update' do
75
+ let(:uuid) { client.customers.create(params).fetch('id') }
76
+ subject { client.customers.update(uuid, params.merge(address_extra_information: 'Extra...')) }
77
+ after { client.customers.delete(uuid) }
78
+
79
+ it "updates the customer" do
80
+ expect(subject).to eq(true)
47
81
  end
48
- it { should eq('customer_id' => 'abc') }
49
82
  end
50
83
 
51
- describe '#search' do
52
- subject { customers.search('terms') }
53
- before do
54
- stub_request(:get, 'http://app:secret@foo/customers?query=terms').
55
- to_return(status: 200, body: '{"customers": []}')
84
+ describe '#delete' do
85
+ let(:uuid) { client.customers.create(params).fetch('id') }
86
+ subject { client.customers.delete(uuid) }
87
+
88
+ it "deletes the customer" do
89
+ expect(subject).to eq(true)
90
+ end
91
+ end
92
+
93
+ describe '#direct_debit_mandate' do
94
+ subject { client.customers.direct_debit_mandate(jane) }
95
+
96
+ it "sends direct debit mandate to the customer or responds with an error when not possible" do
97
+ begin
98
+ subject
99
+ rescue Jortt::Client::Error => e
100
+ expect(e.details.first['key']).to eq("DirectDebit::NotEnabled")
101
+ end
56
102
  end
57
- it { should eq('customers' => []) }
58
103
  end
59
104
  end
@@ -1,40 +1,119 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
- describe Jortt::Client::Invoices do
5
- let(:invoices) do
6
- described_class.new(
7
- double(base_url: 'foo', app_name: 'app', api_key: 'secret'),
8
- )
3
+ describe Jortt::Client::Invoices, :vcr do
4
+ let(:client) { Jortt.client(ENV['JORTT_CLIENT_ID'], ENV['JORTT_CLIENT_SECRET']) }
5
+
6
+ let(:customer) { client.customers.index(query: 'Search target').first }
7
+
8
+ let(:params) do
9
+ {
10
+ customer_id: customer.fetch('id'),
11
+ send_method: 'self',
12
+ line_items: [{
13
+ vat: 21,
14
+ amount_per_unit: {
15
+ value: 499,
16
+ currency: 'EUR'
17
+ },
18
+ units: 4,
19
+ description: 'Your product'
20
+ }]
21
+ }
22
+ end
23
+
24
+ describe '#index' do
25
+ context 'pagination', vcr: false do
26
+ subject { client.invoices.index }
27
+
28
+ before do
29
+ VCR.turn_off!
30
+
31
+ stub_request(:any, "https://app.jortt.nl/oauth-provider/oauth/token").
32
+ to_return(
33
+ headers: {content_type: 'application/json'},
34
+ body: {access_token: 'abc'}.to_json)
35
+
36
+ stub_request(:get, "https://api.jortt.nl/invoices?invoice_status&page=1&query").
37
+ to_return(
38
+ headers: {content_type: 'application/json'},
39
+ body: {
40
+ 'data': [{id: 1}, {id: 2}],
41
+ _links: {next: "https://api.jortt.nl/invoices?page=2"}
42
+ }.to_json
43
+ )
44
+
45
+ stub_request(:get, "https://api.jortt.nl/invoices?invoice_status&page=2&query").
46
+ to_return(
47
+ headers: {content_type: 'application/json'},
48
+ body: {
49
+ data: [{id: 3}, {id: 4}],
50
+ _links: {next: "https://api.jortt.nl/invoices?page=3"}
51
+ }.to_json
52
+ )
53
+
54
+ stub_request(:get, "https://api.jortt.nl/invoices?invoice_status&page=3&query").
55
+ to_return(
56
+ headers: {content_type: 'application/json'},
57
+ body: {
58
+ data: [{id: 5}],
59
+ _links: {next: nil}
60
+ }.to_json
61
+ )
62
+ end
63
+
64
+ after { VCR.turn_on! }
65
+
66
+ it "returns the first page" do
67
+ expect(subject.first.fetch('id')).to eq(1)
68
+ end
69
+
70
+ it "seamlessly returns results from the other pages" do
71
+ expect(subject.to_a.count).to eq(5)
72
+ end
73
+ end
74
+
75
+ context 'invoice_status' do
76
+ subject { client.invoices.index(invoice_status: 'sent') }
77
+
78
+ it "returns those invoices" do
79
+ expect(subject.count).to be > 0
80
+ expect(subject.first.fetch('invoice_status')).to eq('sent')
81
+ end
82
+ end
83
+
84
+ context 'query' do
85
+ subject { client.invoices.index(query: 'Search target') }
86
+
87
+ it "returns the queried invoices" do
88
+ expect(subject.first.dig('invoice_due_amount', 'value')).to eq("2415.16")
89
+ end
90
+ end
9
91
  end
10
92
 
11
93
  describe '#create' do
12
- let(:request_body) { JSON.generate(invoice: {line_items: []}) }
13
- let(:response_body) { JSON.generate(invoice_id: 'abc') }
14
- subject { invoices.create(line_items: []) }
15
- before do
16
- stub_request(:post, 'http://app:secret@foo/invoices').
17
- with(body: request_body).
18
- to_return(status: 200, body: response_body)
94
+ subject { client.invoices.create(params) }
95
+
96
+ it "creates the invoice" do
97
+ uuid_length = 36
98
+ expect(subject['id'].length).to eq(uuid_length)
19
99
  end
20
- it { should eq('invoice_id' => 'abc') }
21
100
  end
22
101
 
23
- describe '#get' do
24
- subject { invoices.get('foo') }
25
- before do
26
- stub_request(:get, 'http://app:secret@foo/invoices/id/foo').
27
- to_return(status: 200, body: '{"id": "foo"}')
102
+ describe '#show' do
103
+ let(:uuid) { client.invoices.index(query: 'Search target').first.fetch('id') }
104
+ subject { client.invoices.show(uuid) }
105
+
106
+ it "returns the invoice" do
107
+ expect(subject.dig('invoice_due_amount', 'value')).to eq("2415.16")
28
108
  end
29
- it { should eq('id' => 'foo') }
30
109
  end
31
110
 
32
- describe '#search' do
33
- subject { invoices.search('terms') }
34
- before do
35
- stub_request(:get, 'http://app:secret@foo/invoices/search?query=terms').
36
- to_return(status: 200, body: '{"invoices": []}')
111
+ describe '#download' do
112
+ let(:uuid) { client.invoices.index(query: 'Download test').first.fetch('id') }
113
+ subject { client.invoices.download(uuid) }
114
+
115
+ it "returns the invoice download link" do
116
+ expect(subject.fetch('download_location')).to match(/https:\/\/files\.jortt\.nl\/.*/)
37
117
  end
38
- it { should eq('invoices' => []) }
39
118
  end
40
119
  end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jortt::Client::LedgerAccounts, :vcr do
4
+ let(:client) { Jortt.client(ENV['JORTT_CLIENT_ID'], ENV['JORTT_CLIENT_SECRET']) }
5
+
6
+ describe '#index' do
7
+ subject { client.ledger_accounts.index }
8
+
9
+ it "returns invoices" do
10
+ expect(subject.count).to be > 0
11
+ expect(subject.first).to eq(
12
+ "ledger_account_id" => "05a59e27-489f-466d-adf7-fc06f576d4ec",
13
+ "name" => "Omzet",
14
+ "parent_ledger_account_id" => "105ea7d7-8bb5-499e-9823-8324826b6563",
15
+ "selectable" => false
16
+ )
17
+ end
18
+ end
19
+ end
@@ -1,30 +1,8 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
- describe Jortt::Client do
5
- describe '#initialize' do
6
- subject { described_class.new(opts) }
7
- let(:opts) { {} }
8
-
9
- specify { expect { subject }.to raise_error(KeyError) }
10
-
11
- context 'given app_name' do
12
- before { opts[:app_name] = 'name' }
13
-
14
- specify { expect { subject }.to raise_error(KeyError) }
15
-
16
- context 'and api_key' do
17
- before { opts[:api_key] = 'secret' }
18
- it { should be_instance_of(described_class) }
19
- its(:base_url) { should eq(described_class::BASE_URL) }
20
- its(:app_name) { should eq('name') }
21
- its(:api_key) { should eq('secret') }
22
- end
23
- end
24
- end
25
-
3
+ describe Jortt::Client, :vcr do
26
4
  context 'configured' do
27
- let(:client) { described_class.new(app_name: 'app', api_key: 'secret') }
5
+ let(:client) { described_class.new(ENV['JORTT_CLIENT_ID'], ENV['JORTT_CLIENT_SECRET']) }
28
6
 
29
7
  describe '#customers' do
30
8
  subject { client.customers }
@@ -36,9 +14,9 @@ describe Jortt::Client do
36
14
  it { should be_instance_of(described_class::Invoices) }
37
15
  end
38
16
 
39
- describe '#invoice' do
40
- subject { client.invoice('foo') }
41
- it { should be_instance_of(described_class::Invoice) }
17
+ describe '#ledger_accounts' do
18
+ subject { client.ledger_accounts }
19
+ it { should be_instance_of(described_class::LedgerAccounts) }
42
20
  end
43
21
  end
44
22
  end