sale 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5709e56615138240b522d2e4e92a8e68db8809aa
4
+ data.tar.gz: 3c4b2e2158939f9787e8d5685c158661c4f60635
5
+ SHA512:
6
+ metadata.gz: d3dcdea744947207aa95a4681debe802c85295c5c93974475389e7cf2c8771584f2fce31de24bb2ccdaa61e0c72b2e2efff604c14c3a49c7833858faf3a96e49
7
+ data.tar.gz: a8e887b2fe4b1dc16a04244f247e57da772bcf302ca0d6c36cad8628bef4897548d785cd4913ac7cc9277473d67af569ece0c0291c2430881951dc05471eaccd
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/gems.rb ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # specified in gemspec
4
+ gemspec
data/lib/base.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'dagger'
2
+ require 'json'
3
+
4
+ module Bsale
5
+ class InvalidResponseError < StandardError; end
6
+
7
+ class APIBase
8
+ ENDPOINT = 'https://api.bsale.cl'.freeze
9
+ VERSION = 'v1'.freeze
10
+
11
+ def initialize(token)
12
+ @token = token or raise 'Token not set!'
13
+ end
14
+
15
+ private
16
+
17
+ def request(method, path, body = nil)
18
+ headers = { access_token: @token }
19
+ url = path['://'] ? path : [ ENDPOINT, VERSION, path ].join('/')
20
+ Dagger.request(method, url, body, { json: true, follow: 3, headers: headers })
21
+ end
22
+
23
+ def handle_response(resp)
24
+ if resp.success?
25
+ data = parse_json(resp.body)
26
+ return data ? OpenStruct.new(data) : nil
27
+ else
28
+ raise InvalidResponseError.new("#{resp.code}: #{resp.body}")
29
+ end
30
+ end
31
+
32
+ def parse_json(str)
33
+ JSON.parse(str)
34
+ rescue JSON::ParserError => e
35
+ puts "Invalid JSON: #{e.message}"
36
+ nil
37
+ end
38
+
39
+ end
40
+
41
+ end
data/lib/bsale.rb ADDED
@@ -0,0 +1,2 @@
1
+ require_relative './buyers'
2
+ require_relative './documents'
data/lib/buyers.rb ADDED
@@ -0,0 +1,94 @@
1
+ require_relative './base'
2
+
3
+ class Bsale::Buyers < Bsale::APIBase
4
+ class BuyerNotFoundError < Bsale::InvalidResponseError; end
5
+
6
+ EMAIL_REGEX = /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
7
+
8
+ def get_buyer_with_email_and_code(email, code = nil)
9
+ if list = get_buyers_with_email(email) and list.any?
10
+ with_code = list.select { |b| code.nil? ? (b.code || '').size > 0 : (code.to_s == b.code.to_s) }
11
+
12
+ if with_code.count != 1
13
+ raise BuyerNotFoundError.new("#{with_code.count} clients under #{email} and code #{code}")
14
+ end
15
+
16
+ return with_code.first
17
+ end
18
+ end
19
+
20
+ def get_buyers_with_email(email)
21
+ return nil unless email && email.match(EMAIL_REGEX)
22
+ get_buyers_with(email: email)
23
+ end
24
+
25
+ def get_buyers_with_code(code)
26
+ get_buyers_with(code: code)
27
+ end
28
+
29
+ def add_buyer(firstName:, lastName:, address:, email:, phone:, code:, note:, activity:, municipality:, city:)
30
+ data = {
31
+ accumulatePoints: 1,
32
+ firstName: firstName,
33
+ lastName: lastName,
34
+ code: code,
35
+ address: address,
36
+ email: email,
37
+ phone: phone,
38
+ city: city,
39
+ municipality: municipality,
40
+ note: note, # gender
41
+ activity: activity # date of birth
42
+ }
43
+
44
+ return nil unless email.match(EMAIL_REGEX)
45
+ resp = request(:post, "/clients.json", data)
46
+ handle_response(resp)
47
+ end
48
+
49
+ def update_buyer(id, data)
50
+ if [:email, :code].include?(data.symbolize_keys.keys)
51
+ raise 'Invalid request, cannot update email nor code!'
52
+ end
53
+
54
+ # puts "Updating client #{id} with data: #{data.inspect}"
55
+ resp = request(:put, "/clients/#{id}.json", data.merge(id: id))
56
+ handle_response(resp)
57
+ end
58
+
59
+ def update_buyer_points(buyer, difference, order_code)
60
+ raise 'No point in modifying 0 points' if difference == 0
61
+
62
+ data = {
63
+ email: buyer.email,
64
+ points: difference.abs,
65
+ type: difference > 0 ? 0 : 1, # 0 to add, 1 to subtract
66
+ description: "Apply #{difference} points from order #{order_code}",
67
+ orderId: order_code
68
+ }
69
+
70
+ resp = request(:put, "/clients/points.json", data)
71
+ if resp.code == 200
72
+ return parse_json(resp.body)['points']
73
+ else
74
+ raise InvalidResponseError.new("#{resp.code}: #{resp.body}")
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def get_buyers_with(attrs)
81
+ if data = get_buyers(attrs.merge(state: 0)) and data['items']
82
+ data['items'].map { |item| OpenStruct.new(item) }
83
+ else
84
+ []
85
+ end
86
+ end
87
+
88
+ def get_buyers(query)
89
+ search = query.map { |k,v| URI.encode(k.to_s) + '=' + URI.encode(v.to_s) }.join('&')
90
+ resp = request(:get, '/clients.json?' + search)
91
+ resp.code == 200 ? parse_json(resp.body) : nil
92
+ end
93
+
94
+ end
data/lib/documents.rb ADDED
@@ -0,0 +1,74 @@
1
+ require_relative './base'
2
+
3
+ class Bsale::Documents < Bsale::APIBase
4
+
5
+ def get_document_types
6
+ resp = request(:get, 'document_types.json')
7
+ handle_response(resp)
8
+ end
9
+
10
+ def get_payment_types
11
+ resp = request(:get, 'payment_types.json')
12
+ handle_response(resp)
13
+ end
14
+
15
+ def get_tax_types
16
+ resp = request(:get, 'taxes.json')
17
+ handle_response(resp)
18
+ end
19
+
20
+ def get_price_lists
21
+ resp = request(:get, 'price_lists.json')
22
+ handle_response(resp)
23
+ end
24
+
25
+ def get_pdf(url)
26
+ resp = request(:get, url)
27
+ if resp.success?
28
+ StringIO.new(resp.body, 'rb')
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ def create_doc(codeSii:, emissionDate:, expirationDate:, declareSii:, priceListId:, \
35
+ officeId: nil, clientId: nil, client: nil, details: nil, payments: nil)
36
+
37
+ body = {
38
+ codeSii: codeSii, # we could also use "documentTypeId"
39
+ declareSii: declareSii,
40
+ emissionDate: emissionDate,
41
+ expirationDate: expirationDate
42
+ }
43
+
44
+ body[:officeId] = officeId if officeId
45
+ body[:priceListId] = priceListId if priceListId
46
+
47
+ # facturas require clientId, unlike boletas
48
+ if [33, 34].include?(codeSii.to_i)
49
+ if clientId
50
+ body[:clientId] = clientId
51
+ elsif client
52
+ body[:client] = client
53
+ else
54
+ raise 'clientId or client required'
55
+ end
56
+ end
57
+
58
+ body[:details] = details if details
59
+ body[:payments] = payments if payments
60
+
61
+ resp = request(:post, "documents.json", body.to_json)
62
+ handle_response(resp)
63
+ end
64
+
65
+ def mark_document_declared(id)
66
+ body = { id: id, informedSii: 1 }
67
+ request(:put, 'documents/set_sii_state.json', body)
68
+ end
69
+
70
+ def remove_document(id:, office_id:)
71
+ request(:delete, "documents/#{id}.json?officeid=#{office_id}")
72
+ end
73
+
74
+ end
data/sale.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # require File.expand_path("../lib/bsale/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "sale"
6
+ s.version = '0.0.1'
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Tomás Pollak']
9
+ s.email = ['tomas@bootic.net']
10
+ s.homepage = "https://github.com/github/bsale-api-wrapper"
11
+ s.summary = "A wrapper around the Bsale API."
12
+ s.description = "A wrapper around the Bsale API."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.add_development_dependency "bundler", ">= 1.0.0"
16
+ s.add_development_dependency "rspec-core"
17
+ s.add_development_dependency "rspec-mocks"
18
+ s.add_development_dependency "rspec-expectations"
19
+
20
+ s.add_runtime_dependency "dagger", "~> 0.9.0"
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
24
+ s.require_path = 'lib'
25
+ # s.bindir = 'bin'
26
+ end
@@ -0,0 +1,176 @@
1
+ require 'bundler/setup'
2
+ require_relative '../lib/bsale'
3
+
4
+ RSpec.configure do |config|
5
+ config.color = true
6
+ end
7
+
8
+ describe 'Bsale' do
9
+
10
+ describe 'Buyers' do
11
+
12
+ let(:client) {
13
+ Bsale::Buyers.new('foo')
14
+ }
15
+
16
+ let(:fake_response) {
17
+ double('Response', code: 200, body: '{"count": 0}')
18
+ }
19
+
20
+ before do
21
+ allow(Dagger).to receive(:request).and_return(fake_response)
22
+ end
23
+
24
+ describe 'get_buyer_with_email_and_code' do
25
+
26
+ describe 'when email is invalid' do
27
+
28
+ it 'returns nil, without making a request' do
29
+ expect(client).not_to receive(:request)
30
+ client.get_buyer_with_email_and_code('aaa')
31
+ end
32
+
33
+ end
34
+
35
+ describe 'no results' do
36
+
37
+ before do
38
+ expect(fake_response).to receive(:body).and_return({ items: [] }.to_json)
39
+ end
40
+
41
+ it 'returns nil without code' do
42
+ client.get_buyer_with_email_and_code('aaa@foo.com')
43
+ end
44
+
45
+ it 'returns nil with code' do
46
+ client.get_buyer_with_email_and_code('aaa@foo.com', '123')
47
+ end
48
+
49
+ end
50
+
51
+ describe 'one result with code' do
52
+
53
+ before do
54
+ expect(fake_response).to receive(:body).and_return({ items: [{ code: 123, accumulatePoints: '1' }] }.to_json)
55
+ end
56
+
57
+ it 'returns result, without code' do
58
+ res = client.get_buyer_with_email_and_code('aaa@foo.com')
59
+ expect(res).to eql(OpenStruct.new({ code: 123, accumulatePoints: '1' }))
60
+ end
61
+
62
+ it 'returns result, with matching code' do
63
+ res = client.get_buyer_with_email_and_code('aaa@foo.com', '123')
64
+ expect(res).to eql(OpenStruct.new({ code: 123, accumulatePoints: '1' }))
65
+ end
66
+
67
+ it 'returns nil, with non-matching code' do
68
+ expect do
69
+ client.get_buyer_with_email_and_code('aaa@foo.com', '234')
70
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
71
+ end
72
+
73
+ end
74
+
75
+ describe 'one result with code, another one without' do
76
+
77
+ before do
78
+ list = [{ code: 123, accumulatePoints: '1' }, { code: '', accumulatePoints: '1' }]
79
+ expect(fake_response).to receive(:body).and_return({ items: list }.to_json)
80
+ end
81
+
82
+ it 'returns result, without code' do
83
+ res = client.get_buyer_with_email_and_code('aaa@foo.com')
84
+ expect(res).to eql(OpenStruct.new({ code: 123, accumulatePoints: '1' }))
85
+ end
86
+
87
+ it 'returns result, with matching code' do
88
+ res = client.get_buyer_with_email_and_code('aaa@foo.com', '123')
89
+ expect(res).to eql(OpenStruct.new({ code: 123, accumulatePoints: '1' }))
90
+ end
91
+
92
+ it 'returns nil, with non-matching code' do
93
+ expect do
94
+ client.get_buyer_with_email_and_code('aaa@foo.com', '234')
95
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
96
+ end
97
+
98
+ end
99
+
100
+ describe 'two results with same code' do
101
+
102
+ before do
103
+ expect(fake_response).to receive(:body).and_return({ items: [{ code: 123 }, { code: 123 }] }.to_json)
104
+ end
105
+
106
+ it 'returns nil, without code' do
107
+ expect do
108
+ client.get_buyer_with_email_and_code('aaa@foo.com')
109
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
110
+ end
111
+
112
+ it 'returns nil, with matching code' do
113
+ expect do
114
+ client.get_buyer_with_email_and_code('aaa@foo.com', '123')
115
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
116
+ end
117
+
118
+ it 'returns nil, with non-matching code' do
119
+ expect do
120
+ client.get_buyer_with_email_and_code('aaa@foo.com', '234')
121
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
122
+ end
123
+
124
+ end
125
+
126
+ describe 'two results with different codes' do
127
+
128
+ before do
129
+ list = [{ code: 123, accumulatePoints: '1' }, { code: 345, accumulatePoints: '1' }]
130
+ expect(fake_response).to receive(:body).and_return({ items: list }.to_json)
131
+ end
132
+
133
+ it 'returns nil, without code' do
134
+ expect do
135
+ client.get_buyer_with_email_and_code('aaa@foo.com')
136
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
137
+ end
138
+
139
+ it 'returns result, when code matches one' do
140
+ res = client.get_buyer_with_email_and_code('aaa@foo.com', '123')
141
+ expect(res).to eql(OpenStruct.new({ code: 123, accumulatePoints: '1' }))
142
+ end
143
+
144
+ it 'returns nil, when code doesnt match any' do
145
+ expect do
146
+ client.get_buyer_with_email_and_code('aaa@foo.com', '234')
147
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
148
+ end
149
+
150
+ end
151
+
152
+ describe 'two results without code' do
153
+
154
+ before do
155
+ expect(fake_response).to receive(:body).and_return({ items: [{ code: '' }, { code: '' }] }.to_json)
156
+ end
157
+
158
+ it 'returns nil, without code' do
159
+ expect do
160
+ client.get_buyer_with_email_and_code('aaa@foo.com')
161
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
162
+ end
163
+
164
+ it 'returns nil, with code' do
165
+ expect do
166
+ client.get_buyer_with_email_and_code('aaa@foo.com', '123')
167
+ end.to raise_error(Bsale::Buyers::BuyerNotFoundError)
168
+ end
169
+
170
+ end
171
+
172
+ end
173
+
174
+ end
175
+
176
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sale
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tomás Pollak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-mocks
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-expectations
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: dagger
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.9.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.9.0
83
+ description: A wrapper around the Bsale API.
84
+ email:
85
+ - tomas@bootic.net
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - Rakefile
91
+ - gems.rb
92
+ - lib/base.rb
93
+ - lib/bsale.rb
94
+ - lib/buyers.rb
95
+ - lib/documents.rb
96
+ - sale.gemspec
97
+ - spec/buyers_spec.rb
98
+ homepage: https://github.com/github/bsale-api-wrapper
99
+ licenses: []
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: 1.3.6
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.5.1
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: A wrapper around the Bsale API.
121
+ test_files: []