bling-ruby-api 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +30 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +44 -0
- data/LICENSE +21 -0
- data/README.md +68 -0
- data/Rakefile +10 -0
- data/bling.gemspec +27 -0
- data/lib/bling.rb +16 -0
- data/lib/bling/api.rb +26 -0
- data/lib/bling/api/client.rb +67 -0
- data/lib/bling/api/locales/translations.yml +71 -0
- data/lib/bling/api/order.rb +224 -0
- data/lib/bling/api/parser.rb +30 -0
- data/lib/bling/api/product.rb +143 -0
- data/lib/bling/api/record.rb +25 -0
- data/lib/bling/api/request.rb +53 -0
- data/lib/bling/api/response.rb +46 -0
- data/lib/bling/api/templates/order_installment_template +6 -0
- data/lib/bling/api/templates/order_item_template +8 -0
- data/lib/bling/api/templates/order_template +46 -0
- data/lib/bling/api/templates/order_update_template +4 -0
- data/lib/bling/api/templates/product_template +22 -0
- data/lib/bling/api/templates/user_template +17 -0
- data/lib/bling/api/translator.rb +61 -0
- data/lib/bling/api/user.rb +72 -0
- data/lib/bling/base.rb +15 -0
- data/lib/bling/config.rb +50 -0
- data/lib/bling/railtie.rb +9 -0
- data/lib/bling/version.rb +3 -0
- data/spec/lib/bling/api/client_spec.rb +45 -0
- data/spec/lib/bling/api/product_spec.rb +50 -0
- data/spec/lib/bling/api/record_spec.rb +12 -0
- data/spec/lib/bling/api/response_spec.rb +23 -0
- data/spec/lib/bling/api/translator_spec.rb +83 -0
- data/spec/spec_helper.rb +8 -0
- metadata +113 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Bling
|
4
|
+
module API
|
5
|
+
class Parser
|
6
|
+
|
7
|
+
def initialize(text, klass_name)
|
8
|
+
@text, @klass_name = text, klass_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def result
|
12
|
+
raw_response = JSON.parse(@text)
|
13
|
+
Translator.translate_hash(raw_response, to: :en)
|
14
|
+
end
|
15
|
+
|
16
|
+
def records
|
17
|
+
raw_records = result[:return][plural_klass_name]
|
18
|
+
return [] if raw_records.nil?
|
19
|
+
raw_records.each_with_object([]) { |record, result| result << Record.new(record[@klass_name]) }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def plural_klass_name
|
25
|
+
@klass_name.to_s.pluralize.to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Bling
|
2
|
+
module API
|
3
|
+
# This class is used to make requests to all available
|
4
|
+
# actions related to products in Bling api. Is strongly recommended
|
5
|
+
# to use the Bling::API module wrapper
|
6
|
+
#
|
7
|
+
class Product < Request
|
8
|
+
|
9
|
+
# Get a list of available products
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# products = Bling::API::Product.new.list
|
13
|
+
#
|
14
|
+
# @return [[Record]] Call products index and return an array with records
|
15
|
+
#
|
16
|
+
def list
|
17
|
+
get_request(t_url(:products))
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get a specific object based on product_id
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# product = Bling::API::Product.new.get
|
24
|
+
#
|
25
|
+
# @param [Integer,String] product_id The product id in Bling
|
26
|
+
#
|
27
|
+
# @return [[Record]] Call product show and return an arrayn with one record
|
28
|
+
#
|
29
|
+
def get(product_id)
|
30
|
+
get_request(t_url(:product, product_id))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Insert a product in Bling
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# product = Bling::API::Product.new.create(
|
37
|
+
# {
|
38
|
+
# code: '92314',
|
39
|
+
# description: 'Awesome product',
|
40
|
+
# additional_description: 'In lovely colors',
|
41
|
+
# unit: 'Pc',
|
42
|
+
# price: 20.50,
|
43
|
+
# cost_price: 14.30,
|
44
|
+
# raw_weight: 2,
|
45
|
+
# weight: 1.8,
|
46
|
+
# tax_category: '1000.01.01',
|
47
|
+
# origin: 0,
|
48
|
+
# quantity: 10,
|
49
|
+
# gtin: 832222,
|
50
|
+
# gtin_package: 13414,
|
51
|
+
# width: 25,
|
52
|
+
# height: 15,
|
53
|
+
# depth: 10,
|
54
|
+
# min_quantity: 1,
|
55
|
+
# max_quantity: 200
|
56
|
+
# }
|
57
|
+
# )
|
58
|
+
#
|
59
|
+
# @param [Hash] params A hash with product options
|
60
|
+
#
|
61
|
+
# @return [[Record]] Call product show and return an array with one record
|
62
|
+
#
|
63
|
+
def create(params)
|
64
|
+
@params = params
|
65
|
+
post_request(t_url(:product), parsed_xml)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Update a product in Bling
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# product = Bling::API::Product.new.create(
|
72
|
+
# {
|
73
|
+
# code: '92314',
|
74
|
+
# description: 'Awesome product',
|
75
|
+
# additional_description: 'In lovely colors',
|
76
|
+
# unit: 'Pc',
|
77
|
+
# price: 20.50,
|
78
|
+
# cost_price: 14.30,
|
79
|
+
# raw_weight: 2,
|
80
|
+
# weight: 1.8,
|
81
|
+
# tax_category: '1000.01.01',
|
82
|
+
# origin: 0,
|
83
|
+
# quantity: 10,
|
84
|
+
# gtin: 832222,
|
85
|
+
# gtin_package: 13414,
|
86
|
+
# width: 25,
|
87
|
+
# height: 15,
|
88
|
+
# depth: 10,
|
89
|
+
# min_quantity: 1,
|
90
|
+
# max_quantity: 200
|
91
|
+
# }
|
92
|
+
# )
|
93
|
+
#
|
94
|
+
# @param [Integer, String] product_id The product id in Bling
|
95
|
+
# @param [Hash] params A hash with product options
|
96
|
+
#
|
97
|
+
# @return [[Record]] Call product show and return an array with one record
|
98
|
+
#
|
99
|
+
def update(product_id, params)
|
100
|
+
@params = params
|
101
|
+
post_request(t_url(:product, product_id), parsed_xml)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Delete a product in Bling
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# Bling::API::Product.new.delete(1)
|
108
|
+
#
|
109
|
+
# @param [Integer,String] product_id The product id in Bling
|
110
|
+
#
|
111
|
+
# @return [[Record]] Call product show and return an array with one record
|
112
|
+
#
|
113
|
+
def delete(product_id)
|
114
|
+
delete_request(t_url(:product, product_id))
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def parsed_xml
|
120
|
+
@params = translate(@params) unless Bling.config.default_language == :en
|
121
|
+
validate(@params, :description, :price)
|
122
|
+
build_missing_keys
|
123
|
+
product_template % @params
|
124
|
+
end
|
125
|
+
|
126
|
+
def build_missing_keys
|
127
|
+
xml_nodes.each do |node|
|
128
|
+
@params[node] = nil unless @params.has_key?(node)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def xml_nodes
|
133
|
+
%i(code description
|
134
|
+
additional_description unit price
|
135
|
+
cost_price raw_weight weight
|
136
|
+
tax_category origin quantity gtin
|
137
|
+
gtin_package width height depth
|
138
|
+
min_quantity max_quantity cest)
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bling
|
2
|
+
module API
|
3
|
+
class Record
|
4
|
+
|
5
|
+
# @return [Hash] The original hash attributes passed on initialize
|
6
|
+
attr_reader :attrs
|
7
|
+
|
8
|
+
# @params [Hash] A hash that will generate accessors for each key
|
9
|
+
def initialize(hash)
|
10
|
+
@attrs = hash
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def method_missing(method, *args, &block)
|
16
|
+
if attrs.has_key?(method)
|
17
|
+
attrs[method]
|
18
|
+
else
|
19
|
+
super(method, args, block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Bling
|
2
|
+
module API
|
3
|
+
class Request
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def get_request(url)
|
8
|
+
Client.new(url, {}, klass_name).get
|
9
|
+
end
|
10
|
+
|
11
|
+
def post_request(url, params)
|
12
|
+
Client.new(url, params, klass_name).post
|
13
|
+
end
|
14
|
+
|
15
|
+
def put_request(url, params)
|
16
|
+
Client.new(url, params, klass_name).post
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete_request(url)
|
20
|
+
Client.new(url, {}, klass_name).delete
|
21
|
+
end
|
22
|
+
|
23
|
+
def klass_name
|
24
|
+
self.class.to_s.split('::').last.downcase.to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
def t_url(url, id=nil)
|
28
|
+
Translator.translate_url(url, id: id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def translate(params)
|
32
|
+
Translator.translate_hash(params)
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate(params, *required_params)
|
36
|
+
required_params.each do |param|
|
37
|
+
if params[param].nil? || params[param] =~ /^\s*$/
|
38
|
+
raise ArgumentError, "The required parameter `:#{param}' is missing."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_missing(method, *args, &block)
|
44
|
+
if method.to_s =~/\A.+_template\z/
|
45
|
+
IO.read(File.join(File.dirname(__FILE__), "templates", method.to_s))
|
46
|
+
else
|
47
|
+
super(method, args, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Bling
|
2
|
+
module API
|
3
|
+
class Response
|
4
|
+
|
5
|
+
# @return [Net::HTTPResponse] The response object returned by Net::HTTP.
|
6
|
+
attr_reader :http_response
|
7
|
+
|
8
|
+
# @param [Net::HTTPResponse] http_response The response object returned by Net::HTTP.
|
9
|
+
def initialize(http_response, klass_name)
|
10
|
+
@http_response = http_response
|
11
|
+
@klass_name = klass_name
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] The raw body of the response object.
|
15
|
+
def body
|
16
|
+
@http_response.body
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Boolean] Whether or not the request was successful.
|
20
|
+
def success?
|
21
|
+
!http_failure? && without_errors
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] Whether or not the HTTP request was a success.
|
25
|
+
def http_failure?
|
26
|
+
!@http_response.is_a?(Net::HTTPSuccess)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Array] with parsed response body.
|
30
|
+
def records
|
31
|
+
parsed_response.records if success?
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def without_errors
|
37
|
+
parsed_response.result[:return] && parsed_response.result[:return][:errors].nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def parsed_response
|
41
|
+
@parsed_response ||= Parser.new(body, @klass_name)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<pedido>
|
3
|
+
<data>%{date}</data>
|
4
|
+
<numero>%{order_number}</numero>
|
5
|
+
<numero_loja>%{ecommerce_order_number}</numero_loja>
|
6
|
+
<loja>%{store}</loja>
|
7
|
+
<nat_operacao>%{selling_category}</nat_operacao>
|
8
|
+
<cliente>
|
9
|
+
<nome>%{name}</nome>
|
10
|
+
<tipoPessoa>%{tax_type}</tipoPessoa>
|
11
|
+
<endereco>%{address}</endereco>
|
12
|
+
<cpf_cnpj>%{document}</cpf_cnpj>
|
13
|
+
<ie_rg>%{ie_rg}</ie_rg>
|
14
|
+
<contribuinte>%{tax_classification}</contribuinte>
|
15
|
+
<numero>%{number}</numero>
|
16
|
+
<complemento>%{additional_address}</complemento>
|
17
|
+
<bairro>%{neighborhood}</bairro>
|
18
|
+
<cep>%{zipcode}</cep>
|
19
|
+
<cidade>%{city}</cidade>
|
20
|
+
<uf>%{state}</uf>
|
21
|
+
<fone>%{phone}</fone>
|
22
|
+
<celular>%{cellphone}</celular>
|
23
|
+
<email>%{email}</email>
|
24
|
+
</cliente>
|
25
|
+
<transporte>
|
26
|
+
<transportadora>%{carrier}</transportadora>
|
27
|
+
<tipo_frete>%{shipment_type}</tipo_frete>
|
28
|
+
<servico_correios>%{correios_service}</servico_correios>
|
29
|
+
<dados_etiqueta>
|
30
|
+
<nome>%{shipment_label_name}</nome>
|
31
|
+
<endereco>%{shipment_label_address}</endereco>
|
32
|
+
<numero>%{shipment_label_number}</numero>
|
33
|
+
<complemento>%{shipment_label_additional_address}</complemento>
|
34
|
+
<municipio>%{shipment_label_city}</municipio>
|
35
|
+
<uf>%{shipment_label_state}</uf>
|
36
|
+
<cep>%{shipment_label_zipcode}</cep>
|
37
|
+
<bairro>%{shipment_label_neighborhood}</bairro>
|
38
|
+
</dados_etiqueta>
|
39
|
+
</transporte>
|
40
|
+
<itens>%{items}</itens>
|
41
|
+
<parcelas>%{installments}</parcelas>
|
42
|
+
<vlr_frete>%{shipment_amount}</vlr_frete>
|
43
|
+
<vlr_desconto>%{discount_amount}</vlr_desconto>
|
44
|
+
<obs>%{additional_info}</obs>
|
45
|
+
<obs_internas>%{private_additional_info}</obs_internas>
|
46
|
+
</pedido>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<produto>
|
3
|
+
<codigo>%{code}</codigo>
|
4
|
+
<descricao>%{description}</descricao>
|
5
|
+
<descricaoComplementar>%{additional_description}</descricaoComplementar>
|
6
|
+
<un>%{unit}</un>
|
7
|
+
<vlr_unit>%{price}</vlr_unit>
|
8
|
+
<preco_custo>%{cost_price}</preco_custo>
|
9
|
+
<peso_bruto>%{raw_weight}</peso_bruto>
|
10
|
+
<peso_liq>%{weight}</peso_liq>
|
11
|
+
<class_fiscal>%{tax_category}</class_fiscal>
|
12
|
+
<origem>%{origin}</origem>
|
13
|
+
<estoque>%{quantity}</estoque>
|
14
|
+
<gtin>%{gtin}</gtin>
|
15
|
+
<gtinEmbalagem>%{gtin_package}</gtinEmbalagem>
|
16
|
+
<largura>%{width}</largura>
|
17
|
+
<altura>%{height}</altura>
|
18
|
+
<profundidade>%{depth}</profundidade>
|
19
|
+
<estoqueMinimo>%{min_quantity}</estoqueMinimo>
|
20
|
+
<estoqueMaximo>%{max_quantity}</estoqueMaximo>
|
21
|
+
<cest>%{cest}</cest>
|
22
|
+
</produto>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<contato>
|
3
|
+
<nome>%{:name}</nome>
|
4
|
+
<fantasia>%{:company_name}</fantasia>
|
5
|
+
<tipoPessoa>%{:tax_type}</tipoPessoa>
|
6
|
+
<cpf_cnpj>%{:document}</cpf_cnpj>
|
7
|
+
<ie_rg>%{:ie_rg}</ie_rg>
|
8
|
+
<endereco>%{:address}</endereco>
|
9
|
+
<numero>%{:number}</numero>
|
10
|
+
<complemento>%{:additional_address}</complemento>
|
11
|
+
<bairro>%{:neighborhood}</bairro>
|
12
|
+
<cep>%{:zipcode}</cep>
|
13
|
+
<cidade>%{:city}</cidade>
|
14
|
+
<uf>%{:state}</uf>
|
15
|
+
<fone>%{:phone}</fone>
|
16
|
+
<email>%{:email}</email>
|
17
|
+
</contato>
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Bling
|
4
|
+
module API
|
5
|
+
class Translator
|
6
|
+
|
7
|
+
I18N_FILE ||= YAML
|
8
|
+
.load_file(File.join(File.dirname(__FILE__), 'locales/translations.yml'))
|
9
|
+
.each_with_object({}){|(k,v), result| result[k.to_sym] = v.to_sym}
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def translate_url(key, id: nil)
|
13
|
+
url = pt[key.to_sym].to_s
|
14
|
+
url +="/#{id}" if id
|
15
|
+
url
|
16
|
+
end
|
17
|
+
|
18
|
+
def translate_hash(hash, to: :en)
|
19
|
+
conversion_hash = send(to)
|
20
|
+
|
21
|
+
hash.inject({}) do |result, (key, value)|
|
22
|
+
new_key = conversion_hash[underscore_symbol(key)]
|
23
|
+
new_key = underscore_symbol(key) if new_key.nil?
|
24
|
+
|
25
|
+
new_value = case value
|
26
|
+
when Hash then translate_hash(value, to: to)
|
27
|
+
when Array then value.map! { |v| translate_hash(v, to: to) }
|
28
|
+
else value
|
29
|
+
end
|
30
|
+
|
31
|
+
result[new_key] = new_value
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def underscore_symbol(key)
|
39
|
+
key.to_s.
|
40
|
+
gsub(/::/, '/').
|
41
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
42
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
43
|
+
tr("-", "_").
|
44
|
+
downcase.
|
45
|
+
to_sym
|
46
|
+
end
|
47
|
+
|
48
|
+
def en
|
49
|
+
I18N_FILE
|
50
|
+
end
|
51
|
+
|
52
|
+
def pt
|
53
|
+
@pt ||= I18N_FILE
|
54
|
+
.dup
|
55
|
+
.each_with_object({}) { |(key, value), result| result[value] = key }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|