bling-ruby-api 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 +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
|