solidus_bling 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +53 -0
- data/.gem_release.yml +5 -0
- data/.github/stale.yml +1 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +5 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +48 -0
- data/LICENSE +26 -0
- data/README.md +73 -0
- data/Rakefile +7 -0
- data/app/assets/javascripts/spree/backend/solidus_bling.js +2 -0
- data/app/assets/javascripts/spree/frontend/solidus_bling.js +2 -0
- data/app/assets/stylesheets/spree/backend/solidus_bling.css +4 -0
- data/app/assets/stylesheets/spree/frontend/solidus_bling.css +4 -0
- data/app/jobs/application_job.rb +7 -0
- data/app/jobs/erp_contact_job.rb +18 -0
- data/app/jobs/erp_order_job.rb +20 -0
- data/app/jobs/erp_product_job.rb +11 -0
- data/app/models/bling/api.rb +56 -0
- data/app/models/bling/contato.rb +83 -0
- data/app/models/bling/pedido.rb +131 -0
- data/app/models/bling/produto.rb +33 -0
- data/app/models/erp_account.rb +3 -0
- data/app/models/erp_event.rb +2 -0
- data/app/models/erp_product.rb +3 -0
- data/app/subscribers/bling/subscriber.rb +39 -0
- data/bin/console +17 -0
- data/bin/rails +7 -0
- data/bin/rails-engine +13 -0
- data/bin/rails-sandbox +16 -0
- data/bin/rake +7 -0
- data/bin/sandbox +78 -0
- data/bin/setup +8 -0
- data/config/initializers/solidus_bling.rb +11 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20230906141624_create_erp_accounts.rb +17 -0
- data/db/migrate/20230906141728_add_erp_id_to_spree_orders.rb +6 -0
- data/db/migrate/20230906142108_create_erp_products.rb +12 -0
- data/db/migrate/20230906142806_create_erp_events.rb +14 -0
- data/db/migrate/20230908145050_add_number_district_to_addresses.rb +6 -0
- data/lib/generators/solidus_bling/install/install_generator.rb +41 -0
- data/lib/generators/solidus_bling/install/templates/initializer.rb +11 -0
- data/lib/solidus_bling/configuration.rb +19 -0
- data/lib/solidus_bling/engine.rb +26 -0
- data/lib/solidus_bling/testing_support/factories.rb +4 -0
- data/lib/solidus_bling/version.rb +5 -0
- data/lib/solidus_bling.rb +6 -0
- data/solidus_bling.gemspec +36 -0
- data/spec/jobs/erp_contact_job_spec.rb +5 -0
- data/spec/jobs/erp_order_job_spec.rb +5 -0
- data/spec/jobs/erp_product_job_spec.rb +5 -0
- data/spec/models/erp_account_spec.rb +5 -0
- data/spec/models/erp_event_spec.rb +5 -0
- data/spec/models/erp_product_spec.rb +5 -0
- data/spec/spec_helper.rb +32 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a155fcd0164de40b6427c56c654d7421abe29f0950087e261ef344cab588dfcc
|
4
|
+
data.tar.gz: 2b1379be0f430857f42c2efb05dfe3ae2c8aa51acd153973fed3d7715e1a39e6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b82ef68e8f8dd7200eff20528ab256ddc7cbec38416c57a7595faed41f9aaabfea389837dac2383db5df2e6551a1af946b13424e753ab0570926d9b1ec4ed0f5
|
7
|
+
data.tar.gz: 741773e5b6d7d13fd90b1b2ed68909254c643cf0878a1ea55d38ee7c581dbaead123037be78ddd9cb15f0382126130881d30d4e3d6cd715159a370b35e7e71c5
|
@@ -0,0 +1,53 @@
|
|
1
|
+
version: 2.1
|
2
|
+
|
3
|
+
orbs:
|
4
|
+
# Required for feature specs.
|
5
|
+
browser-tools: circleci/browser-tools@1.1
|
6
|
+
|
7
|
+
# Always take the latest version of the orb, this allows us to
|
8
|
+
# run specs against Solidus supported versions only without the need
|
9
|
+
# to change this configuration every time a Solidus version is released
|
10
|
+
# or goes EOL.
|
11
|
+
solidusio_extensions: solidusio/extensions@volatile
|
12
|
+
|
13
|
+
jobs:
|
14
|
+
run-specs-with-sqlite:
|
15
|
+
executor: solidusio_extensions/sqlite
|
16
|
+
steps:
|
17
|
+
- browser-tools/install-chrome
|
18
|
+
- solidusio_extensions/run-tests
|
19
|
+
run-specs-with-postgres:
|
20
|
+
executor: solidusio_extensions/postgres
|
21
|
+
steps:
|
22
|
+
- browser-tools/install-chrome
|
23
|
+
- solidusio_extensions/run-tests
|
24
|
+
run-specs-with-mysql:
|
25
|
+
executor: solidusio_extensions/mysql
|
26
|
+
steps:
|
27
|
+
- browser-tools/install-chrome
|
28
|
+
- solidusio_extensions/run-tests
|
29
|
+
lint-code:
|
30
|
+
executor: solidusio_extensions/sqlite-memory
|
31
|
+
steps:
|
32
|
+
- solidusio_extensions/lint-code
|
33
|
+
|
34
|
+
workflows:
|
35
|
+
"Run specs on supported Solidus versions":
|
36
|
+
jobs:
|
37
|
+
- run-specs-with-sqlite
|
38
|
+
- run-specs-with-postgres
|
39
|
+
- run-specs-with-mysql
|
40
|
+
- lint-code
|
41
|
+
|
42
|
+
"Weekly run specs against main":
|
43
|
+
triggers:
|
44
|
+
- schedule:
|
45
|
+
cron: "0 0 * * 4" # every Thursday
|
46
|
+
filters:
|
47
|
+
branches:
|
48
|
+
only:
|
49
|
+
- main
|
50
|
+
jobs:
|
51
|
+
- run-specs-with-sqlite
|
52
|
+
- run-specs-with-postgres
|
53
|
+
- run-specs-with-mysql
|
data/.gem_release.yml
ADDED
data/.github/stale.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
_extends: .github
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
5
|
+
|
6
|
+
branch = ENV.fetch('SOLIDUS_BRANCH', 'main')
|
7
|
+
gem 'solidus', github: 'solidusio/solidus', branch: branch
|
8
|
+
|
9
|
+
# The solidus_frontend gem has been pulled out since v3.2
|
10
|
+
if branch >= 'v3.2'
|
11
|
+
gem 'solidus_frontend'
|
12
|
+
elsif branch == 'main'
|
13
|
+
gem 'solidus_frontend', github: 'solidusio/solidus_frontend'
|
14
|
+
else
|
15
|
+
gem 'solidus_frontend', github: 'solidusio/solidus', branch: branch
|
16
|
+
end
|
17
|
+
|
18
|
+
# Needed to help Bundler figure out how to resolve dependencies,
|
19
|
+
# otherwise it takes forever to resolve them.
|
20
|
+
# See https://github.com/bundler/bundler/issues/6677
|
21
|
+
gem 'rails', '>0.a'
|
22
|
+
|
23
|
+
|
24
|
+
# Provides basic authentication functionality for testing parts of your engine
|
25
|
+
gem 'solidus_auth_devise'
|
26
|
+
|
27
|
+
case ENV.fetch('DB', nil)
|
28
|
+
when 'mysql'
|
29
|
+
gem 'mysql2'
|
30
|
+
when 'postgresql'
|
31
|
+
gem 'pg'
|
32
|
+
else
|
33
|
+
gem 'sqlite3'
|
34
|
+
end
|
35
|
+
|
36
|
+
# While we still support Ruby < 3 we need to workaround a limitation in
|
37
|
+
# the 'async' gem that relies on the latest ruby, since RubyGems doesn't
|
38
|
+
# resolve gems based on the required ruby version.
|
39
|
+
gem 'async', '< 3' if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3')
|
40
|
+
|
41
|
+
gemspec
|
42
|
+
|
43
|
+
# Use a local Gemfile to include development dependencies that might not be
|
44
|
+
# relevant for the project or for other contributors, e.g. pry-byebug.
|
45
|
+
#
|
46
|
+
# We use `send` instead of calling `eval_gemfile` to work around an issue with
|
47
|
+
# how Dependabot parses projects: https://github.com/dependabot/dependabot-core/issues/1658.
|
48
|
+
send(:eval_gemfile, 'Gemfile-local') if File.exist? 'Gemfile-local'
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2023 ulysses-bull
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
* Neither the name Solidus nor the names of its contributors may be used to
|
13
|
+
endorse or promote products derived from this software without specific
|
14
|
+
prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
20
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
21
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
22
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
23
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
24
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
26
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Solidus Bling
|
2
|
+
|
3
|
+
[![CircleCI](https://circleci.com/gh/solidusio-contrib/solidus_bling.svg?style=shield)](https://circleci.com/gh/solidusio-contrib/solidus_bling)
|
4
|
+
[![codecov](https://codecov.io/gh/solidusio-contrib/solidus_bling/branch/master/graph/badge.svg)](https://codecov.io/gh/solidusio-contrib/solidus_bling)
|
5
|
+
|
6
|
+
<!-- Explain what your extension does. -->
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add solidus_bling to your Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'solidus_bling'
|
14
|
+
```
|
15
|
+
|
16
|
+
Bundle your dependencies and run the installation generator:
|
17
|
+
|
18
|
+
```shell
|
19
|
+
bin/rails generate solidus_bling:install
|
20
|
+
```
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
<!-- Explain how to use your extension once it's been installed. -->
|
25
|
+
|
26
|
+
## Development
|
27
|
+
|
28
|
+
### Testing the extension
|
29
|
+
|
30
|
+
First bundle your dependencies, then run `bin/rake`. `bin/rake` will default to building the dummy
|
31
|
+
app if it does not exist, then it will run specs. The dummy app can be regenerated by using
|
32
|
+
`bin/rake extension:test_app`.
|
33
|
+
|
34
|
+
```shell
|
35
|
+
bin/rake
|
36
|
+
```
|
37
|
+
|
38
|
+
To run [Rubocop](https://github.com/bbatsov/rubocop) static code analysis run
|
39
|
+
|
40
|
+
```shell
|
41
|
+
bundle exec rubocop
|
42
|
+
```
|
43
|
+
|
44
|
+
When testing your application's integration with this extension you may use its factories.
|
45
|
+
You can load Solidus core factories along with this extension's factories using this statement:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
SolidusDevSupport::TestingSupport::Factories.load_for(SolidusBling::Engine)
|
49
|
+
```
|
50
|
+
|
51
|
+
### Running the sandbox
|
52
|
+
|
53
|
+
To run this extension in a sandboxed Solidus application, you can run `bin/sandbox`. The path for
|
54
|
+
the sandbox app is `./sandbox` and `bin/rails` will forward any Rails commands to
|
55
|
+
`sandbox/bin/rails`.
|
56
|
+
|
57
|
+
Here's an example:
|
58
|
+
|
59
|
+
```
|
60
|
+
$ bin/rails server
|
61
|
+
=> Booting Puma
|
62
|
+
=> Rails 6.0.2.1 application starting in development
|
63
|
+
* Listening on tcp://127.0.0.1:3000
|
64
|
+
Use Ctrl-C to stop
|
65
|
+
```
|
66
|
+
|
67
|
+
### Releasing new versions
|
68
|
+
|
69
|
+
Please refer to the [dedicated page](https://github.com/solidusio/solidus/wiki/How-to-release-extensions) in the Solidus wiki.
|
70
|
+
|
71
|
+
## License
|
72
|
+
|
73
|
+
Copyright (c) 2023 ulysses-bull, released under the New BSD License.
|
data/Rakefile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
class ApplicationJob < ActiveJob::Base
|
2
|
+
# Automatically retry jobs that encountered a deadlock
|
3
|
+
# retry_on ActiveRecord::Deadlocked
|
4
|
+
|
5
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
6
|
+
# discard_on ActiveJob::DeserializationError
|
7
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class ErpContactJob < ApplicationJob
|
2
|
+
queue_as :default
|
3
|
+
|
4
|
+
def perform order:, contact:, event_name:, method_name:
|
5
|
+
erp_event = ErpEvent.new
|
6
|
+
erp_event.internal_id = order.id
|
7
|
+
erp_event.name = event_name
|
8
|
+
erp_event.method = method_name
|
9
|
+
if contact.include? "error"
|
10
|
+
erp_event.status = 'error'
|
11
|
+
erp_event.message = contact["error"]["description"]
|
12
|
+
else
|
13
|
+
erp_event.status = 'success'
|
14
|
+
end
|
15
|
+
erp_event.body = order.to_json
|
16
|
+
erp_event.save!
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class ErpOrderJob < ApplicationJob
|
2
|
+
queue_as :default
|
3
|
+
|
4
|
+
def perform order:, event_name:, method_name:
|
5
|
+
erp_event = ErpEvent.new
|
6
|
+
erp_event.internal_id = order.id
|
7
|
+
erp_event.name = event_name
|
8
|
+
erp_event.method = method_name
|
9
|
+
begin
|
10
|
+
Bling::Pedido.new(order).send
|
11
|
+
erp_event.status = 'success'
|
12
|
+
rescue => e
|
13
|
+
erp_event.status = 'error'
|
14
|
+
erp_event.message = e.to_json
|
15
|
+
ensure
|
16
|
+
erp_event.body = order.to_json
|
17
|
+
erp_event.save!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class ErpProductJob < ApplicationJob
|
2
|
+
queue_as :default
|
3
|
+
|
4
|
+
def perform products:, event_name:, method_name:
|
5
|
+
erp_event = ErpEvent.new
|
6
|
+
erp_event.name = event_name
|
7
|
+
erp_event.method = method_name
|
8
|
+
erp_event.body = products.to_json
|
9
|
+
erp_event.save!
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Bling
|
2
|
+
class Api
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
app_name = SolidusBling.config.app_name
|
6
|
+
@account = find_account(app_name)
|
7
|
+
verify_token
|
8
|
+
@headers = {
|
9
|
+
"Content-Type": "application/json",
|
10
|
+
"Authorization": "Bearer #{@account.access_token}"
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def refresh_token
|
15
|
+
basic_encoded = Base64.strict_encode64("#{@account.client_id}:#{@account.client_secret}")
|
16
|
+
headers = {
|
17
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
18
|
+
"Authorization": "Basic #{basic_encoded}"
|
19
|
+
}
|
20
|
+
body = "grant_type=refresh_token&refresh_token=#{@account.refresh_token}"
|
21
|
+
res = ::Typhoeus.post("#{@account.api_base_url}/oauth/token", headers: headers, body: body)
|
22
|
+
body = JSON.parse(res.body)
|
23
|
+
expire_datetime = res.headers["date"].to_datetime.utc + body["expires_in"].seconds
|
24
|
+
@account.update(
|
25
|
+
access_token: body["access_token"],
|
26
|
+
refresh_token: body["refresh_token"],
|
27
|
+
token_expires_in: expire_datetime
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def need_refresh_token?
|
32
|
+
return true if @account.token_expires_in.nil?
|
33
|
+
DateTime.now.utc > (@account.token_expires_in - 2.hours)
|
34
|
+
end
|
35
|
+
|
36
|
+
def verify_token
|
37
|
+
refresh_token if need_refresh_token?
|
38
|
+
end
|
39
|
+
|
40
|
+
def response_has_error? json
|
41
|
+
if json.include? "error"
|
42
|
+
error = json["error"]
|
43
|
+
raise "ERROR: #{json}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def find_account app_name
|
50
|
+
account = ErpAccount.find_by(app_name: app_name)
|
51
|
+
raise 'Conta de aplicativo inexistente' if account.nil?
|
52
|
+
account
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Bling
|
2
|
+
class Contato < Bling::Api
|
3
|
+
|
4
|
+
attr_reader :info
|
5
|
+
|
6
|
+
def initialize order
|
7
|
+
super()
|
8
|
+
@info = find_or_create(order)
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_or_create order
|
12
|
+
raise "Order com CPF/CNPJ vazio" if order.cpf_cnpj.nil?
|
13
|
+
contato = find_by_cpf(order.cpf_cnpj)
|
14
|
+
if contato.nil?
|
15
|
+
contato_external_id = create(order)
|
16
|
+
contato = {"id" => contato_external_id}
|
17
|
+
end
|
18
|
+
contato
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_by_cpf cpf_cnpj
|
22
|
+
req = Typhoeus.get("#{@account.api_base_url}/contatos?numeroDocumento=#{cpf_cnpj.gsub(/\D/, "")}", headers: @headers)
|
23
|
+
json = JSON.parse(req.body)
|
24
|
+
response_has_error?(json)
|
25
|
+
user = {}
|
26
|
+
if json.include? "data" || !json["data"].empty?
|
27
|
+
user = json["data"].first
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def create order
|
32
|
+
contato = build_hash_contato(order)
|
33
|
+
begin
|
34
|
+
attempts ||= 1
|
35
|
+
req = Typhoeus.post("#{@account.api_base_url}/contatos", headers: @headers, body: JSON.dump(contato))
|
36
|
+
raise if req.code == 429
|
37
|
+
rescue
|
38
|
+
retry if (attempts += 1) < 5
|
39
|
+
ensure
|
40
|
+
json = JSON.parse(req.body)
|
41
|
+
response_has_error?(json)
|
42
|
+
Spree::Bus.publish :erp_contato_created, args: {order: order, contato: json}
|
43
|
+
json.dig("data", "id")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def tipo_pessoa doc
|
50
|
+
doc.length > 11 ? "J" : "F"
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_hash_contato order
|
54
|
+
{
|
55
|
+
"nome": order.ship_address.name,
|
56
|
+
"telefone": order.ship_address.phone,
|
57
|
+
"tipo": tipo_pessoa(order.cpf_cnpj),
|
58
|
+
"numeroDocumento": order.cpf_cnpj,
|
59
|
+
"email": order.email,
|
60
|
+
"endereco": {
|
61
|
+
"geral": {
|
62
|
+
"endereco": order.bill_address.address1,
|
63
|
+
"cep": order.bill_address.zipcode,
|
64
|
+
"bairro": order.bill_address.district,
|
65
|
+
"municipio": order.bill_address.city,
|
66
|
+
"uf": order.bill_address.state.abbr,
|
67
|
+
"numero": order.bill_address.number,
|
68
|
+
"complemento": order.bill_address.address2
|
69
|
+
},
|
70
|
+
"cobranca": {
|
71
|
+
"endereco": order.bill_address.address1,
|
72
|
+
"cep": order.bill_address.zipcode,
|
73
|
+
"bairro": order.bill_address.district,
|
74
|
+
"municipio": order.bill_address.city,
|
75
|
+
"uf": order.bill_address.state.abbr,
|
76
|
+
"numero": order.bill_address.number,
|
77
|
+
"complemento": order.bill_address.address2
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module Bling
|
2
|
+
class Pedido < Bling::Api
|
3
|
+
|
4
|
+
ID_LOJA_BLING = SolidusBling.config.store_id.to_i || 204601505
|
5
|
+
ID_VENDEDOR_BLING = SolidusBling.config.seller_id.to_i || 15596272829
|
6
|
+
ID_FORMA_PGTO_BLING = SolidusBling.config.payment_type_id.to_i || 4484076
|
7
|
+
ID_CAT_RECEITA_BLING = SolidusBling.config.category_id.to_i || 14653794318
|
8
|
+
|
9
|
+
def initialize order
|
10
|
+
super()
|
11
|
+
@order = order
|
12
|
+
end
|
13
|
+
|
14
|
+
def send
|
15
|
+
if @order.present?
|
16
|
+
contato = Bling::Contato.new(@order)
|
17
|
+
hash_order = build_hash_order(@order, contato.info)
|
18
|
+
begin
|
19
|
+
attempts ||= 1
|
20
|
+
req = Typhoeus.post("#{@account.api_base_url}/pedidos/vendas", headers: @headers, body: JSON.dump(hash_order))
|
21
|
+
raise if req.code == 429
|
22
|
+
rescue
|
23
|
+
retry if (attempts += 1) < 5
|
24
|
+
ensure
|
25
|
+
json = JSON.parse(req.body)
|
26
|
+
response_has_error?(json)
|
27
|
+
if json.include? "data"
|
28
|
+
@order.erp_order_id = json["data"]["id"]
|
29
|
+
@order.save
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def build_hash_order order, contato
|
38
|
+
{
|
39
|
+
"numeroLoja": order.number,
|
40
|
+
"data": order.completed_at.strftime("%Y-%m-%d"),
|
41
|
+
"loja": {
|
42
|
+
"id": ID_LOJA_BLING
|
43
|
+
},
|
44
|
+
"contato": {
|
45
|
+
"id": contato["id"]
|
46
|
+
},
|
47
|
+
"itens": build_array_items(order.line_items),
|
48
|
+
"vendedor": {
|
49
|
+
"id": ID_VENDEDOR_BLING
|
50
|
+
},
|
51
|
+
"transporte": build_hash_transporte(order),
|
52
|
+
"parcelas": build_array_parcelas(order),
|
53
|
+
"desconto": {
|
54
|
+
"valor": order.promo_total&.to_f.abs || 0,
|
55
|
+
"unidade": "REAL"
|
56
|
+
},
|
57
|
+
"totalProdutos": order.item_total.to_f,
|
58
|
+
"total": order.total.to_f,
|
59
|
+
"observacoes": " Nº Pedido Loja: #{order.number}",
|
60
|
+
"observacoesInternas": "Pagamento: #{@parcelas}x R$ #{(order.total.to_f / @parcelas).round(2)}\nForma de envio: #{order.shipments.last.selected_shipping_rate.name}\n#{build_cupons(order.promotions)}",
|
61
|
+
"categoria": {
|
62
|
+
"id": ID_CAT_RECEITA_BLING
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_array_items items
|
68
|
+
items.map do |item|
|
69
|
+
erp_product = ErpProduct.find_by(sku: item.sku)
|
70
|
+
{
|
71
|
+
"id": erp_product.external_id,
|
72
|
+
"quantidade": item.quantity,
|
73
|
+
"valor": item.price.to_f,
|
74
|
+
"produto": {
|
75
|
+
"id": erp_product.external_id
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_array_parcelas order
|
82
|
+
@parcelas = 1
|
83
|
+
if order.payments.last.source
|
84
|
+
@parcelas = order.payments.last.source.try(:installments) || 1
|
85
|
+
end
|
86
|
+
valor_parcela = order.total.to_f / @parcelas
|
87
|
+
1.upto(@parcelas).map do |parcela|
|
88
|
+
{
|
89
|
+
"dataVencimento": (order.completed_at + parcela.months).strftime("%Y-%m-%d"),
|
90
|
+
"valor": valor_parcela,
|
91
|
+
"formaPagamento": {
|
92
|
+
"id": ID_FORMA_PGTO_BLING
|
93
|
+
}
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def build_hash_transporte order
|
99
|
+
{
|
100
|
+
"fretePorConta": 1,
|
101
|
+
"frete": order.shipment_total.to_f,
|
102
|
+
"quantidadeVolumes": 1,
|
103
|
+
"etiqueta": {
|
104
|
+
"nome": order.ship_address.name,
|
105
|
+
"endereco": order.ship_address.address1,
|
106
|
+
"numero": order.ship_address.number,
|
107
|
+
"complemento": order.ship_address.address2,
|
108
|
+
"municipio": order.ship_address.city,
|
109
|
+
"uf": order.ship_address.state.abbr,
|
110
|
+
"bairro": order.ship_address.district,
|
111
|
+
"cep": order.ship_address.zipcode,
|
112
|
+
"nomePais": "Brasil"
|
113
|
+
},
|
114
|
+
"volumes": [
|
115
|
+
{
|
116
|
+
"servico": order.shipments.last.selected_shipping_rate.name,
|
117
|
+
"codigoRastreamento": ""
|
118
|
+
}
|
119
|
+
]
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
def build_cupons cupons
|
124
|
+
string_cupons = ''
|
125
|
+
if !cupons.empty?
|
126
|
+
string_cupons = "Cupons utilizados: #{cupons.pluck(:name).join(", ")}"
|
127
|
+
end
|
128
|
+
string_cupons
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Bling
|
2
|
+
class Produto < Bling::Api
|
3
|
+
|
4
|
+
def update
|
5
|
+
skus = Spree::Variant.pluck(:sku)
|
6
|
+
hash_products = {}
|
7
|
+
products = skus.map do |sku|
|
8
|
+
begin
|
9
|
+
attempts ||= 1
|
10
|
+
req = Typhoeus.get("#{@account.api_base_url}/produtos?codigo=#{sku}", headers: @headers)
|
11
|
+
res_json = JSON.parse(req.body)
|
12
|
+
p "#{sku} => #{res_json}"
|
13
|
+
next if res_json["data"] == []
|
14
|
+
product = res_json["data"].first
|
15
|
+
hash_products[sku] = product["id"]
|
16
|
+
{
|
17
|
+
erp_account_id: @account.id,
|
18
|
+
sku: sku,
|
19
|
+
external_id: product["id"].to_s,
|
20
|
+
name: product["nome"]
|
21
|
+
}
|
22
|
+
rescue => e
|
23
|
+
hash_products[sku] = res_json
|
24
|
+
p "#{sku} => #{res_json} => #{e}"
|
25
|
+
sleep 1
|
26
|
+
retry if (attempts += 1) < 5
|
27
|
+
end
|
28
|
+
end.compact.uniq
|
29
|
+
ErpProduct.upsert_all(products, unique_by: :sku)
|
30
|
+
Spree::Bus.publish :erp_products_updated, products: hash_products
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|