solidus_bling 1.0.0

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.
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