solidus_bactracs 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.circleci/config.yml +41 -0
  4. data/.gem_release.yml +5 -0
  5. data/.github/stale.yml +17 -0
  6. data/.github_changelog_generator +2 -0
  7. data/.gitignore +22 -0
  8. data/.rspec +2 -0
  9. data/.rubocop.yml +14 -0
  10. data/.rubocop_todo.yml +40 -0
  11. data/CHANGELOG.md +16 -0
  12. data/Gemfile +33 -0
  13. data/LICENSE +26 -0
  14. data/README.md +216 -0
  15. data/Rakefile +6 -0
  16. data/app/assets/javascripts/spree/backend/solidus_backtracs.js +2 -0
  17. data/app/assets/javascripts/spree/frontend/solidus_backtracs.js +2 -0
  18. data/app/assets/stylesheets/spree/backend/solidus_backtracs.css +4 -0
  19. data/app/assets/stylesheets/spree/frontend/solidus_backtracs.css +4 -0
  20. data/app/controllers/spree/backtracs_controller.rb +46 -0
  21. data/app/decorators/models/solidus_backtracs/spree/shipment_decorator.rb +33 -0
  22. data/app/helpers/solidus_backtracs/export_helper.rb +60 -0
  23. data/app/jobs/solidus_backtracs/api/schedule_shipment_syncs_job.rb +36 -0
  24. data/app/jobs/solidus_backtracs/api/sync_shipment_job.rb +17 -0
  25. data/app/jobs/solidus_backtracs/api/sync_shipments_job.rb +41 -0
  26. data/app/queries/solidus_backtracs/shipment/between_query.rb +14 -0
  27. data/app/queries/solidus_backtracs/shipment/exportable_query.rb +24 -0
  28. data/app/queries/solidus_backtracs/shipment/pending_api_sync_query.rb +51 -0
  29. data/app/views/spree/backtracs/export.xml.builder +58 -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 +86 -0
  36. data/bin/setup +8 -0
  37. data/config/locales/en.yml +5 -0
  38. data/config/routes.rb +6 -0
  39. data/db/migrate/20210220093010_add_backtracs_api_sync_fields.rb +8 -0
  40. data/lib/generators/solidus_backtracs/install/install_generator.rb +27 -0
  41. data/lib/generators/solidus_backtracs/install/templates/initializer.rb +92 -0
  42. data/lib/solidus_backtracs/api/batch_syncer.rb +45 -0
  43. data/lib/solidus_backtracs/api/client.rb +36 -0
  44. data/lib/solidus_backtracs/api/rate_limited_error.rb +23 -0
  45. data/lib/solidus_backtracs/api/request_error.rb +33 -0
  46. data/lib/solidus_backtracs/api/request_runner.rb +109 -0
  47. data/lib/solidus_backtracs/api/shipment_serializer.rb +103 -0
  48. data/lib/solidus_backtracs/api/threshold_verifier.rb +28 -0
  49. data/lib/solidus_backtracs/configuration.rb +63 -0
  50. data/lib/solidus_backtracs/engine.rb +19 -0
  51. data/lib/solidus_backtracs/errors.rb +23 -0
  52. data/lib/solidus_backtracs/shipment_notice.rb +58 -0
  53. data/lib/solidus_backtracs/testing_support/factories.rb +4 -0
  54. data/lib/solidus_backtracs/version.rb +5 -0
  55. data/lib/solidus_backtracs.rb +16 -0
  56. data/solidus_bactracs.gemspec +39 -0
  57. data/spec/controllers/spree/backtracs_controller_spec.rb +103 -0
  58. data/spec/fixtures/backtracs_xml_schema.xsd +171 -0
  59. data/spec/jobs/solidus_backtracs/api/schedule_shipment_syncs_job_spec.rb +32 -0
  60. data/spec/jobs/solidus_backtracs/api/sync_shipments_job_spec.rb +102 -0
  61. data/spec/lib/solidus_backtracs/api/batch_syncer_spec.rb +228 -0
  62. data/spec/lib/solidus_backtracs/api/client_spec.rb +120 -0
  63. data/spec/lib/solidus_backtracs/api/rate_limited_error_spec.rb +21 -0
  64. data/spec/lib/solidus_backtracs/api/request_error_spec.rb +20 -0
  65. data/spec/lib/solidus_backtracs/api/request_runner_spec.rb +65 -0
  66. data/spec/lib/solidus_backtracs/api/shipment_serializer_spec.rb +25 -0
  67. data/spec/lib/solidus_backtracs/api/threshold_verifier_spec.rb +61 -0
  68. data/spec/lib/solidus_backtracs/shipment_notice_spec.rb +111 -0
  69. data/spec/lib/solidus_backtracs_spec.rb +9 -0
  70. data/spec/models/spree/shipment_spec.rb +49 -0
  71. data/spec/queries/solidus_backtracs/shipment/between_query_spec.rb +53 -0
  72. data/spec/queries/solidus_backtracs/shipment/exportable_query_spec.rb +53 -0
  73. data/spec/queries/solidus_backtracs/shipment/pending_api_sync_query_spec.rb +37 -0
  74. data/spec/spec_helper.rb +31 -0
  75. data/spec/support/configuration_helper.rb +13 -0
  76. data/spec/support/controllers.rb +1 -0
  77. data/spec/support/webmock.rb +3 -0
  78. data/spec/support/xsd.rb +5 -0
  79. metadata +249 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4b7e4705f8b153de50fd8758b429b25bb10aaac9026ee0623fcb3fb4abab3fbb
4
+ data.tar.gz: 4ce4611fba9dbc776d89800e081f95221b71019ad6d04200f0cc171957a18a21
5
+ SHA512:
6
+ metadata.gz: 17c8b9f6e4ee8523d4f9fa69c7608a87bbed339ec4ba2afd26c0f071d9da0db84cf3e371604c26c4e9dec84bb41d4f66646ee14ec229740c2bbffd2c31d67eb2
7
+ data.tar.gz: 6891b4cff40267a4f8762b271fa1a69633c3b0f658ccda99dbce45941b56d8895f9ecaef0dbb0f91597271c880cb66f6545e396c98c74ac98f64577484a23193
data/.bundle/config ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_JOBS: "12"
@@ -0,0 +1,41 @@
1
+ version: 2.1
2
+
3
+ orbs:
4
+ # Always take the latest version of the orb, this allows us to
5
+ # run specs against Solidus supported versions only without the need
6
+ # to change this configuration every time a Solidus version is released
7
+ # or goes EOL.
8
+ solidusio_extensions: solidusio/extensions@volatile
9
+
10
+ jobs:
11
+ run-specs-with-postgres:
12
+ executor: solidusio_extensions/postgres
13
+ steps:
14
+ - solidusio_extensions/run-tests
15
+ run-specs-with-mysql:
16
+ executor: solidusio_extensions/mysql
17
+ steps:
18
+ - solidusio_extensions/run-tests
19
+ lint-code:
20
+ executor: solidusio_extensions/sqlite-memory
21
+ steps:
22
+ - solidusio_extensions/lint-code
23
+
24
+ workflows:
25
+ "Run specs on supported Solidus versions":
26
+ jobs:
27
+ - run-specs-with-postgres
28
+ - run-specs-with-mysql
29
+ - lint-code
30
+
31
+ "Weekly run specs against master":
32
+ triggers:
33
+ - schedule:
34
+ cron: "0 0 * * 4" # every Thursday
35
+ filters:
36
+ branches:
37
+ only:
38
+ - master
39
+ jobs:
40
+ - run-specs-with-postgres
41
+ - run-specs-with-mysql
data/.gem_release.yml ADDED
@@ -0,0 +1,5 @@
1
+ bump:
2
+ recurse: false
3
+ file: 'lib/solidus_backtracs/version.rb'
4
+ message: Bump SolidusBacktracs to %{version}
5
+ tag: true
data/.github/stale.yml ADDED
@@ -0,0 +1,17 @@
1
+ # Number of days of inactivity before an issue becomes stale
2
+ daysUntilStale: 60
3
+ # Number of days of inactivity before a stale issue is closed
4
+ daysUntilClose: false
5
+ # Issues with these labels will never be considered stale
6
+ exemptLabels:
7
+ - pinned
8
+ - security
9
+ # Label to use when marking an issue as stale
10
+ staleLabel: stale
11
+ # Comment to post when marking an issue as stale. Set to `false` to disable
12
+ markComment: >
13
+ This issue has been automatically marked as stale because it has not had
14
+ recent activity. It might be closed if no further activity occurs. Thank you
15
+ for your contributions.
16
+ # Comment to post when closing a stale issue. Set to `false` to disable
17
+ closeComment: false
@@ -0,0 +1,2 @@
1
+ issues=false
2
+ exclude-labels=infrastructure
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ \#*
3
+ *~
4
+ .#*
5
+ .DS_Store
6
+ .idea
7
+ .project
8
+ .sass-cache
9
+ coverage
10
+ Gemfile.lock
11
+ tmp
12
+ nbproject
13
+ pkg
14
+ *.swp
15
+ spec/dummy
16
+ spec/examples.txt
17
+ /sandbox
18
+ .rvmrc
19
+ .ruby-version
20
+ .ruby-gemset
21
+ .tool-versions
22
+ TAGS
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require:
4
+ - solidus_dev_support/rubocop
5
+
6
+ AllCops:
7
+ NewCops: disable
8
+ TargetRubyVersion: 2.5
9
+
10
+ Layout/LineLength:
11
+ Enabled: false
12
+
13
+ Rails/SkipsModelValidations:
14
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,40 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2020-09-22 09:09:44 UTC using RuboCop version 0.87.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ Lint/UselessAssignment:
11
+ Exclude:
12
+ - 'app/controllers/spree/backtracs_controller.rb'
13
+
14
+ # Offense count: 1
15
+ RSpec/AnyInstance:
16
+ Exclude:
17
+ - 'spec/lib/solidus_backtracs/shipment_notice_spec.rb'
18
+
19
+ # Offense count: 5
20
+ RSpec/MultipleExpectations:
21
+ Max: 2
22
+
23
+ # Offense count: 8
24
+ RSpec/NestedGroups:
25
+ Max: 5
26
+
27
+ # Offense count: 6
28
+ # Configuration parameters: ForbiddenMethods, AllowedMethods.
29
+ # ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all
30
+ Rails/SkipsModelValidations:
31
+ Exclude:
32
+ - 'spec/lib/solidus_backtracs/shipment_notice_spec.rb'
33
+ - 'spec/models/spree/shipment_spec.rb'
34
+ - 'spec/support/shipment_helper.rb'
35
+
36
+ # Offense count: 1
37
+ # Configuration parameters: MinBodyLength.
38
+ Style/GuardClause:
39
+ Exclude:
40
+ - 'spec/support/configuration_helper.rb'
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 2.2.0
4
+
5
+ Support the Solidus Assembly gem, consider allowed SKUs within assemblies/bundles
6
+ Retry logic for the authenticated request
7
+
8
+ ## 2.1.0
9
+
10
+ New auth and API patterns for Backtracs
11
+ Support specific SKUs to be mapped
12
+
13
+ ## 2.0.0
14
+
15
+ Diverged the gem from Shipstation to the Bactracs API
16
+
data/Gemfile ADDED
@@ -0,0 +1,33 @@
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', 'master')
7
+ gem 'solidus', github: 'solidusio/solidus', branch: branch
8
+
9
+ # Needed to help Bundler figure out how to resolve dependencies,
10
+ # otherwise it takes forever to resolve them.
11
+ # See https://github.com/bundler/bundler/issues/6677
12
+ gem 'rails', '>0.a'
13
+
14
+ # Provides basic authentication functionality for testing parts of your engine
15
+ gem 'solidus_auth_devise'
16
+
17
+ case ENV['DB']
18
+ when 'mysql'
19
+ gem 'mysql2'
20
+ when 'postgresql'
21
+ gem 'pg'
22
+ else
23
+ gem 'sqlite3'
24
+ end
25
+
26
+ gemspec
27
+
28
+ # Use a local Gemfile to include development dependencies that might not be
29
+ # relevant for the project or for other contributors, e.g. pry-byebug.
30
+ #
31
+ # We use `send` instead of calling `eval_gemfile` to work around an issue with
32
+ # how Dependabot parses projects: https://github.com/dependabot/dependabot-core/issues/1658.
33
+ send(:eval_gemfile, 'Gemfile-local') if File.exist? 'Gemfile-local'
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2013 Boomer Digital
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,216 @@
1
+ # Solidus Bactracs
2
+
3
+
4
+ This gem integrates [Bactracs](http://www.backtracs.com) with [Solidus](http://solidus.io). It
5
+ enables your Solidus system to push shipment RMAs to the system.
6
+
7
+ > This integration was cloned from [spree_shipstation](https://github.com/DynamoMTL/spree_shipstation) to provide some consistency with other Solidus<-->Shipping related patterns.
8
+
9
+ ## Installation
10
+
11
+ Add solidus_bactracs to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'solidus_bactracs', github: 'suvie-eng/solidus_bactracs'
15
+ ```
16
+
17
+ Bundle your dependencies and run the installation generator:
18
+
19
+ ```shell
20
+ bin/rails generate solidus_bactracs:install
21
+ ```
22
+
23
+ The installer will create a configuration initializer that you'll need to customize.
24
+
25
+ The installer will also create a migration, which is required by the API integration. If you are
26
+ going to use the XML integration, feel free to delete the migration file, as those columns won't be
27
+ used by the extension.
28
+
29
+ ## Usage
30
+
31
+ This extension can integrate with ShipStation in two ways.
32
+
33
+ ### XML integration
34
+
35
+ The [XML integration](https://help.backtracs.com/hc/en-us/articles/360025856192-Custom-Store-Development-Guide)
36
+ works by exposing a route in your Solidus application that generates an XML feed of all recently
37
+ created and updated shipments in your Solidus store.
38
+
39
+ #### XML integration: Configuration
40
+
41
+ ⛔️ This section is outdated and inaccurate.
42
+
43
+ In order to enable the XML integration, make sure to configure the relevant section of the
44
+ configuration initializer, and configure your ShipStation store accordingly:
45
+
46
+ - **Username**: the username defined in your configuration.
47
+ - **Password**: the password defined in your configuration.
48
+ - **URL to custom page**: `https://yourdomain.com/backtracs.xml`.
49
+
50
+ You can also configure your ShipStation store to pull the XML feed automatically on a recurring
51
+ basis, or manually by clicking the "Refresh stores" button.
52
+
53
+ There are five shipment states for an order (= shipment) in ShipStation. These states do not
54
+ necessarily align with Solidus, but you can configure ShipStation to create a mapping for your
55
+ specific needs. Here's the recommended mapping:
56
+
57
+ Bactracs description | Bactracs status | Solidus status
58
+ ------------------------|--------------------|---------------
59
+ Awaiting Payment | `unpaid` | `pending`
60
+ Awaiting Shipment | `paid` | `ready`
61
+ Shipped | `shipped` | `shipped`
62
+ Cancelled | `cancelled` | `cancelled`
63
+ On-Hold | `on-hold` | `pending`
64
+
65
+ Once you've configured the XML integration in your app and ShipStation, there's nothing else you
66
+ need to do. ShipStation will
67
+
68
+ #### XML integration: Usage
69
+
70
+ There's nothing you need to do. Once properly configured, the integration just works!
71
+
72
+ #### XML integration: Gotchas
73
+
74
+ There are a few gotchas you need to be aware of:
75
+
76
+ - If you change the shipping method of an order in ShipStation, the change will not be reflected in
77
+ Solidus and the tracking link might not work properly.
78
+ - When `bactracs_capture_at_notification` is enabled, any errors during payment capture will
79
+ prevent the update of the shipment's tracking number.
80
+
81
+ ### API integration
82
+
83
+ The [API integration](https://www.backtracs.com/docs/api/) works by calling the ShipStation API
84
+ to sync all of your shipments continuously.
85
+
86
+ Because ShipStation has very low rate limits (i.e., 40 reqs/minute at the time of writing), the
87
+ API integration does not send an API request for every single shipment update, as you would expect
88
+ from a traditional API integration.
89
+
90
+ Instead, a background job runs on a recurring basis and batches together all the shipments that need
91
+ to be created or updated in ShipStation. These shipments are then sent in groups of 100 (by default)
92
+ to ShipStation's [bulk order upsert endpoint](https://www.backtracs.com/docs/api/orders/create-update-multiple-orders/).
93
+
94
+ This allows us to work around Bactracs's rate limit and sync up to 4000 shipments/minute.
95
+
96
+ As you may imagine, this technique also comes at the expense of some additional complexity in the
97
+ implementation, but the extension abstracts it all away for you.
98
+
99
+ #### API integration: Configuration
100
+
101
+ In order to enable the API integration, make sure to configure the relevant section of the
102
+ configuration initializer. At the very least, the integration needs to know your API credentials
103
+ and store ID, but there are additional options you can configure — just look at the initializer!
104
+
105
+ #### API integration: Usage
106
+
107
+ Once you've configured the integration, you will also need to enqueue the `ScheduleShipmentSyncsJob`
108
+ on a recurring basis, to kick off the synchronization process. Because every app uses a different
109
+ background processing library, this is left up to the user.
110
+
111
+ Here's what an example with [sidekiq-scheduler](https://github.com/moove-it/sidekiq-scheduler) might
112
+ look like:
113
+
114
+ ```yaml
115
+ # config/sidekiq.yml
116
+ :schedule:
117
+ schedule_shipment_syncs:
118
+ every: ['1m', first_in: '0s']
119
+ class: 'SolidusBactracs::Api::ScheduleShipmentSyncsJob'
120
+ ```
121
+
122
+ This will schedule the job to run every minute. This is generally a good starting point, but feel
123
+ free to adjust it as needed.
124
+
125
+ #### API integration: Gotchas
126
+
127
+ One of the main issues to be aware of is that the Bactracs API *always* responds with an HTTP 200, even if the request failed. This may be a SOAP convention, or just the idiosyncrasies of the API design.
128
+
129
+ ⚠️ You must always inspect the `Message` component of the response in order to determine success/fail. THe e.g. `Result` field can say "true" but the Message states a failure.
130
+
131
+ ----
132
+
133
+ There other factor you need to be aware of when integrating via the API: the interval between your syncs is, on average, larger than your latency in processing background jobs, or you are going to experience sync overlaps.
134
+
135
+ As an example, if it takes your Sidekiq process 10 seconds to execute a job from the time it's
136
+ scheduled, but you schedule a shipment sync every 5 seconds, your sync jobs will start overlapping,
137
+ making your latency even worse.
138
+
139
+ This is a problem that is faced by all recurring jobs. The solution is two-fold:
140
+
141
+ 1. Monitor the latency of your background processing queues. Seriously, do it.
142
+ 2. Make sure your sync interval is not too aggressive: unless you really need to, there's no point
143
+ in syncing your shipments more often than once a minute.
144
+
145
+ ## Development
146
+
147
+ ### Testing the extension
148
+
149
+
150
+ ⛔️ This section is outdated and inaccurate.
151
+
152
+ First bundle your dependencies, then run `bin/rake`. `bin/rake` will default to building the dummy
153
+ app if it does not exist, then it will run specs. The dummy app can be regenerated by using
154
+ `bin/rake extension:test_app`.
155
+
156
+ ```shell
157
+ bin/rake
158
+ ```
159
+
160
+ To run [Rubocop](https://github.com/bbatsov/rubocop) static code analysis run
161
+
162
+ ```shell
163
+ bundle exec rubocop
164
+ ```
165
+
166
+ When testing your application's integration with this extension you may use its factories.
167
+ Simply add this require statement to your `spec/spec_helper.rb`:
168
+
169
+ ```ruby
170
+ require 'solidus_bactracs/testing_support/factories'
171
+ ```
172
+
173
+ Or, if you are using `FactoryBot.definition_file_paths`, you can load Solidus core
174
+ factories along with this extension's factories using this statement:
175
+
176
+ ```ruby
177
+ SolidusDevSupport::TestingSupport::Factories.load_for(SolidusBactracs::Engine)
178
+ ```
179
+
180
+ ### Running the sandbox
181
+
182
+
183
+ ⛔️ This section is outdated and inaccurate.
184
+
185
+ To run this extension in a sandboxed Solidus application, you can run `bin/sandbox`. The path for
186
+ the sandbox app is `./sandbox` and `bin/rails` will forward any Rails commands to
187
+ `sandbox/bin/rails`.
188
+
189
+ Here's an example:
190
+
191
+ ```
192
+ $ bin/rails server
193
+ => Booting Puma
194
+ => Rails 6.0.2.1 application starting in development
195
+ * Listening on tcp://127.0.0.1:3000
196
+ Use Ctrl-C to stop
197
+ ```
198
+
199
+ ### Updating the changelog
200
+
201
+ Before and after releases the changelog should be updated to reflect the up-to-date status of
202
+ the project:
203
+
204
+ ```shell
205
+ bin/rake changelog
206
+ git add CHANGELOG.md
207
+ git commit -m "Update the changelog"
208
+ ```
209
+
210
+ ### Releasing new versions
211
+
212
+ Please refer to the dedicated [page](https://github.com/solidusio/solidus/wiki/How-to-release-extensions) on Solidus wiki.
213
+
214
+ ## License
215
+
216
+ Copyright (c) 2022 Suvie, released under the New BSD License.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_dev_support/rake_tasks'
4
+ SolidusDevSupport::RakeTasks.install
5
+
6
+ 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,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ class BacktracsController < Spree::BaseController
5
+ protect_from_forgery with: :null_session, only: :shipnotify
6
+
7
+ before_action :authenticate_backtracs
8
+
9
+ def export
10
+ @shipments = SolidusBacktracs::Shipment::ExportableQuery.apply(Spree::Shipment.all)
11
+ @shipments = SolidusBacktracs::Shipment::BetweenQuery.apply(
12
+ @shipments,
13
+ from: date_param(:start_date),
14
+ to: date_param(:end_date),
15
+ )
16
+ @shipments = @shipments.page(params[:page]).per(50)
17
+
18
+ respond_to do |format|
19
+ format.xml { render layout: false }
20
+ end
21
+ end
22
+
23
+ def shipnotify
24
+ shipment_notice_class = SolidusBacktracs.configuration.shipment_notice_class.constantize
25
+ shipment_notice_class.from_payload(params.to_unsafe_h).apply
26
+ head :ok
27
+ rescue SolidusBacktracs::Error => e
28
+ head :bad_request
29
+ end
30
+
31
+ private
32
+
33
+ def date_param(name)
34
+ return if params[name].blank?
35
+
36
+ Time.strptime("#{params[name]} UTC", '%m/%d/%Y %H:%M %Z')
37
+ end
38
+
39
+ def authenticate_backtracs
40
+ authenticate_or_request_with_http_basic do |username, password|
41
+ username == SolidusBacktracs.configuration.username &&
42
+ password == SolidusBacktracs.configuration.password
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBacktracs
4
+ module Spree
5
+ module ShipmentDecorator
6
+ def self.prepended(base)
7
+ base.singleton_class.prepend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def exportable
12
+ ::Spree::Deprecation.warn <<~DEPRECATION
13
+ `Spree::Shipment.exportable` is deprecated and will be removed in a future version
14
+ of solidus_backtracs. Please use `SolidusBacktracs::Shipment::ExportableQuery.apply`.
15
+ DEPRECATION
16
+
17
+ SolidusBacktracs::Shipment::ExportableQuery.apply(self)
18
+ end
19
+
20
+ def between(from, to)
21
+ ::Spree::Deprecation.warn <<~DEPRECATION
22
+ `Spree::Shipment.between` is deprecated and will be removed in a future version
23
+ of solidus_backtracs. Please use `SolidusBacktracs::Shipment::BetweenQuery.apply`.
24
+ DEPRECATION
25
+
26
+ SolidusBacktracs::Shipment::BetweenQuery.apply(self, from: from, to: to)
27
+ end
28
+ end
29
+
30
+ ::Spree::Shipment.prepend self
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'builder'
4
+
5
+ module SolidusBacktracs
6
+ module ExportHelper
7
+ DATE_FORMAT = '%m/%d/%Y %H:%M'
8
+ BACTRACS_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S'
9
+
10
+ # rubocop:disable all
11
+ def self.address(xml, order, type)
12
+ name = "#{type.to_s.titleize}To"
13
+ address = order.send("#{type}_address")
14
+
15
+ xml.__send__(name) {
16
+ xml.Name address.respond_to?(:name) ? address.name : address.full_name
17
+ xml.Company address.company
18
+
19
+ if type == :ship
20
+ xml.Address1 address.address1
21
+ xml.Address2 address.address2
22
+ xml.City address.city
23
+ xml.State address.state ? address.state.abbr : address.state_name
24
+ xml.PostalCode address.zipcode
25
+ xml.Country address.country.iso
26
+ end
27
+
28
+ xml.Phone address.phone
29
+ }
30
+ end
31
+
32
+ def self.backtracs_address(xml, order, type)
33
+ address = order.send("#{type}_address")
34
+ if address.present?
35
+ name = "#{type.to_s.titleize}To"
36
+
37
+ xml.__send__(name) {
38
+ xml.CompanyName address.company ? address.company : address.name
39
+ xml.Contact address.name
40
+ xml.ContactEmail order.email
41
+ xml.Address1 address.address1
42
+ xml.Address2 address.address2
43
+ xml.City address.city
44
+ xml.State address.state ? address.state.abbr : address.state_name
45
+ xml.Zip address.zipcode
46
+ xml.Phone address.phone.present? ? address.phone : "000-000-0000"
47
+ xml.PhoneAlt address.phone.present? ? address.phone : "000-000-0000"
48
+ xml.Country address.country.iso
49
+ }
50
+ else
51
+ Rails.logger.info({
52
+ message: 'missing address type',
53
+ order: order.id,
54
+ type: type
55
+ }.to_s)
56
+ end
57
+ end
58
+ # rubocop:enable all
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBacktracs
4
+ module Api
5
+ class ScheduleShipmentSyncsJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform
9
+ shipments = query_shipments
10
+ Rails.logger.info("#{self.class.name} - #{shipments.count} shipments to sync to Bactracs")
11
+
12
+ shipments.find_in_batches(batch_size: SolidusBacktracs.config.api_batch_size) do |batch|
13
+ SyncShipmentsJob.perform_later(batch.to_a)
14
+ end
15
+ rescue StandardError => e
16
+ SolidusBacktracs.config.error_handler.call(e, {})
17
+ end
18
+
19
+ def shippable_skus
20
+ SolidusBacktracs.config.shippable_skus.present? ? SolidusBacktracs.config.shippable_skus : Spree::Variant.pluck(:sku)
21
+ end
22
+
23
+ def query_shipments
24
+ shipments = SolidusBacktracs::Shipment::PendingApiSyncQuery.apply(
25
+ ::Spree::Shipment
26
+ .joins(inventory_units: [:variant])
27
+ .where("spree_variants.sku" => SolidusBacktracs.config.shippable_skus)
28
+ .where("spree_shipments.state" => :ready)
29
+ .where(backtracs_synced_at: nil)
30
+ .distinct
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+