sale 0.0.1

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: 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: []