solidus_bling 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +53 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +1 -0
  5. data/.github_changelog_generator +2 -0
  6. data/.gitignore +21 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +5 -0
  9. data/CHANGELOG.md +5 -0
  10. data/Gemfile +48 -0
  11. data/LICENSE +26 -0
  12. data/README.md +73 -0
  13. data/Rakefile +7 -0
  14. data/app/assets/javascripts/spree/backend/solidus_bling.js +2 -0
  15. data/app/assets/javascripts/spree/frontend/solidus_bling.js +2 -0
  16. data/app/assets/stylesheets/spree/backend/solidus_bling.css +4 -0
  17. data/app/assets/stylesheets/spree/frontend/solidus_bling.css +4 -0
  18. data/app/jobs/application_job.rb +7 -0
  19. data/app/jobs/erp_contact_job.rb +18 -0
  20. data/app/jobs/erp_order_job.rb +20 -0
  21. data/app/jobs/erp_product_job.rb +11 -0
  22. data/app/models/bling/api.rb +56 -0
  23. data/app/models/bling/contato.rb +83 -0
  24. data/app/models/bling/pedido.rb +131 -0
  25. data/app/models/bling/produto.rb +33 -0
  26. data/app/models/erp_account.rb +3 -0
  27. data/app/models/erp_event.rb +2 -0
  28. data/app/models/erp_product.rb +3 -0
  29. data/app/subscribers/bling/subscriber.rb +39 -0
  30. data/bin/console +17 -0
  31. data/bin/rails +7 -0
  32. data/bin/rails-engine +13 -0
  33. data/bin/rails-sandbox +16 -0
  34. data/bin/rake +7 -0
  35. data/bin/sandbox +78 -0
  36. data/bin/setup +8 -0
  37. data/config/initializers/solidus_bling.rb +11 -0
  38. data/config/locales/en.yml +5 -0
  39. data/config/routes.rb +5 -0
  40. data/db/migrate/20230906141624_create_erp_accounts.rb +17 -0
  41. data/db/migrate/20230906141728_add_erp_id_to_spree_orders.rb +6 -0
  42. data/db/migrate/20230906142108_create_erp_products.rb +12 -0
  43. data/db/migrate/20230906142806_create_erp_events.rb +14 -0
  44. data/db/migrate/20230908145050_add_number_district_to_addresses.rb +6 -0
  45. data/lib/generators/solidus_bling/install/install_generator.rb +41 -0
  46. data/lib/generators/solidus_bling/install/templates/initializer.rb +11 -0
  47. data/lib/solidus_bling/configuration.rb +19 -0
  48. data/lib/solidus_bling/engine.rb +26 -0
  49. data/lib/solidus_bling/testing_support/factories.rb +4 -0
  50. data/lib/solidus_bling/version.rb +5 -0
  51. data/lib/solidus_bling.rb +6 -0
  52. data/solidus_bling.gemspec +36 -0
  53. data/spec/jobs/erp_contact_job_spec.rb +5 -0
  54. data/spec/jobs/erp_order_job_spec.rb +5 -0
  55. data/spec/jobs/erp_product_job_spec.rb +5 -0
  56. data/spec/models/erp_account_spec.rb +5 -0
  57. data/spec/models/erp_event_spec.rb +5 -0
  58. data/spec/models/erp_product_spec.rb +5 -0
  59. data/spec/spec_helper.rb +32 -0
  60. 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
@@ -0,0 +1,5 @@
1
+ bump:
2
+ recurse: false
3
+ file: 'lib/solidus_bling/version.rb'
4
+ message: Bump SolidusBling to %{version}
5
+ tag: true
data/.github/stale.yml ADDED
@@ -0,0 +1 @@
1
+ _extends: .github
@@ -0,0 +1,2 @@
1
+ issues=false
2
+ exclude-labels=infrastructure
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ \#*
3
+ *~
4
+ .#*
5
+ .DS_Store
6
+ .idea
7
+ .project
8
+ .sass-cache
9
+ coverage
10
+ Gemfile.lock
11
+ Gemfile-local
12
+ tmp
13
+ nbproject
14
+ pkg
15
+ *.swp
16
+ spec/dummy
17
+ spec/examples.txt
18
+ /sandbox
19
+ .rvmrc
20
+ .ruby-version
21
+ .ruby-gemset
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ require:
2
+ - solidus_dev_support/rubocop
3
+
4
+ AllCops:
5
+ NewCops: disable
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+
4
+
5
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
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
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require 'solidus_dev_support/rake_tasks'
5
+ SolidusDevSupport::RakeTasks.install
6
+
7
+ task default: 'extension:specs'
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/backend/all.js'
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js'
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/frontend/all.css'
4
+ */
@@ -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
@@ -0,0 +1,3 @@
1
+ class ErpAccount < ApplicationRecord
2
+ has_many :products, foreign_key: 'erp_account_id', class_name: 'ErpProduct'
3
+ end
@@ -0,0 +1,2 @@
1
+ class ErpEvent < ApplicationRecord
2
+ end
@@ -0,0 +1,3 @@
1
+ class ErpProduct < ApplicationRecord
2
+ belongs_to :erp_account
3
+ end