Compare commits

..

31 Commits

Author SHA1 Message Date
David Cook
fd889133cb Enable master variants with associated order cycles
There's only 5 in UK prod. keeping them is easier than figuring out if it's safe to delete.

(cherry picked from commit 4bf65e330b)
2023-06-23 14:16:45 +10:00
David Cook
4ac3dda398 Delete stock_items for master variants
All variants have stock_items records, but master variants never use them, so these were always redundant.

(cherry picked from commit 2025a98f58)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
9d5d269f1f Set master variants which are associated to line items to non-master
Line items which reference a master variant is a scenario that in theory shouldn't have been valid or even possible for at least 5-6 years, and these old bits of data in theory should have been cleaned up at the time those changes were made. But a couple of servers have some really old data that's not in a nice state.

Here we can just flip the is_master flag to false for those specific (legacy data) cases before deleting any other master variants, to keep the legacy line item data intact.

(cherry picked from commit c88799618f)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
f4405775f6 Remove line items related to master variants
These shouldn't technically exist, but apparently they can be present if the dataset is old enough. They can trigger a foreign key violation if they are present when a master variant is deleted, so they need to be dropped if present.

(cherry picked from commit f9185ea56e)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
3a38361857 Remove inventory units related to master variants
These shouldn't technically exist, but apparently they can be present if the dataset is old enough. They can trigger a foreign key violation if they are present when a master variant is deleted, so they need to be dropped if present.

(cherry picked from commit 6f5d3ceacc)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
68b59ab7a6 Remove array syntax on new product form for product image
(cherry picked from commit 24aa55e053)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
a56541216e Update ScopeVariantsForSearch logic to match both product and variant SKUs
(cherry picked from commit ae24b2d688)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
05d9646f3e Blank out product SKU when cloning a product
This was effectively being done before for the product's sku (stored on the master variant) via the #duplicate_variant method, but now it needs to be done explicitly on the product in #duplicate_product

(cherry picked from commit 0f253bb2a0)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
b1de64bf3e Remove unnecessary iterator
(cherry picked from commit 15000c7ed1)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
e976cc6d95 Disable variant is_master column
(cherry picked from commit 733dd3c428)
2023-06-23 14:16:45 +10:00
Matt-Yorkley
9a89b22364 Remove master images data migration tests
(cherry picked from commit be72bbebb9)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
1b304e2aa4 Remove is_master from variant serializer
(cherry picked from commit fbd09869bb)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
48cdca59fd Remove superfluous method from products controller
(cherry picked from commit ced60d4382)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
9095abfed2 Delete master variants
(cherry picked from commit b59bdc75e9)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
aa9fd682d8 Remove is_master and not_master scopes
(cherry picked from commit 1daab8994d)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
5b73ccb213 Remove unused is_master references in tests
(cherry picked from commit 0703bb4583)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
dda3cfa58d Remove master variant validation conditionals
(cherry picked from commit 85059bfb26)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
9da649a296 Improve validation feedback on new variant page and add test coverage
(cherry picked from commit 8247dce2dc)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
2e53b9a0c6 Fix flaky spec: use milliseconds in cache service and remove sleep
(cherry picked from commit 618900767f)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
527e305e2f Set unit_value to true when cloning a product
This value doesn't get persisted, but it's presence is validated. The product's duplicated variants store the actual :unit_value attribute

(cherry picked from commit 1e36043a2e)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
89b59f97ee Reorganise associations, validations, scopes and callbacks for clarity
(cherry picked from commit 1922598d2d)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
26d3cffba3 Remove master variant from product
(cherry picked from commit 3ef7d2c9ff)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
86703bb545 Migrate first master variant image to product image
(cherry picked from commit 21cba0aa13)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
8e99f496ff Migrate product image from master variant to product
(cherry picked from commit 7dc1091bc2)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
2f2506e698 Migrate master variant :sku to product
(cherry picked from commit d8649fc9fb)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
7ef9c2f56a Stop storing unit_value and unit_description on master variant
(cherry picked from commit 6b9b5ea347)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
ee4402f751 Stop using master variant for storing :display_as value
(cherry picked from commit 8c0b8dad85)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
79a2d1228d Stop using master variant as a potential store for prices
(cherry picked from commit 1b06c20197)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
da3202460c Update old specs that rely on master variant instead of real variants
(cherry picked from commit 80a0138b48)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
75ccc5c72f Simplify product images delegation mess
(cherry picked from commit d4188da7c1)
2023-06-23 14:16:44 +10:00
Matt-Yorkley
aca72e6071 Remove dead code
(cherry picked from commit 42a5a48816)
2023-06-23 14:16:44 +10:00
551 changed files with 3719 additions and 15143 deletions

View File

@@ -7,21 +7,21 @@ assignees: ''
---
## 1. Preparation on Thursday
## Preparation on Thursday
- [ ] Merge pull requests in the [Ready To Go] column
- [ ] Include translations: `tx pull --force`
- [ ] [Draft new release]. Look at previous [releases] for inspiration.
- [ ] Notify [#instance-managers] of user-facing changes.
## 2. Testing
## Testing
- [ ] [Find build] of the release commit and copy it below.
- [ ] Move this issue to Test Ready.
- [ ] Notify `@testers` in [#testing].
- [ ] Test build: <!-- paste build link here, e.g. https://semaphore...builds/1234 -->
## 3. Finish on Tuesday
## Finish on Tuesday
- [ ] Publish and notify [#global-community] (this is automatically posted with a plugin)
- [ ] Deploy the new release to all managed instances.
@@ -40,7 +40,7 @@ The full process is described at https://github.com/openfoodfoundation/openfoodn
[Ready To Go]: #zenhub
[Transifex pull request]: https://github.com/openfoodfoundation/openfoodnetwork/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+head%3Atransifex
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%23%23%23+Experimental+features+for+testing+:sunglasses:%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A
[Draft new release]: https://github.com/openfoodfoundation/openfoodnetwork/releases/new?tag=v&title=v+Code+Name&body=Congrats%0A%0ADescription%0A%0A%23%23+User+facing+changes+:eyes:%0A%0A%0A%0A%23%23+Technical+changes+:wrench:%0A%0A
[releases]: https://github.com/openfoodfoundation/openfoodnetwork/releases
[#instance-managers]: https://app.slack.com/client/T02G54U79/CG7NJ966B
[#testing]: https://openfoodnetwork.slack.com/app_redirect?channel=C02TZ6X00

View File

@@ -1,7 +1,7 @@
name: "Deploy to Staging"
on:
pull_request_target:
pull_request:
types: [labeled]
workflow_dispatch:
inputs:
@@ -14,27 +14,23 @@ on:
- staging.openfoodnetwork.org.au
- staging.coopcircuits.fr
permissions:
contents: read
jobs:
deploy_pr:
if: contains(fromJSON('["pr-staged-uk", "pr-staged-au", "pr-staged-fr"]'), github.event.label.name)
runs-on: ubuntu-latest
steps:
- name: "Check user has write access"
uses: "lannonbr/repo-permission-check-action@2.0.2"
with:
permission: "write"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Configure deployment key
if: success()
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.DEPLOYMENT_KEY }}" > ~/.ssh/id_rsa
echo "${{ secrets.DEPLOYMENT_HOSTS }}" > ~/.ssh/known_hosts
- name: Deploy to Staging
if: success()
env:
LABEL: ${{ github.event.label.name }}
run: |
ssh ofn-deploy@${{ github.event.label.description }} -o LogLevel=ERROR "pull-request-${{ github.event.pull_request.number }} ."
@@ -42,21 +38,12 @@ jobs:
if: ${{ inputs.server }}
runs-on: ubuntu-latest
steps:
- name: "Check user has write access"
uses: "lannonbr/repo-permission-check-action@2.0.2"
with:
permission: "write"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Configure deployment key
if: success()
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.DEPLOYMENT_KEY }}" > ~/.ssh/id_rsa
echo "${{ secrets.DEPLOYMENT_HOSTS }}" > ~/.ssh/known_hosts
- name: Deploy to Staging
if: success()
run: |
ssh ofn-deploy@${{ inputs.server }} -o LogLevel=ERROR "$GITHUB_REF_NAME $GITHUB_SHA"

View File

@@ -25,7 +25,6 @@ Metrics/BlockLength:
AllowedMethods: [
"class_eval",
"collection",
"configure",
"context",
"delete",
"describe",
@@ -45,9 +44,6 @@ Metrics/BlockLength:
"xdescribe",
]
Metrics/ParameterLists:
CountKeywordArgs: false
Rails/ApplicationRecord:
Exclude:
# Migrations should not contain application code:
@@ -93,8 +89,6 @@ Naming/VariableNumber:
AllowedIdentifiers:
- street_address_1
- street_address_2
AllowedPatterns:
- _v[\d]+
Bundler/DuplicatedGem:
Enabled: false

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
3.1.4
3.0.3

View File

@@ -1,10 +1,9 @@
# frozen_string_literal: true
source 'https://rubygems.org'
ruby "3.0.3"
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
ruby File.read('.ruby-version').chomp
gem 'dotenv-rails', require: 'dotenv/rails-now' # Load ENV vars before other gems
gem 'rails'
@@ -116,6 +115,8 @@ gem 'spreadsheet_architect' # write spreadsheets
gem 'whenever', require: false
gem 'test-unit', '~> 3.5'
gem 'coffee-rails', '~> 5.0.0'
gem 'angular_rails_csrf'
@@ -139,6 +140,7 @@ gem "faraday"
gem "private_address_check"
group :production, :staging do
gem 'ddtrace'
gem 'sd_notify' # For better Systemd process management. Used by Puma.
end

View File

@@ -26,6 +26,9 @@ PATH
remote: engines/dfc_provider
specs:
dfc_provider (0.0.1)
active_model_serializers (~> 0.8.4)
jwt (~> 2.2)
rspec (~> 3.9)
PATH
remote: engines/order_management
@@ -41,49 +44,49 @@ GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
actioncable (7.0.6)
actionpack (= 7.0.6)
activesupport (= 7.0.6)
actioncable (7.0.5)
actionpack (= 7.0.5)
activesupport (= 7.0.5)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.6)
actionpack (= 7.0.6)
activejob (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
actionmailbox (7.0.5)
actionpack (= 7.0.5)
activejob (= 7.0.5)
activerecord (= 7.0.5)
activestorage (= 7.0.5)
activesupport (= 7.0.5)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.6)
actionpack (= 7.0.6)
actionview (= 7.0.6)
activejob (= 7.0.6)
activesupport (= 7.0.6)
actionmailer (7.0.5)
actionpack (= 7.0.5)
actionview (= 7.0.5)
activejob (= 7.0.5)
activesupport (= 7.0.5)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.6)
actionview (= 7.0.6)
activesupport (= 7.0.6)
actionpack (7.0.5)
actionview (= 7.0.5)
activesupport (= 7.0.5)
rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionpack-action_caching (1.2.2)
actionpack (>= 4.0.0)
actiontext (7.0.6)
actionpack (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
actiontext (7.0.5)
actionpack (= 7.0.5)
activerecord (= 7.0.5)
activestorage (= 7.0.5)
activesupport (= 7.0.5)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.6)
activesupport (= 7.0.6)
actionview (7.0.5)
activesupport (= 7.0.5)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
@@ -95,19 +98,19 @@ GEM
activemodel (>= 5.2.0)
activestorage (>= 5.2.0)
activesupport (>= 5.2.0)
activejob (7.0.6)
activesupport (= 7.0.6)
activejob (7.0.5)
activesupport (= 7.0.5)
globalid (>= 0.3.6)
activemerchant (1.123.0)
activesupport (>= 4.2)
builder (>= 2.1.2, < 4.0.0)
i18n (>= 0.6.9)
nokogiri (~> 1.4)
activemodel (7.0.6)
activesupport (= 7.0.6)
activerecord (7.0.6)
activemodel (= 7.0.6)
activesupport (= 7.0.6)
activemodel (7.0.5)
activesupport (= 7.0.5)
activerecord (7.0.5)
activemodel (= 7.0.5)
activesupport (= 7.0.5)
activerecord-import (1.4.1)
activerecord (>= 4.2)
activerecord-postgresql-adapter (0.0.1)
@@ -118,14 +121,14 @@ GEM
multi_json (~> 1.11, >= 1.11.2)
rack (>= 2.0.8, < 3)
railties (>= 5.2.4.1)
activestorage (7.0.6)
actionpack (= 7.0.6)
activejob (= 7.0.6)
activerecord (= 7.0.6)
activesupport (= 7.0.6)
activestorage (7.0.5)
actionpack (= 7.0.5)
activejob (= 7.0.5)
activerecord (= 7.0.5)
activesupport (= 7.0.5)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.6)
activesupport (7.0.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@@ -154,20 +157,20 @@ GEM
awesome_nested_set (3.5.0)
activerecord (>= 4.0.0, < 7.1)
aws-eventstream (1.2.0)
aws-partitions (1.792.0)
aws-sdk-core (3.179.0)
aws-partitions (1.779.0)
aws-sdk-core (3.174.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.71.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sdk-kms (1.66.0)
aws-sdk-core (~> 3, >= 3.174.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.132.0)
aws-sdk-core (~> 3, >= 3.179.0)
aws-sdk-s3 (1.124.0)
aws-sdk-core (~> 3, >= 3.174.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.6)
aws-sigv4 (1.6.0)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.18)
bigdecimal (3.0.2)
@@ -175,7 +178,7 @@ GEM
bindex (0.8.1)
bootsnap (1.16.0)
msgpack (~> 1.2)
bugsnag (6.26.0)
bugsnag (6.25.2)
concurrent-ruby (~> 1.0)
builder (3.2.4)
bullet (7.0.7)
@@ -234,6 +237,12 @@ GEM
datafoodconsortium-connector (1.0.0.pre.alpha.6)
virtual_assembly-semantizer (~> 1.0, >= 1.0.4)
date (3.3.3)
ddtrace (1.12.1)
debase-ruby_core_source (= 3.2.1)
libdatadog (~> 2.0.0.1.0)
libddwaf (~> 1.9.0.0.0)
msgpack
debase-ruby_core_source (3.2.1)
debug (1.8.0)
irb (>= 1.5.0)
reline (>= 0.3.1)
@@ -267,7 +276,7 @@ GEM
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (2.7.10)
faraday (2.7.6)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-follow_redirects (0.3.0)
@@ -314,7 +323,7 @@ GEM
fuubar (2.5.1)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
geocoder (1.8.2)
geocoder (1.8.1)
globalid (1.1.0)
activesupport (>= 5.0)
gmaps4rails (2.1.2)
@@ -374,13 +383,15 @@ GEM
jsonapi-serializer (2.2.0)
activesupport (>= 4.2)
jwt (2.7.1)
knapsack_pro (5.3.4)
knapsack_pro (5.1.0)
rake
language_server-protocol (3.17.0.3)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
libdatadog (2.0.0.1.0)
libddwaf (1.9.0.0.1)
ffi (~> 1.0)
link_header (0.0.8)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
@@ -404,8 +415,8 @@ GEM
rake
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.8.4)
minitest (5.18.1)
mini_portile2 (2.8.2)
minitest (5.18.0)
monetize (1.12.0)
money (~> 6.12)
money (6.16.0)
@@ -413,7 +424,7 @@ GEM
msgpack (1.7.1)
multi_json (1.15.0)
multi_xml (0.6.0)
net-imap (0.3.6)
net-imap (0.3.4)
date
net-protocol
net-pop (0.1.2)
@@ -423,7 +434,7 @@ GEM
net-smtp (0.3.3)
net-protocol
nio4r (2.5.9)
nokogiri (1.15.3)
nokogiri (1.15.2)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oauth2 (1.4.11)
@@ -477,18 +488,19 @@ GEM
ruby-rc4
ttfunk
pg (1.2.3)
power_assert (2.0.3)
private_address_check (0.5.0)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.3)
public_suffix (5.0.1)
puma (6.3.0)
nio4r (~> 2.0)
query_count (1.1.1)
activerecord (>= 4.2)
railties (>= 4.2)
raabro (1.4.0)
racc (1.7.1)
racc (1.7.0)
rack (2.2.7)
rack-mini-profiler (2.3.4)
rack (>= 1.2.0)
@@ -506,27 +518,26 @@ GEM
rack-test (2.1.0)
rack (>= 1.3)
rack-timeout (0.6.3)
rails (7.0.6)
actioncable (= 7.0.6)
actionmailbox (= 7.0.6)
actionmailer (= 7.0.6)
actionpack (= 7.0.6)
actiontext (= 7.0.6)
actionview (= 7.0.6)
activejob (= 7.0.6)
activemodel (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
rails (7.0.5)
actioncable (= 7.0.5)
actionmailbox (= 7.0.5)
actionmailer (= 7.0.5)
actionpack (= 7.0.5)
actiontext (= 7.0.5)
actionview (= 7.0.5)
activejob (= 7.0.5)
activemodel (= 7.0.5)
activerecord (= 7.0.5)
activestorage (= 7.0.5)
activesupport (= 7.0.5)
bundler (>= 1.15.0)
railties (= 7.0.6)
railties (= 7.0.5)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
activesupport (>= 5.0.1.rc1)
rails-dom-testing (2.1.1)
activesupport (>= 5.0.0)
minitest
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-erd (1.7.2)
activerecord (>= 4.2)
@@ -540,9 +551,9 @@ GEM
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
rails_safe_tasks (1.0.0)
railties (7.0.6)
actionpack (= 7.0.6)
activesupport (= 7.0.6)
railties (7.0.5)
actionpack (= 7.0.5)
activesupport (= 7.0.5)
method_source
rake (>= 12.2)
thor (~> 1.0)
@@ -570,7 +581,7 @@ GEM
responders (3.1.0)
actionpack (>= 5.2)
railties (>= 5.2)
rexml (3.2.6)
rexml (3.2.5)
roadie (5.0.1)
css_parser (~> 1.4)
nokogiri (~> 1.8)
@@ -605,31 +616,30 @@ GEM
rspec-support (~> 3.12)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.12.1)
rswag-api (2.10.1)
rspec-support (3.12.0)
rswag-api (2.9.0)
railties (>= 3.1, < 7.1)
rswag-specs (2.10.1)
rswag-specs (2.9.0)
activesupport (>= 3.1, < 7.1)
json-schema (>= 2.2, < 4.0)
railties (>= 3.1, < 7.1)
rspec-core (>= 2.14)
rswag-ui (2.10.1)
rswag-ui (2.9.0)
actionpack (>= 3.1, < 7.1)
railties (>= 3.1, < 7.1)
rubocop (1.55.0)
rubocop (1.52.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.3)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.1, < 2.0)
rubocop-ast (>= 1.28.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
parser (>= 3.2.1.0)
rubocop-rails (2.20.2)
rubocop-rails (2.19.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
@@ -643,7 +653,7 @@ GEM
rubyzip (2.3.2)
rufus-scheduler (3.8.2)
fugit (~> 1.1, >= 1.1.6)
sanitize (6.0.2)
sanitize (6.0.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
sass (3.4.25)
@@ -685,13 +695,13 @@ GEM
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
state_machines (0.6.0)
state_machines-activemodel (0.9.0)
activemodel (>= 6.0)
state_machines (>= 0.6.0)
state_machines-activerecord (0.9.0)
activerecord (>= 6.0)
state_machines-activemodel (>= 0.9.0)
state_machines (0.5.0)
state_machines-activemodel (0.8.0)
activemodel (>= 5.1)
state_machines (>= 0.5.0)
state_machines-activerecord (0.8.0)
activerecord (>= 5.1)
state_machines-activemodel (>= 0.8.0)
stimulus_reflex (3.5.0.rc2)
actioncable (>= 5.2, < 8)
actionpack (>= 5.2, < 8)
@@ -703,17 +713,19 @@ GEM
railties (>= 5.2, < 8)
redis (>= 4.0, < 6.0)
stringex (2.8.6)
stripe (8.6.0)
stripe (8.5.0)
swd (1.3.0)
activesupport (>= 3)
attr_required (>= 0.0.5)
httpclient (>= 2.4)
temple (0.8.2)
test-unit (3.6.0)
power_assert
thor (1.2.2)
thread-local (1.1.0)
tilt (2.1.0)
timecop (0.9.6)
timeout (0.4.0)
timeout (0.3.2)
ttfunk (1.7.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
@@ -728,8 +740,8 @@ GEM
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
vcr (6.2.0)
view_component (3.5.0)
vcr (6.1.0)
view_component (3.2.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
@@ -806,6 +818,7 @@ DEPENDENCIES
database_cleaner
datafoodconsortium-connector
db2fog!
ddtrace
debug (>= 1.0.0)
debugger-linecache
devise
@@ -897,6 +910,7 @@ DEPENDENCIES
stimulus_reflex (= 3.5.0.rc2)
stringex (~> 2.8.5)
stripe
test-unit (~> 3.5)
timecop
valid_email2
vcr
@@ -911,7 +925,7 @@ DEPENDENCIES
wkhtmltopdf-binary
RUBY VERSION
ruby 3.1.4p223
ruby 3.0.3p157
BUNDLED WITH
2.4.3

View File

@@ -7,3 +7,4 @@
require_relative 'config/application'
Openfoodnetwork::Application.load_tasks

View File

@@ -68,6 +68,25 @@
//= require textAngular.min.js
//= require i18n/translations
//= require darkswarm/i18n.translate.js
//= require moment/min/moment.min.js
//= require moment/locale/ar.js
//= require moment/locale/ca.js
//= require moment/locale/de.js
//= require moment/locale/en-gb.js
//= require moment/locale/es.js
//= require moment/locale/fil.js
//= require moment/locale/fr.js
//= require moment/locale/it.js
//= require moment/locale/nb.js
//= require moment/locale/nl-be.js
//= require moment/locale/pt-br.js
//= require moment/locale/pt.js
//= require moment/locale/ru.js
//= require moment/locale/sv.js
//= require moment/locale/tr.js
//= require moment/locale/pl.js
//= require js-big-decimal/dist/web/js-big-decimal.min.js
// foundation
//= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js

View File

@@ -113,7 +113,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
(DirtyProducts.count() > 0 and confirm(t("unsaved_changes_confirmation"))) or (DirtyProducts.count() == 0)
editProductUrl = (product, variant) ->
"/admin/products/" + product.id + ((if variant then "/variants/" + variant.id else "")) + "/edit"
"/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit"
$scope.editWarn = (product, variant) ->
if confirm_unsaved_changes()
@@ -162,7 +162,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
if confirm(t("are_you_sure"))
$http(
method: "DELETE"
url: "/api/v0/products/" + product.id + "/variants/" + variant.id
url: "/api/v0/products/" + product.permalink_live + "/variants/" + variant.id
).then (response) ->
$scope.removeVariant(product, variant)
else
@@ -352,6 +352,9 @@ filterSubmitProducts = (productsToFilter) ->
if product.hasOwnProperty("inherits_properties")
filteredProduct.inherits_properties = product.inherits_properties
hasUpdatableProperty = true
if product.hasOwnProperty("available_on")
filteredProduct.available_on = product.available_on
hasUpdatableProperty = true
if filteredMaster?
filteredProduct.master_attributes = filteredMaster
hasUpdatableProperty = true

View File

@@ -0,0 +1,5 @@
angular.module("admin.dropdown").directive "linksDropdown", ($window)->
restrict: "C"
scope:
links: "="
templateUrl: "admin/links_dropdown.html"

View File

@@ -11,5 +11,24 @@
//= require i18n/translations
//= require darkswarm/i18n.translate.js
//= require moment/min/moment.min.js
//= require moment/locale/ar.js
//= require moment/locale/ca.js
//= require moment/locale/de.js
//= require moment/locale/en-gb.js
//= require moment/locale/es.js
//= require moment/locale/fil.js
//= require moment/locale/fr.js
//= require moment/locale/it.js
//= require moment/locale/nb.js
//= require moment/locale/nl-be.js
//= require moment/locale/pt-br.js
//= require moment/locale/pt.js
//= require moment/locale/ru.js
//= require moment/locale/sv.js
//= require moment/locale/tr.js
//= require moment/locale/pl.js
window.angular = { module: function(noop){ return { value: function(){} } } }
//= require js-big-decimal/dist/web/js-big-decimal.min.js
window.angular = { module: function(noop){ return { value: function(){} } } }

View File

@@ -29,6 +29,24 @@
#
#= require angular-flash.min.js
#
#= require moment/min/moment.min.js
#= require moment/locale/ar.js
#= require moment/locale/ca.js
#= require moment/locale/de.js
#= require moment/locale/en-gb.js
#= require moment/locale/es.js
#= require moment/locale/fil.js
#= require moment/locale/fr.js
#= require moment/locale/it.js
#= require moment/locale/nb.js
#= require moment/locale/nl-be.js
#= require moment/locale/pt-br.js
#= require moment/locale/pt.js
#= require moment/locale/ru.js
#= require moment/locale/sv.js
#= require moment/locale/tr.js
#= require moment/locale/pl.js
#
#= require modernizr
#
#= require foundation-sites/js/foundation.js

View File

@@ -0,0 +1,19 @@
.ofn-drop-down
%span
%i.icon-check
{{ 'admin.actions' | t }}
%i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
%div.menu{ 'ng-show' => "expanded" }
%div{ 'ng-repeat' => "link in links" }
%a.menu_item{ 'ng-if': "link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method }}", "ujs-navigate": "false", confirm: "{{link.confirm}}" } }
%span
%i{ ng: { class: "link.icon" } }
%span {{ link.name }}
%a.menu_item{ 'ng-if': "link.confirm && !link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}", "data-confirm": "{{link.confirm}}" }
%span
%i{ ng: { class: "link.icon" } }
%span {{ link.name }}
%a.menu_item{ 'ng-if': "!link.confirm && !link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}" }
%span
%i{ ng: { class: "link.icon" } }
%span {{ link.name }}

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
class ScopedChannel < ApplicationCable::Channel
class << self
def for_id(id)
"ScopedChannel:#{id}"
end
end
def subscribed
stream_from "ScopedChannel:#{params[:id]}"
end
end

View File

@@ -1,8 +1,7 @@
# frozen_string_literal: true
class ConfirmModalComponent < ModalComponent
def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil,
confirm_reflexes: nil)
def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil, confirm_reflexes: nil)
super(id: id, close_button: true)
@confirm_actions = confirm_actions
@reflex = reflex

View File

@@ -20,7 +20,7 @@ class ProductComponent < ViewComponentReflex::Component
@product.id
end
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
def column_value(column)
case column
when 'name'
@@ -43,11 +43,13 @@ class ProductComponent < ViewComponentReflex::Component
@product.tax_category.name
when 'inherits_properties'
@product.inherits_properties
when 'available_on'
format_date(@product.available_on)
when 'import_date'
format_date(@product.import_date)
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
private

View File

@@ -17,6 +17,7 @@ class ProductsTableComponent < ViewComponentReflex::Component
label: I18n.t("admin.products_page.columns_selector.inherits_properties"),
value: "inherits_properties"
},
{ label: I18n.t("admin.products_page.columns_selector.available_on"), value: "available_on" },
{ label: I18n.t("admin.products_page.columns_selector.import_date"), value: "import_date" }
].sort do |a, b|
a[:label] <=> b[:label]

View File

@@ -12,7 +12,7 @@ module Admin
@line_items = order_permissions.
editable_line_items.where(order_id: orders).
includes(:variant).
ransack(params[:q]).result.order(:id)
ransack(params[:q]).result
@pagy, @line_items = pagy(@line_items) if pagination_required?
@@ -34,8 +34,7 @@ module Admin
# and https://www.postgresql.org/docs/current/static/sql-select.html#SQL-FOR-UPDATE-SHARE
order.with_lock do
if order.contents.update_item(@line_item, line_item_params)
# No Content, does not trigger ng resource auto-update
render body: nil, status: :no_content
render body: nil, status: :no_content # No Content, does not trigger ng resource auto-update
else
render json: { errors: @line_item.errors }, status: :precondition_failed
end

View File

@@ -36,10 +36,8 @@ module Admin
end
def create
@customer = Customer.find_or_initialize_by(customer_params.slice(:email, :enterprise_id))
@customer = Customer.new(customer_params)
if user_can_create_customer?
@customer.created_manually = true
if @customer.save
tag_rule_mapping = TagRule.mapping_for(Enterprise.where(id: @customer.enterprise))
render_as_json @customer, tag_rule_mapping: tag_rule_mapping
@@ -85,7 +83,7 @@ module Admin
def customers
return @customers if @customers.present?
@customers = Customer.visible.managed_by(spree_current_user)
@customers = Customer.managed_by(spree_current_user)
return @customers if params[:enterprise_id].blank?
@customers = @customers.where(enterprise_id: params[:enterprise_id])

View File

@@ -6,6 +6,8 @@ module Admin
class EnterpriseFeesController < Admin::ResourceController
before_action :load_enterprise_fee_set, only: :index
before_action :load_data
before_action :check_enterprise_fee_input, only: [:bulk_update]
before_action :check_calculators_compatibility_with_taxes, only: [:bulk_update]
def index
@include_calculators = params[:include_calculators].present?
@@ -34,8 +36,7 @@ module Admin
end
def bulk_update
# Forms has strong parameters, so we don't need to validate them in controller
@enterprise_fee_set = EnterpriseFeesBulkUpdate.new(params)
@enterprise_fee_set = Sets::EnterpriseFeeSet.new(enterprise_fee_bulk_params)
if @enterprise_fee_set.save
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
@@ -61,7 +62,9 @@ module Admin
when :for_order_cycle
order_cycle = OrderCycle.find_by(id: params[:order_cycle_id]) if params[:order_cycle_id]
coordinator = Enterprise.find_by(id: params[:coordinator_id]) if params[:coordinator_id]
order_cycle ||= OrderCycle.new(coordinator: coordinator) if coordinator.present?
if order_cycle.nil? && coordinator.present?
order_cycle = OrderCycle.new(coordinator: coordinator)
end
enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user,
order_cycle).visible_enterprises
EnterpriseFee.for_enterprises(enterprises).order('enterprise_id', 'fee_type', 'name')
@@ -88,5 +91,47 @@ module Admin
main_app.admin_enterprise_fees_path
end
def enterprise_fee_bulk_params
params.require(:sets_enterprise_fee_set).permit(
collection_attributes: [
:id, :enterprise_id, :fee_type, :name, :tax_category_id,
:inherits_tax_category, :calculator_type,
{ calculator_attributes: PermittedAttributes::Calculator.attributes }
]
)
end
def check_enterprise_fee_input
enterprise_fee_bulk_params['collection_attributes'].each do |_, fee_row|
enterprise_fees = fee_row['calculator_attributes']&.slice(
:preferred_flat_percent, :preferred_amount,
:preferred_first_item, :preferred_additional_item,
:preferred_minimal_amount, :preferred_normal_amount,
:preferred_discount_amount, :preferred_per_unit
)
next unless enterprise_fees
enterprise_fees.each do |_, enterprise_amount|
unless enterprise_amount.nil? || Float(enterprise_amount, exception: false)
flash[:error] = I18n.t(:calculator_preferred_value_error)
return redirect_to redirect_path
end
end
end
end
def check_calculators_compatibility_with_taxes
enterprise_fee_bulk_params['collection_attributes'].each do |_, enterprise_fee|
next unless enterprise_fee['inherits_tax_category'] == "true"
next unless EnterpriseFee::PER_ORDER_CALCULATORS.include?(enterprise_fee['calculator_type'])
flash[:error] = I18n.t(
'activerecord.errors.models.enterprise_fee.inherit_tax_requires_per_item_calculator'
)
return redirect_to redirect_path
end
end
end
end

View File

@@ -16,8 +16,7 @@ module Admin
@enterprise_relationship = EnterpriseRelationship.new enterprise_relationship_params
if @enterprise_relationship.save
render plain: Api::Admin::EnterpriseRelationshipSerializer
.new(@enterprise_relationship).to_json
render plain: Api::Admin::EnterpriseRelationshipSerializer.new(@enterprise_relationship).to_json
else
render status: :bad_request,
json: { errors: @enterprise_relationship.errors.full_messages.join(', ') }

View File

@@ -92,7 +92,7 @@ module Admin
return render :welcome, layout: "spree/layouts/bare_admin"
end
attributes = { sells: register_params[:sells], visible: "only_through_links" }
attributes = { sells: register_params[:sells], visible: true }
if @enterprise.update(attributes)
flash[:success] = I18n.t(:enterprise_register_success_notice, enterprise: @enterprise.name)
@@ -121,12 +121,8 @@ module Admin
def for_order_cycle
respond_to do |format|
format.json do
render(
json: @collection,
each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer,
order_cycle: @order_cycle,
spree_current_user: spree_current_user
)
render json: @collection,
each_serializer: Api::Admin::ForOrderCycle::EnterpriseSerializer, order_cycle: @order_cycle, spree_current_user: spree_current_user
end
end
end
@@ -182,7 +178,9 @@ module Admin
when :for_order_cycle
@order_cycle = OrderCycle.find_by(id: params[:order_cycle_id]) if params[:order_cycle_id]
coordinator = Enterprise.find_by(id: params[:coordinator_id]) if params[:coordinator_id]
@order_cycle ||= OrderCycle.new(coordinator: coordinator) if coordinator.present?
if @order_cycle.nil? && coordinator.present?
@order_cycle = OrderCycle.new(coordinator: coordinator)
end
enterprises = OpenFoodNetwork::OrderCyclePermissions.new(spree_current_user, @order_cycle)
.visible_enterprises

View File

@@ -37,14 +37,8 @@ module Admin
end
def reset_absent_products
@importer = ProductImport::ProductImporter.new(
File.new(file_path),
spree_current_user,
import_into: params[:import_into],
enterprises_to_reset: params[:enterprises_to_reset],
updated_ids: params[:updated_ids],
settings: params[:settings]
)
@importer = ProductImport::ProductImporter.new(File.new(file_path),
spree_current_user, import_into: params[:import_into], enterprises_to_reset: params[:enterprises_to_reset], updated_ids: params[:updated_ids], settings: params[:settings])
if params.key?(:enterprises_to_reset) && params.key?(:updated_ids)
@importer.reset_absent(params[:updated_ids])
@@ -62,13 +56,8 @@ module Admin
end
def process_data(method)
@importer = ProductImport::ProductImporter.new(
File.new(file_path),
spree_current_user,
start: params[:start],
end: params[:end],
settings: params[:settings]
)
@importer = ProductImport::ProductImporter.new(File.new(file_path),
spree_current_user, start: params[:start], end: params[:end], settings: params[:settings])
begin
@importer.public_send("#{method}_entries")
@@ -139,8 +128,7 @@ module Admin
end
def raise_invalid_file_path
redirect_to '/admin/product_import',
notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
redirect_to '/admin/product_import', notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
raise 'Invalid File Path'
end
TEMP_FILE_PATH_REGEX = %r{^/tmp/product_import[A-Za-z0-9-]*/import\.csv$}

View File

@@ -1,7 +0,0 @@
# frozen_string_literal: true
module Admin
class ProductsV3Controller < Spree::Admin::BaseController
def index; end
end
end

View File

@@ -22,29 +22,24 @@ module Admin
def show
@report = report_class.new(spree_current_user, params, render: render_data?)
@background_reports = OpenFoodNetwork::FeatureToggle
.enabled?(:background_reports, spree_current_user)
if @background_reports && request.post?
return background(report_format)
end
if params[:report_format].present?
export_report
else
show_report
end
rescue Timeout::Error
render_timeout_error
end
private
def export_report
send_data @report.render_as(report_format), filename: report_filename
send_data render_report_as(report_format), filename: report_filename
end
def show_report
assign_view_data
@table = @report.render_as(:html) if render_data?
@table = render_report_as(:html) if render_data?
render "show"
end
@@ -61,24 +56,45 @@ module Admin
request.post?
end
def background(format)
cable_ready[ScopedChannel.for_id(params[:uuid])]
.inner_html(
selector: "#report-table",
html: render_to_string(partial: "admin/reports/loading")
).scroll_into_view(
selector: "#report-table",
block: "start"
).broadcast
def render_report_as(format)
if OpenFoodNetwork::FeatureToggle.enabled?(:background_reports, spree_current_user)
@blob = ReportBlob.create_for_upload_later!(report_filename)
ReportJob.perform_later(
report_class, spree_current_user, params, format, @blob
)
Timeout.timeout(max_wait_time) do
sleep 1 until @blob.content_stored?
end
blob = ReportBlob.create_for_upload_later!(report_filename)
# This result has been rendered by Rails in safe mode already.
@blob.result.html_safe # rubocop:disable Rails/OutputSafety
else
@report.render_as(format)
end
end
ReportJob.perform_later(
report_class: report_class, user: spree_current_user, params: params,
format: format, blob: blob, channel: ScopedChannel.for_id(params[:uuid]),
)
def render_timeout_error
assign_view_data
if @blob
@error = ".report_taking_longer_html"
@error_url = @blob.expiring_service_url
else
@error = ".report_taking_longer"
@error_url = ""
end
render "show"
end
head :no_content
def max_wait_time
# This value is used by rack-timeout and nginx, usually 30 seconds in
# staging and production:
server_timeout = ENV.fetch("RACK_TIMEOUT_SERVICE_TIMEOUT", "15").to_f
# Zero disables the timeout:
return 0 if server_timeout.zero?
# We want to time out earlier than nginx:
server_timeout - 2.seconds
end
end
end

View File

@@ -18,8 +18,7 @@ module Admin
if view_context.subscriptions_setup_complete?(@shops)
@order_cycles = OrderCycle.joins(:schedules).managed_by(spree_current_user)
.includes([:distributors, :cached_incoming_exchanges])
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user)
.includes(:taggings)
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user).includes(:taggings)
@payment_method_tags = payment_method_tags_by_id
@shipping_methods = Spree::ShippingMethod.managed_by(spree_current_user)
else
@@ -101,8 +100,7 @@ module Admin
end
def load_shops
@shops = Enterprise.managed_by(spree_current_user)
.is_distributor.where(enable_subscriptions: true)
@shops = Enterprise.managed_by(spree_current_user).is_distributor.where(enable_subscriptions: true)
end
def load_form_data
@@ -141,9 +139,7 @@ module Admin
@open_orders_to_keep = @subscription.proxy_orders.placed_and_open.pluck(:id)
return if @open_orders_to_keep.empty? || params[:open_orders] == 'keep'
render json: {
errors: { open_orders: t('admin.subscriptions.confirm_cancel_open_orders_msg') }
},
render json: { errors: { open_orders: t('admin.subscriptions.confirm_cancel_open_orders_msg') } },
status: :conflict
end
@@ -151,9 +147,7 @@ module Admin
return if params[:canceled_orders] == 'notified'
return if @subscription.proxy_orders.active.canceled.empty?
render json: {
errors: { canceled_orders: t('admin.subscriptions.resume_canceled_orders_msg') }
},
render json: { errors: { canceled_orders: t('admin.subscriptions.resume_canceled_orders_msg') } },
status: :conflict
end

View File

@@ -9,11 +9,14 @@ module Admin
end
def create
@voucher = Voucher.create(permitted_resource_params.merge(enterprise: @enterprise))
voucher_params = permitted_resource_params.merge(enterprise: @enterprise)
@voucher = Voucher.create(voucher_params)
if @voucher.save
flash[:success] = flash_message_for(@voucher, :successfully_created)
redirect_to edit_admin_enterprise_path(@enterprise, anchor: :vouchers_panel)
redirect_to(
"#{edit_admin_enterprise_path(@enterprise)}#vouchers_panel",
flash: { success: flash_message_for(@voucher, :successfully_created) }
)
else
flash[:error] = @voucher.errors.full_messages.to_sentence
render :new

View File

@@ -6,7 +6,7 @@ module Api
skip_authorization_check only: :index
def index
@customers = current_api_user.customers.visible
@customers = current_api_user.customers
render json: @customers, each_serializer: CustomerSerializer
end

View File

@@ -74,7 +74,7 @@ module Api
end
def override_visible
enterprise_params[:visible] = "only_through_links"
enterprise_params[:visible] = false
end
def enterprise_params

View File

@@ -10,6 +10,8 @@ module Api
respond_to :json
DEFAULT_PER_PAGE = 15
before_action :set_default_available_on, only: :create
skip_authorization_check only: [:show, :bulk_products, :overridable]
def show
@@ -21,10 +23,15 @@ module Api
authorize! :create, Spree::Product
@product = Spree::Product.new(product_params)
if @product.save
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
else
invalid_resource!(@product)
begin
if @product.save
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
else
invalid_resource!(@product)
end
rescue ActiveRecord::RecordNotUnique
@product.permalink = nil
retry
end
end
@@ -89,6 +96,8 @@ module Api
private
def find_product(id)
product_scope.find_by!(permalink: id.to_s)
rescue ActiveRecord::RecordNotFound
product_scope.find(id)
end
@@ -143,6 +152,10 @@ module Api
@product_params ||=
params.permit(product: PermittedAttributes::Product.attributes)[:product].to_h
end
def set_default_available_on
product_params[:available_on] ||= Time.zone.now
end
end
end
end

View File

@@ -50,7 +50,7 @@ module Api
private
def product
@product ||= Spree::Product.find(params[:product_id]) if params[:product_id]
@product ||= Spree::Product.find_by(permalink: params[:product_id]) if params[:product_id]
end
def scope

View File

@@ -29,8 +29,7 @@ module Api
def create
authorize! :update, Enterprise.find(customer_params[:enterprise_id])
customer = Customer.find_or_initialize_by(customer_params.slice(:email, :enterprise_id))
customer.assign_attributes(customer_params)
customer = Customer.new(customer_params)
if customer.save
render json: Api::V1::CustomerSerializer.new(customer), status: :created
@@ -81,7 +80,7 @@ module Api
end
def visible_customers
Customer.visible.managed_by(current_api_user)
Customer.managed_by(current_api_user)
end
def customer_params
@@ -97,7 +96,6 @@ module Api
]
).to_h
attributes.merge!(created_manually: true)
attributes.merge!(tag_list: params[:tags]) if params.key?(:tags)
transform_address!(attributes, :billing_address, :bill_address)

View File

@@ -14,7 +14,7 @@ module CheckoutCallbacks
prepend_before_action :require_order_cycle
prepend_before_action :require_distributor_chosen
before_action :load_order, :associate_user, :load_saved_addresses
before_action :load_order, :associate_user, :load_saved_addresses, :load_saved_credit_cards
before_action :load_shipping_methods, if: -> { params[:step] == "details" }
before_action :ensure_order_not_completed
@@ -30,6 +30,8 @@ module CheckoutCallbacks
@order.manual_shipping_selection = true
@order.checkout_processing = true
@voucher_adjustment = @order.voucher_adjustments.first
redirect_to(main_app.shop_path) && return if redirect_to_shop?
redirect_to_cart_path && return unless valid_order_line_items?
end
@@ -41,6 +43,11 @@ module CheckoutCallbacks
@order.ship_address ||= finder.ship_address
end
def load_saved_credit_cards
@saved_credit_cards = spree_current_user&.credit_cards&.with_payment_profile.to_a
@selected_card = nil
end
def load_shipping_methods
@shipping_methods = available_shipping_methods.sort { |a, b| a.name.casecmp(b.name) }
end

View File

@@ -41,8 +41,8 @@ module OrderCompletion
main_app.order_path(@order, order_token: @order.token)
end
def order_failed_route(step: 'details')
main_app.checkout_step_path(step:)
def order_failed_route
main_app.checkout_path
end
def order_invalid_for_checkout?
@@ -60,8 +60,8 @@ module OrderCompletion
def process_payment_completion!
unless @order.process_payments!
payment_failed
return redirect_to order_failed_route(step: 'payment')
processing_failed
return redirect_to order_failed_route
end
if OrderWorkflow.new(@order).next && @order.complete?
@@ -82,20 +82,12 @@ module OrderCompletion
order_completion_reset(@order)
end
def payment_failed
notify_failure
end
def processing_failed
notify_failure
Checkout::PostCheckoutActions.new(@order).failure
end
def notify_failure(error = RuntimeError.new(order_processing_error))
def processing_failed(error = RuntimeError.new(order_processing_error))
Bugsnag.notify(error) do |payload|
payload.add_metadata :order, @order
end
flash[:error] = order_processing_error if flash.blank?
Checkout::PostCheckoutActions.new(@order).failure
end
def order_processing_error

View File

@@ -27,11 +27,8 @@ module PaymentGateways
redirect_to provider.express_checkout_url(pp_response, useraction: 'commit')
else
flash[:error] =
Spree.t(
'flash.generic_error',
scope: 'paypal',
reasons: pp_response.errors.map(&:long_message).join(" "),
)
Spree.t('flash.generic_error', scope: 'paypal',
reasons: pp_response.errors.map(&:long_message).join(" "))
redirect_to main_app.checkout_step_path(:payment)
end
rescue SocketError

View File

@@ -24,13 +24,14 @@ class SplitCheckoutController < ::BaseController
def edit
redirect_to_step_based_on_order unless params[:step]
check_step if params[:step]
recalculate_tax if params[:step] == "summary"
return if available_shipping_methods.any?
flash[:error] = I18n.t('split_checkout.errors.no_shipping_methods_available')
flash_error_when_no_shipping_method_available if available_shipping_methods.none?
end
def update
return process_voucher if params[:apply_voucher].present?
if confirm_order || update_order
return if performed?
@@ -60,6 +61,27 @@ class SplitCheckoutController < ::BaseController
replace("#flashes", partial("shared/flashes", locals: { flashes: flash }))
end
def render_voucher_section_or_redirect
respond_to do |format|
format.cable_ready { render_voucher_section }
format.html { redirect_to checkout_step_path(:payment) }
end
end
# Using the power of cable_car we replace only the #voucher_section instead of reloading the page
def render_voucher_section
render(
status: :ok,
cable_ready: cable_car.replace(
"#voucher-section",
partial(
"split_checkout/voucher_section",
locals: { order: @order, voucher_adjustment: @order.voucher_adjustments.first }
)
)
)
end
def order_error_messages
# Remove ship_address.* errors if no shipping method is not selected
remove_ship_address_errors if no_ship_address_needed?
@@ -104,6 +126,10 @@ class SplitCheckoutController < ::BaseController
end
end
def flash_error_when_no_shipping_method_available
flash[:error] = I18n.t('split_checkout.errors.no_shipping_methods_available')
end
def check_payments_adjustments
@order.payments.each(&:ensure_correct_adjustment)
end
@@ -176,6 +202,39 @@ class SplitCheckoutController < ::BaseController
selected_shipping_method.first.require_ship_address == false
end
def process_voucher
if add_voucher
render_voucher_section_or_redirect
elsif @order.errors.present?
render_error
end
end
def add_voucher
if params.dig(:order, :voucher_code).blank?
@order.errors.add(:voucher, I18n.t('split_checkout.errors.voucher_not_found'))
return false
end
# Fetch Voucher
voucher = Voucher.find_by(code: params[:order][:voucher_code], enterprise: @order.distributor)
if voucher.nil?
@order.errors.add(:voucher, I18n.t('split_checkout.errors.voucher_not_found'))
return false
end
adjustment = voucher.create_adjustment(voucher.code, @order)
if !adjustment.valid?
@order.errors.add(:voucher, I18n.t('split_checkout.errors.add_voucher_error'))
adjustment.errors.each { |error| @order.errors.import(error) }
return false
end
true
end
def summary_step?
params[:step] == "summary"
end
@@ -203,7 +262,6 @@ class SplitCheckoutController < ::BaseController
def validate_payment!
return true if params.dig(:order, :payments_attributes, 0, :payment_method_id).present?
return true if @order.zero_priced_order?
@order.errors.add :payment_method, I18n.t('split_checkout.errors.select_a_payment_method')
end
@@ -250,4 +308,18 @@ class SplitCheckoutController < ::BaseController
redirect_to checkout_step_path(:payment) if params[:step] == "summary"
end
end
def recalculate_tax
@order.create_tax_charge!
@order.update_order!
apply_voucher if @order.voucher_adjustments.present?
end
def apply_voucher
VoucherAdjustmentsService.calculate(@order)
# update order to take into account the voucher we applied
@order.update_order!
end
end

View File

@@ -33,7 +33,7 @@ module Spree
def model_class
const_name = controller_name.classify
return "Spree::#{const_name}".constantize if Object.const_defined?("Spree::#{const_name}")
return "Spree::#{const_name}".constantize if Spree.const_defined?(const_name)
nil
end

View File

@@ -6,7 +6,7 @@ module Spree
# This will make resource controller redirect correctly after deleting product images.
# This can be removed after upgrading to Spree 2.1.
# See here https://github.com/spree/spree/commit/334a011d2b8e16355e4ae77ae07cd93f7cbc8fd1
belongs_to 'spree/product'
belongs_to 'spree/product', find_by: :permalink
before_action :load_data
@@ -80,7 +80,7 @@ module Spree
end
def load_data
@product = Product.find(params[:product_id])
@product = Product.find_by(permalink: params[:product_id])
end
def set_viewable

View File

@@ -4,42 +4,15 @@ module Spree
module Admin
class InvoicesController < Spree::Admin::BaseController
respond_to :json
def index
@order = Spree::Order.find_by(number: params[:order_id])
authorize! :invoice, @order
end
authorize_resource class: false
def create
Spree::Order.where(id: params[:order_ids]).find_each do |order|
authorize! :invoice, order
end
invoice_service = BulkInvoiceService.new
invoice_service.start_pdf_job(params[:order_ids])
render json: invoice_service.id, status: :ok
end
def generate
@order = Order.find_by(number: params[:order_id])
authorize! :invoice, @order
@comparator = OrderInvoiceComparator.new(@order)
if @comparator.can_generate_new_invoice?
@order.invoices.create!(
date: Time.zone.today,
number: @order.invoices.count + 1,
data: invoice_data
)
elsif @comparator.can_update_latest_invoice?
@order.invoices.last.update!(
date: Time.zone.today,
data: invoice_data
)
end
redirect_back(fallback_location: spree.admin_dashboard_path)
end
def show
invoice_id = params[:id]
invoice_pdf = BulkInvoiceService.new.filepath(invoice_id)
@@ -56,12 +29,6 @@ module Spree
render json: { created: false }, status: :unprocessable_entity
end
end
protected
def invoice_data
@invoice_data ||= InvoiceDataGenerator.new(@order).generate
end
end
end
end

View File

@@ -24,6 +24,7 @@ module Spree
end
refresh_shipment_rates
recalculate_taxes
OrderWorkflow.new(@order).advance_to_payment
flash[:success] = Spree.t('customer_details_updated')
@@ -51,6 +52,15 @@ module Spree
@order.shipments.map(&:refresh_rates)
end
def recalculate_taxes
# If the order's address has been changed, the tax zone could be different,
# which means a different set of tax rates might be applicable.
@order.create_tax_charge!
Spree::TaxRate.adjust(@order, @order.adjustments.admin)
@order.update_totals_and_states
end
def order_params
params.require(:order).permit(
:email,

View File

@@ -99,10 +99,6 @@ module Spree
end
def print
if OpenFoodNetwork::FeatureToggle.enabled?(:invoices)
@order = @order.invoices.find(params[:invoice_id]).presenter
end
render_with_wicked_pdf InvoiceRenderer.new.args(@order)
end

View File

@@ -3,7 +3,7 @@
module Spree
module Admin
class ProductPropertiesController < ::Admin::ResourceController
belongs_to 'spree/product'
belongs_to 'spree/product', find_by: :permalink
before_action :find_properties
before_action :setup_property, only: [:index]

View File

@@ -103,7 +103,7 @@ module Spree
protected
def find_resource
Product.find(params[:id])
Product.find_by!(permalink: params[:id])
end
def location_after_save

View File

@@ -5,7 +5,7 @@ require 'open_food_network/scope_variants_for_search'
module Spree
module Admin
class VariantsController < ::Admin::ResourceController
belongs_to 'spree/product'
belongs_to 'spree/product', find_by: :permalink
def index
@url_filters = ::ProductFilters.new.extract(request.query_parameters)

View File

@@ -33,8 +33,8 @@ module Spree
render status: :ok, cable_ready: cable_car.
inner_html(
"#login-feedback",
partial("layouts/alert",
locals: { type: "alert", message: t('devise.failure.already_registered') })
partial("layouts/alert",
locals: { type: "alert", message: t('devise.failure.already_registered') })
).
dispatch_event(name: "login:modal:open")
else
@@ -48,9 +48,9 @@ module Spree
if @user.save
render cable_ready: cable_car.inner_html(
"#signup-feedback",
partial("layouts/alert",
locals: { type: "success",
message: t('devise.user_registrations.spree_user.signed_up_but_unconfirmed') })
partial("layouts/alert",
locals: { type: "success",
message: t('devise.user_registrations.spree_user.signed_up_but_unconfirmed') })
)
else
render status: :unprocessable_entity, cable_ready: cable_car.morph(

View File

@@ -24,8 +24,8 @@ class UserConfirmationsController < DeviseController
else
render cable_ready: cable_car.inner_html(
"##{params[:tab] || 'forgot'}-feedback",
partial("layouts/alert",
locals: { type: "success", message: t("devise.confirmations.send_instructions") })
partial("layouts/alert",
locals: { type: "success", message: t("devise.confirmations.send_instructions") })
)
return
end

View File

@@ -1,81 +1,32 @@
# frozen_string_literal: true
class VoucherAdjustmentsController < BaseController
before_action :set_order
def create
if add_voucher
VoucherAdjustmentsService.calculate(@order)
@order.update_totals_and_states
update_payment_section
elsif @order.errors.present?
render_error
end
end
include CablecarResponses
def destroy
# An order can have more than one adjustment linked to one voucher
adjustment = @order.voucher_adjustments.find_by(id: params[:id])
if adjustment.present?
@order.voucher_adjustments.where(originator_id: adjustment.originator_id)&.destroy_all
end
@order = current_order
update_payment_section
@order.voucher_adjustments.find_by(id: params[:id])&.destroy
respond_to do |format|
format.cable_ready { render_voucher_section }
format.html { redirect_to checkout_step_path(:payment) }
end
end
private
def set_order
@order = current_order
end
def add_voucher
if voucher_params[:voucher_code].blank?
@order.errors.add(:voucher_code, I18n.t('split_checkout.errors.voucher_not_found'))
return false
end
voucher = Voucher.find_by(code: voucher_params[:voucher_code], enterprise: @order.distributor)
if voucher.nil?
@order.errors.add(:voucher_code, I18n.t('split_checkout.errors.voucher_not_found'))
return false
end
adjustment = voucher.create_adjustment(voucher.code, @order)
unless adjustment.valid?
@order.errors.add(:voucher_code, I18n.t('split_checkout.errors.add_voucher_error'))
adjustment.errors.each { |error| @order.errors.import(error) }
return false
end
true
end
def update_payment_section
render cable_ready: cable_car.replace(
selector: "#checkout-payment-methods",
html: render_to_string(partial: "split_checkout/payment", locals: { step: "payment" })
)
end
def render_error
flash.now[:error] = @order.errors.full_messages.to_sentence
render status: :unprocessable_entity, cable_ready: cable_car.
replace("#flashes", partial("shared/flashes", locals: { flashes: flash })).
replace(
# Using the power of cable_car we replace only the #voucher_section instead of reloading the page
def render_voucher_section
render(
status: :ok,
cable_ready: cable_car.replace(
"#voucher-section",
partial(
"split_checkout/voucher_section",
locals: { order: @order, voucher_adjustment: @order.voucher_adjustments.first }
)
)
end
def voucher_params
params.require(:order).permit(:voucher_code)
)
end
end

View File

@@ -1,74 +0,0 @@
# frozen_string_literal: true
class EnterpriseFeesBulkUpdate
include ActiveModel::Model
attr_reader :errors
validate :check_enterprise_fee_input
validate :check_calculators_compatibility_with_taxes
def initialize(params)
@errors = ActiveModel::Errors.new self
@params = params
end
def save
return false unless valid?
@enterprise_fee_set = Sets::EnterpriseFeeSet.new(enterprise_fee_bulk_params)
unless @enterprise_fee_set.save
@enterprise_fee_set.errors.each do |error|
@errors.add(error.attribute, error.type)
end
return false
end
true
end
private
def check_enterprise_fee_input
enterprise_fee_bulk_params['collection_attributes'].each do |_, fee_row|
enterprise_fees = fee_row['calculator_attributes']&.slice(
:preferred_flat_percent, :preferred_amount,
:preferred_first_item, :preferred_additional_item,
:preferred_minimal_amount, :preferred_normal_amount,
:preferred_discount_amount, :preferred_per_unit
)
next unless enterprise_fees
enterprise_fees.each do |_, enterprise_amount|
unless enterprise_amount.nil? || Float(enterprise_amount, exception: false)
@errors.add(:base, I18n.t(:calculator_preferred_value_error))
end
end
end
end
def check_calculators_compatibility_with_taxes
enterprise_fee_bulk_params['collection_attributes'].each do |_, enterprise_fee|
next unless enterprise_fee['inherits_tax_category'] == "true"
next unless EnterpriseFee::PER_ORDER_CALCULATORS.include?(enterprise_fee['calculator_type'])
@errors.add(
:base,
I18n.t(
'activerecord.errors.models.enterprise_fee.inherit_tax_requires_per_item_calculator'
)
)
end
end
def enterprise_fee_bulk_params
@params.require(:sets_enterprise_fee_set).permit(
collection_attributes: [
:id, :enterprise_id, :fee_type, :name, :tax_category_id,
:inherits_tax_category, :calculator_type,
{ calculator_attributes: PermittedAttributes::Calculator.attributes }
]
)
end
end

View File

@@ -7,8 +7,7 @@ module AngularFormHelper
container.map do |element|
html_attributes = option_html_attributes(element)
text, value = option_text_and_value(element).map(&:to_s)
%(<option value="#{ERB::Util.html_escape(value)}"\
#{html_attributes}>#{ERB::Util.html_escape(text)}</option>)
%(<option value="#{ERB::Util.html_escape(value)}"#{html_attributes}>#{ERB::Util.html_escape(text)}</option>)
end.join("\n").html_safe
end

View File

@@ -38,15 +38,15 @@ module ApplicationHelper
end.join("\n").html_safe
end
def ng_form_for(name, *args, &)
def ng_form_for(name, *args, &block)
options = args.extract_options!
form_for(name, *(args << options.merge(builder: AngularFormBuilder)), &)
form_for(name, *(args << options.merge(builder: AngularFormBuilder)), &block)
end
# Pass URL helper calls on to spree where applicable so that we don't need to use
# spree.foo_path in any view rendered from non-spree-namespaced controllers.
def method_missing(method, *args, &)
def method_missing(method, *args, &block)
if method.to_s.end_with?('_path', '_url') && spree.respond_to?(method)
spree.public_send(method, *args)
else

View File

@@ -120,8 +120,6 @@ module CheckoutHelper
end
def payment_or_shipping_price(method, order)
return unless method
price = method.compute_amount(order)
if price.zero?
t('checkout_method_free')
@@ -141,7 +139,7 @@ module CheckoutHelper
def stripe_card_options(cards)
cards.map do |cc|
[
"#{cc.brand} #{cc.last_digits} #{I18n.t(:card_expiry_abbreviation)}:" \
"#{cc.brand} #{cc.last_digits} #{I18n.t(:card_expiry_abbreviation)}:"\
"#{cc.month.to_s.rjust(2, '0')}/#{cc.year}", cc.id
]
end

View File

@@ -77,8 +77,7 @@ module EnterprisesHelper
end
def subscriptions_enabled?
spree_current_user.admin? ||
spree_current_user.enterprises.where(enable_subscriptions: true).any?
spree_current_user.admin? || spree_current_user.enterprises.where(enable_subscriptions: true).any?
end
def enterprise_url_selector(enterprise)

View File

@@ -1,11 +1,11 @@
# frozen_string_literal: true
module LinkHelper
def link_to_service(baseurl, name, html_options = {}, &)
def link_to_service(baseurl, name, html_options = {}, &block)
return if name.blank?
html_options = html_options.merge target: '_blank'
link_to(ext_url(baseurl, name), html_options, &)
link_to ext_url(baseurl, name), html_options, &block
end
def ext_url(prefix, url)

View File

@@ -36,13 +36,6 @@ module OrderCyclesHelper
shipping_and_payment_methods: true
end
def distributors_with_editable_shipping_and_payment_methods(order_cycle)
return order_cycle.distributors if Enterprise
.managed_by(spree_current_user).exists?(order_cycle.coordinator.id)
order_cycle.distributors.managed_by(spree_current_user)
end
def order_cycle_status_class(order_cycle)
if order_cycle.undated?
'undated'

View File

@@ -8,14 +8,4 @@ module OrderHelper
def outstanding_balance_label(order)
order.outstanding_balance.label
end
def show_generate_invoice_button?(order)
comparator = order_comparator(order)
comparator.can_generate_new_invoice? ||
comparator.can_update_latest_invoice?
end
def order_comparator(order)
OrderInvoiceComparator.new(order)
end
end

View File

@@ -32,28 +32,7 @@ module ReportsHelper
end
end
def fee_name_options(orders)
EnterpriseFee.where(id: enterprise_fee_ids(orders))
.pluck(:name, :id)
end
def fee_owner_options(orders)
Enterprise.where(id: enterprise_fee_owner_ids(orders))
.pluck(:name, :id)
end
def currency_symbol
Spree::Money.currency_symbol
end
def enterprise_fee_owner_ids(orders)
EnterpriseFee.where(id: enterprise_fee_ids(orders))
.pluck(:enterprise_id)
end
def enterprise_fee_ids(orders)
Spree::Adjustment.enterprise_fee
.where(order_id: orders.map(&:id))
.pluck(:originator_id)
end
end

View File

@@ -3,14 +3,14 @@
module Spree
module Admin
module BaseHelper
def field_container(model, method, options = {}, &)
def field_container(model, method, options = {}, &block)
css_classes = options[:class].to_a
css_classes << 'field'
if error_message_on(model, method).present?
css_classes << 'withError'
end
content_tag(:div,
capture(&),
capture(&block),
class: css_classes.join(' '),
id: "#{model}_#{method}_field")
end

View File

@@ -31,7 +31,6 @@ module Spree
end
def invoice_links
return [] if OpenFoodNetwork::FeatureToggle.enabled?(:invoices)
return [] unless Spree::Config[:enable_invoices?]
[send_invoice_link, print_invoice_link]

View File

@@ -18,8 +18,9 @@ module Spree
def changeable_orders
# Only returns open order for the current user + shop + oc combo
return @changeable_orders unless @changeable_orders.nil?
return @changeable_orders = [] unless spree_current_user &&
current_distributor && current_order_cycle
unless spree_current_user && current_distributor && current_order_cycle
return @changeable_orders = []
end
return @changeable_orders = [] unless current_distributor.allow_order_changes?
@changeable_orders = Spree::Order.complete.where(

View File

@@ -5,13 +5,11 @@ module Spree
def payment_method(payment)
# hack to allow us to retrieve the name of a "deleted" payment method
id = payment.payment_method_id
return if id.nil?
Spree::PaymentMethod.find_with_destroyed(id)
end
def payment_method_name(payment)
payment_method(payment)&.name
payment_method(payment).name
end
end
end

View File

@@ -7,11 +7,4 @@ class ApplicationJob < ActiveJob::Base
# Most jobs are safe to ignore if the underlying records are no longer available
# discard_on ActiveJob::DeserializationError
private
def enable_active_storage_urls
ActiveStorage::Current.url_options ||=
Rails.application.config.action_controller.default_url_options
end
end

View File

@@ -2,14 +2,9 @@
# Renders a report and stores it in a given blob.
class ReportJob < ApplicationJob
include CableReady::Broadcaster
delegate :render, to: ActionController::Base
before_perform :enable_active_storage_urls
NOTIFICATION_TIME = 5.seconds
def perform(report_class:, user:, params:, format:, blob:, channel: nil)
def perform(report_class, user, params, format, blob)
start_time = Time.zone.now
report = report_class.new(user, params, render: true)
@@ -19,8 +14,6 @@ class ReportJob < ApplicationJob
execution_time = Time.zone.now - start_time
email_result(user, blob) if execution_time > NOTIFICATION_TIME
broadcast_result(channel, format, blob) if channel
end
def email_result(user, blob)
@@ -29,17 +22,4 @@ class ReportJob < ApplicationJob
blob: blob,
).report_ready.deliver_later
end
def broadcast_result(channel, format, blob)
cable_ready[channel].inner_html(
selector: "#report-table",
html: actioncable_content(format, blob)
).broadcast
end
def actioncable_content(format, blob)
return blob.result if format.to_sym == :html
render(partial: "admin/reports/download", locals: { file_url: blob.expiring_service_url })
end
end

View File

@@ -41,9 +41,7 @@ class SubscriptionConfirmJob < ApplicationJob
def recently_closed_order_cycles
OrderCycle.closed.where(
'order_cycles.orders_close_at BETWEEN (?) AND (?) ' \
'OR order_cycles.updated_at BETWEEN (?) AND (?)',
1.hour.ago, Time.zone.now, 1.hour.ago, Time.zone.now
'order_cycles.orders_close_at BETWEEN (?) AND (?) OR order_cycles.updated_at BETWEEN (?) AND (?)', 1.hour.ago, Time.zone.now, 1.hour.ago, Time.zone.now
)
end

View File

@@ -13,7 +13,7 @@ module Spree
@edit_password_reset_url = spree.
edit_spree_user_password_url(reset_password_token: token)
subject = "#{Spree::Config[:site_name]} " \
"#{I18n.t('spree.user_mailer.reset_password_instructions.subject')}"
"#{I18n.t('spree.user_mailer.reset_password_instructions.subject')}"
I18n.with_locale valid_locale(user) do
mail(to: user.email, subject: subject)

View File

@@ -38,16 +38,14 @@ class SubscriptionMailer < ApplicationMailer
@shop = Enterprise.find(summary.shop_id)
@summary = summary
mail(to: @shop.contact.email,
subject: "#{Spree::Config[:site_name]} " \
"#{t('subscription_mailer.placement_summary_email.subject')}")
subject: "#{Spree::Config[:site_name]} #{t('subscription_mailer.placement_summary_email.subject')}")
end
def confirmation_summary_email(summary)
@shop = Enterprise.find(summary.shop_id)
@summary = summary
mail(to: @shop.contact.email,
subject: "#{Spree::Config[:site_name]} " \
"#{t('subscription_mailer.confirmation_summary_email.subject')}")
subject: "#{Spree::Config[:site_name]} #{t('subscription_mailer.confirmation_summary_email.subject')}")
end
private

View File

@@ -15,16 +15,6 @@ class ApplicationRecord < ActiveRecord::Base
ENV["S3_BUCKET"].present? ? :amazon_public : :local
end
# We might have a development environment without S3 but with a database
# dump pointing to S3 images. Accessing the service fails then.
def image_variant_url_for(variant)
if ENV["S3_BUCKET"].present? && variant.service.public?
variant.processed.url
else
url_for(variant)
end
end
def url_for(*args)
Rails.application.routes.url_helpers.url_for(*args)
end

View File

@@ -35,7 +35,7 @@ module Calculator
private
def compute_for(count)
(count * preferred_additional_item.to_f) + preferred_first_item.to_f
count * preferred_additional_item.to_f + preferred_first_item.to_f
end
end
end

View File

@@ -4,7 +4,7 @@ require 'active_support/concern'
# This module is an adapter for OFN to work with Spree 2 code.
#
# Although Spree 2 supports multiple shipments per order, in OFN we have only 1 shipment per order.
# Although Spree 2 supports multiple shipments per order, in OFN we have only one shipment per order.
# A shipment is associated to a shipping_method through a selected shipping_rate.
# See https://github.com/openfoodfoundation/openfoodnetwork/wiki/Spree-Upgrade:-Migration-to-multiple-shipments
# for details.
@@ -30,8 +30,7 @@ module OrderShipment
shipments.first.shipping_method
end
# Finds the shipment's shipping_rate for the given shipping_method_id
# and selects that shipping_rate.
# Finds the shipment's shipping_rate for the given shipping_method_id and selects that shipping_rate.
# If the selection is successful, it persists it in the database by saving the shipment.
# If it fails, it does not clear the current shipping_method selection.
#

View File

@@ -6,8 +6,7 @@ module ProductStock
extend ActiveSupport::Concern
def on_demand
raise 'Cannot determine product on_demand value of product with multiple variants' if
variants.size > 1
raise 'Cannot determine product on_demand value of product with multiple variants' if variants.size > 1
variants.first.on_demand
end

View File

@@ -42,8 +42,7 @@ module VariantStock
# Checks whether this variant is produced on demand.
def on_demand
# A variant that has not been saved yet or has been soft-deleted doesn't have a stock item
# This provides a default value for variant.on_demand
# using Spree::StockLocation.backorderable_default
# This provides a default value for variant.on_demand using Spree::StockLocation.backorderable_default
return Spree::StockLocation.first.backorderable_default if new_record? || deleted?
# This can be removed unless we have seen this error in Bugsnag recently
@@ -77,10 +76,8 @@ module VariantStock
end
end
# Moving Spree::Stock::Quantifier.can_supply? to the variant enables us
# to override this behaviour for variant overrides
# We can have this responsibility here in the variant because there is
# only one stock item per variant
# Moving Spree::Stock::Quantifier.can_supply? to the variant enables us to override this behaviour for variant overrides
# We can have this responsibility here in the variant because there is only one stock item per variant
#
# Here we depend only on variant.total_on_hand and variant.on_demand.
# This way, variant_overrides only need to override variant.total_on_hand and variant.on_demand.
@@ -88,10 +85,8 @@ module VariantStock
on_demand || total_on_hand >= quantity
end
# Moving Spree::StockLocation.fill_status to the variant enables us
# to override this behaviour for variant overrides
# We can have this responsibility here in the variant because there is
# only one stock item per variant
# Moving Spree::StockLocation.fill_status to the variant enables us to override this behaviour for variant overrides
# We can have this responsibility here in the variant because there is only one stock item per variant
#
# Here we depend only on variant.total_on_hand and variant.on_demand.
# This way, variant_overrides only need to override variant.total_on_hand and variant.on_demand.
@@ -107,8 +102,7 @@ module VariantStock
[on_hand, backordered]
end
# We can have this responsibility here in the variant because there is
# only one stock item per variant
# We can have this responsibility here in the variant because there is only one stock item per variant
#
# This enables us to override this behaviour for variant overrides
def move(quantity, originator = nil)
@@ -144,8 +138,7 @@ module VariantStock
# Overwrites stock_item.count_on_hand
#
# Calling stock_item.adjust_count_on_hand will bypass filling backorders
# and creating stock movements
# Calling stock_item.adjust_count_on_hand will bypass filling backorders and creating stock movements
# If that was required we could call self.move
def overwrite_stock_levels(new_level)
stock_item.adjust_count_on_hand(new_level.to_i - stock_item.count_on_hand)

View File

@@ -34,17 +34,10 @@ class Customer < ApplicationRecord
validates :code, uniqueness: { scope: :enterprise_id, allow_nil: true }
validates :email, presence: true, 'valid_email_2/email': true,
uniqueness: {
scope: :enterprise_id,
message: I18n.t('validation_msg_is_associated_with_an_exising_customer')
}
uniqueness: { scope: :enterprise_id, message: I18n.t('validation_msg_is_associated_with_an_exising_customer') }
scope :of, ->(enterprise) { where(enterprise_id: enterprise) }
scope :managed_by, ->(user) {
user&.persisted? ? where(user: user).or(of(Enterprise.managed_by(user))) : none
}
scope :created_manually, -> { where(created_manually: true) }
scope :visible, -> { where(id: Spree::Order.complete.select(:customer_id)).or(created_manually) }
scope :managed_by, ->(user) { user&.persisted? ? where(user: user).or(of(Enterprise.managed_by(user))) : none }
before_create :associate_user

View File

@@ -51,9 +51,9 @@ class Enterprise < ApplicationRecord
belongs_to :owner, class_name: 'Spree::User',
inverse_of: :owned_enterprises
has_many :distributor_payment_methods,
inverse_of: :distributor, foreign_key: :distributor_id
inverse_of: :distributor, foreign_key: :distributor_id
has_many :distributor_shipping_methods,
inverse_of: :distributor, foreign_key: :distributor_id
inverse_of: :distributor, foreign_key: :distributor_id
has_many :payment_methods, through: :distributor_payment_methods
has_many :shipping_methods, through: :distributor_shipping_methods
has_many :customers
@@ -114,11 +114,11 @@ class Enterprise < ApplicationRecord
validate :shopfront_taxons
validate :shopfront_producers
validate :enforce_ownership_limit, if: lambda { owner_id_changed? && !owner_id.nil? }
validates :instagram,
format: {
with: VALID_INSTAGRAM_REGEX,
message: Spree.t('errors.messages.invalid_instagram_url')
}, allow_blank: true
validates :instagram,
format: {
with: VALID_INSTAGRAM_REGEX,
message: Spree.t('errors.messages.invalid_instagram_url')
}, allow_blank: true
validate :validate_white_label_logo_link
before_validation :initialize_permalink, if: lambda { permalink.nil? }
@@ -359,7 +359,7 @@ class Enterprise < ApplicationRecord
def category
# Make this crazy logic human readable so we can argue about it sanely.
cat = is_primary_producer ? "producer_" : "non_producer_"
cat << ("sells_" + sells)
cat << "sells_" + sells
# Map backend cases to front end cases.
case cat
@@ -459,10 +459,10 @@ class Enterprise < ApplicationRecord
def image_url_for(image, name)
return unless image.variable?
return image.variant(name).processed.url if image.attachment.service.name == :amazon_public
image_variant_url_for(image.variant(name))
rescue ActiveStorage::Error, MiniMagick::Error, ActionView::Template::Error => e
Bugsnag.notify "Enterprise ##{id} #{image.try(:name)} error: #{e.message}"
url_for(image.variant(name))
rescue ActiveStorage::Error => e
Rails.logger.error(e.message)
nil
@@ -493,7 +493,7 @@ class Enterprise < ApplicationRecord
end
def correct_whatsapp_url(phone_number)
phone_number && ("https://wa.me/" + phone_number.tr('+ ', ''))
phone_number && "https://wa.me/" + phone_number.tr('+ ', '')
end
def correct_instagram_url(url)
@@ -505,10 +505,7 @@ class Enterprise < ApplicationRecord
end
def set_unused_address_fields
if address.present?
address.firstname = address.lastname = address.phone =
address.company = 'unused'
end
address.firstname = address.lastname = address.phone = address.company = 'unused' if address.present?
business_address.first_name = business_address.last_name = 'unused' if business_address.present?
end

View File

@@ -1,22 +0,0 @@
# frozen_string_literal: true
class Invoice < ApplicationRecord
belongs_to :order, class_name: 'Spree::Order'
serialize :data, Hash
before_validation :serialize_order
after_create :cancel_previous_invoices
def presenter
@presenter ||= Invoice::DataPresenter.new(self)
end
def serialize_order
return data unless data.empty?
self.data = Invoice::OrderSerializer.new(order).serializable_hash
end
def cancel_previous_invoices
order.invoices.where.not(id:).update_all(cancelled: true)
end
end

View File

@@ -1,133 +0,0 @@
# frozen_string_literal: true
class Invoice
class DataPresenter
attr_reader :invoice
delegate :data, :date, to: :invoice
FINALIZED_NON_SUCCESSFUL_STATES = %w(canceled returned).freeze
extend Invoice::DataPresenterAttributes
attributes :additional_tax_total, :currency, :included_tax_total, :payment_total,
:shipping_method_id, :state, :total, :number, :note, :special_instructions,
:completed_at
attributes_with_presenter :bill_address, :customer, :distributor, :ship_address,
:shipping_method, :order_cycle
array_attribute :sorted_line_items, class_name: 'LineItem'
array_attribute :all_eligible_adjustments, class_name: 'Adjustment'
array_attribute :payments, class_name: 'Payment'
# if any of the following attributes is updated, a new invoice should be generated
invoice_generation_attributes :additional_tax_total, :all_eligible_adjustments, :bill_address,
:included_tax_total, :payments, :payment_total, :ship_address,
:shipping_method_id, :sorted_line_items, :total
# if any of the following attributes is updated, the latest invoice should be updated
invoice_update_attributes :note, :special_instructions, :state,
:all_eligible_adjustments, :payments
def initialize(invoice)
@invoice = invoice
end
def has_taxes_included
included_tax_total > 0
end
def total_tax
additional_tax_total + included_tax_total
end
def order_completed_at
return nil if data[:completed_at].blank?
Time.zone.parse(data[:completed_at])
end
def checkout_adjustments(exclude: [], reject_zero_amount: true)
adjustments = all_eligible_adjustments
adjustments.reject! { |a| a.originator_type == 'Spree::TaxRate' }
if exclude.include? :line_item
adjustments.reject! { |a|
a.adjustable_type == 'Spree::LineItem'
}
end
if reject_zero_amount
adjustments.reject! { |a| a.amount == 0 }
end
adjustments
end
def display_checkout_taxes_hash
totals = OrderTaxAdjustmentsFetcher.new(nil).totals(all_tax_adjustments)
totals.map do |tax_rate, tax_amount|
{
amount: Spree::Money.new(tax_amount, currency: order.currency),
percentage: number_to_percentage(tax_rate.amount * 100, precision: 1),
rate_amount: tax_rate.amount,
}
end.sort_by { |tax| tax[:rate_amount] }
end
def all_tax_adjustments
all_eligible_adjustments.select { |a| a.originator_type == 'Spree::TaxRate' }
end
def invoice_date
date
end
def paid?
data[:payment_state] == 'paid' || data[:payment_state] == 'credit_owed'
end
def outstanding_balance?
!new_outstanding_balance.zero?
end
def new_outstanding_balance
if state.in?(FINALIZED_NON_SUCCESSFUL_STATES)
-payment_total
else
total - payment_total
end
end
def outstanding_balance_label
new_outstanding_balance.negative? ? I18n.t(:credit_owed) : I18n.t(:balance_due)
end
def last_payment
payments.max_by(&:created_at)
end
def last_payment_method
last_payment&.payment_method
end
def display_outstanding_balance
Spree::Money.new(new_outstanding_balance, currency: currency)
end
def display_checkout_tax_total
Spree::Money.new(total_tax, currency: currency)
end
def display_checkout_total_less_tax
Spree::Money.new(total - total_tax, currency: currency)
end
def display_total
Spree::Money.new(total, currency: currency)
end
end
end

View File

@@ -1,34 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Address < Invoice::DataPresenter::Base
attributes :firstname, :lastname, :address1, :address2, :city, :zipcode, :company, :phone
attributes_with_presenter :state
invoice_generation_attributes :firstname, :lastname, :address1, :address2, :city, :zipcode,
:company, :phone
def full_name
"#{firstname} #{lastname}".strip
end
def address_part1
render_address([address1, address2])
end
def address_part2
render_address([city, zipcode, state&.name])
end
def full_address
render_address([address1, address2, city, zipcode, state&.name])
end
private
def render_address(address_parts)
address_parts.compact_blank.join(', ')
end
end
end
end

View File

@@ -1,28 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Adjustment < Invoice::DataPresenter::Base
attributes :additional_tax_total, :adjustable_type, :amount, :currency, :included_tax_total,
:label, :originator_type
invoice_generation_attributes :additional_tax_total, :adjustable_type, :amount,
:included_tax_total
invoice_update_attributes :label
def display_amount
Spree::Money.new(amount, currency: currency)
end
def display_taxes(display_zero: false)
if included_tax_total.positive?
amount = Spree::Money.new(included_tax_total, currency: currency)
I18n.t(:tax_amount_included, amount: amount)
elsif additional_tax_total.positive?
Spree::Money.new(additional_tax_total, currency: currency)
elsif display_zero
Spree::Money.new(0.00, currency: currency)
end
end
end
end
end

View File

@@ -1,14 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Base
attr_reader :data
def initialize(data)
@data = data
end
extend Invoice::DataPresenterAttributes
end
end
end

View File

@@ -1,8 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class BillAddress < Invoice::DataPresenter::Address
end
end
end

View File

@@ -1,8 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class BusinessAddress < Invoice::DataPresenter::Address
end
end
end

View File

@@ -1,9 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Contact < Invoice::DataPresenter::Base
attributes :name, :email
end
end
end

View File

@@ -1,9 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Customer < Invoice::DataPresenter::Base
attributes :code, :email
end
end
end

View File

@@ -1,14 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Distributor < Invoice::DataPresenter::Base
attributes :name, :abn, :acn, :logo_url, :display_invoice_logo, :invoice_text, :email_address
attributes_with_presenter :contact, :address, :business_address
def display_invoice_logo?
display_invoice_logo == true
end
end
end
end

View File

@@ -1,33 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class LineItem < Invoice::DataPresenter::Base
attributes :added_tax, :currency, :included_tax, :price_with_adjustments, :quantity,
:variant_id
attributes_with_presenter :variant
invoice_generation_attributes :added_tax, :included_tax, :price_with_adjustments,
:quantity, :variant_id
delegate :name_to_display, :options_text, to: :variant
def display_amount_with_adjustments
Spree::Money.new((price_with_adjustments * quantity), currency: currency)
end
def single_display_amount_with_adjustments
Spree::Money.new(price_with_adjustments, currency: currency)
end
def display_line_items_taxes(display_zero: true)
if included_tax.positive?
Spree::Money.new( included_tax, currency: currency)
elsif added_tax.positive?
Spree::Money.new( added_tax, currency: currency)
elsif display_zero
Spree::Money.new(0.00, currency: currency)
end
end
end
end
end

View File

@@ -1,9 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class OrderCycle < Invoice::DataPresenter::Base
attributes :name
end
end
end

View File

@@ -1,25 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Payment < Invoice::DataPresenter::Base
attributes :amount, :currency, :state, :payment_method_id
attributes_with_presenter :payment_method
invoice_generation_attributes :amount, :payment_method_id
invoice_update_attributes :state
def created_at
datetime = data&.[](:created_at)
datetime.present? ? Time.zone.parse(datetime) : nil
end
def display_amount
Spree::Money.new(amount, currency: currency)
end
def payment_method_name
payment_method&.name
end
end
end
end

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class PaymentMethod < Invoice::DataPresenter::Base
attributes :id, :name, :description
invoice_generation_attributes :id
end
end
end

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Product < Invoice::DataPresenter::Base
attributes :name
attributes_with_presenter :supplier
end
end
end

View File

@@ -1,8 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class ShipAddress < Invoice::DataPresenter::Address
end
end
end

View File

@@ -1,10 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class ShippingMethod < Invoice::DataPresenter::Base
attributes :id, :name, :require_ship_address
invoice_generation_attributes :id
end
end
end

View File

@@ -1,9 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class State < Invoice::DataPresenter::Base
attributes :name
end
end
end

View File

@@ -1,9 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Supplier < Invoice::DataPresenter::Base
attributes :name
end
end
end

View File

@@ -1,16 +0,0 @@
# frozen_string_literal: false
class Invoice
class DataPresenter
class Variant < Invoice::DataPresenter::Base
attributes :id, :display_name, :options_text
attributes_with_presenter :product
def name_to_display
return product.name if display_name.blank?
display_name
end
end
end
end

View File

@@ -1,59 +0,0 @@
# frozen_string_literal: true
class Invoice
module DataPresenterAttributes
extend ActiveSupport::Concern
def attributes(*attributes, prefix: nil)
attributes.each do |attribute|
define_method([prefix, attribute].compact_blank.join("_")) do
data&.[](attribute)
end
end
end
def attributes_with_presenter(*attributes)
attributes.each do |attribute|
define_method(attribute) do
instance_variable = instance_variable_get("@#{attribute}")
return instance_variable if instance_variable
instance_variable_set("@#{attribute}",
Invoice::DataPresenter.const_get(
attribute.to_s.classify
).new(data&.[](attribute)))
end
end
end
def array_attribute(attribute_name, class_name: nil)
define_method(attribute_name) do
instance_variable = instance_variable_get("@#{attribute_name}")
return instance_variable if instance_variable
instance_variable_set("@#{attribute_name}",
data&.[](attribute_name)&.map { |item|
Invoice::DataPresenter.const_get(class_name).new(item)
})
end
end
# if one of the list attributes is updated, the invoice needs to be regenerated
def invoice_generation_attributes(*attributes)
define_method(:invoice_generation_values) do
attributes.map do |attribute|
public_send(attribute)
end
end
end
# if one of the list attributes is updated, the invoice needs to be updated
def invoice_update_attributes(*attributes)
define_method(:invoice_update_values) do
attributes.map do |attribute|
public_send(attribute)
end
end
end
end
end

View File

@@ -11,12 +11,7 @@ module ProductImport
include ActiveModel::Validations
attr_accessor :line_number, :valid, :validates_as, :product_object, :product_validations,
:on_hand_nil, :has_overrides, :units, :unscaled_units, :unit_type, :tax_category,
:shipping_category, :id, :product_id, :producer, :producer_id, :distributor,
:distributor_id, :name, :display_name, :sku, :unit_value, :unit_description,
:variant_unit, :variant_unit_scale, :variant_unit_name, :display_as, :category,
:primary_taxon_id, :price, :on_hand, :on_demand, :tax_category_id,
:shipping_category_id, :description, :import_date, :enterprise, :enterprise_id
:on_hand_nil, :has_overrides, :units, :unscaled_units, :unit_type, :tax_category, :shipping_category, :id, :product_id, :producer, :producer_id, :distributor, :distributor_id, :name, :display_name, :sku, :unit_value, :unit_description, :variant_unit, :variant_unit_scale, :variant_unit_name, :display_as, :category, :primary_taxon_id, :price, :on_hand, :on_demand, :tax_category_id, :shipping_category_id, :description, :import_date, :enterprise, :enterprise_id
NON_DISPLAY_ATTRIBUTES = ['id', 'product_id', 'unscaled_units', 'variant_id', 'enterprise',
'enterprise_id', 'producer_id', 'distributor_id', 'primary_taxon',
@@ -73,11 +68,7 @@ module ProductImport
def invalid_attributes
invalid_attrs = {}
errors = if @product_validations
@product_validations.messages.merge(self.errors.messages)
else
self.errors.messages
end
errors = @product_validations ? @product_validations.messages.merge(self.errors.messages) : self.errors.messages
errors.each do |attr, message|
invalid_attrs[attr.to_s] = "#{attr.to_s.capitalize} #{message.first}"
end

View File

@@ -73,13 +73,11 @@ module ProductImport
end
def units_and_unit_type_present?
@attrs.key?('units') && @attrs.key?('unit_type') && @attrs['units'].present? &&
@attrs['unit_type'].present?
@attrs.key?('units') && @attrs.key?('unit_type') && @attrs['units'].present? && @attrs['unit_type'].present?
end
def units_and_variant_unit_name_present?
@attrs.key?('units') && @attrs.key?('variant_unit_name') && @attrs['units'].present? &&
@attrs['variant_unit_name'].present?
@attrs.key?('units') && @attrs.key?('variant_unit_name') && @attrs['units'].present? && @attrs['variant_unit_name'].present?
end
def valid_unit_type?(unit_type)

View File

@@ -17,8 +17,7 @@ class ProxyOrder < ApplicationRecord
scope :canceled, -> { where('proxy_orders.canceled_at IS NOT NULL') }
scope :not_canceled, -> { where('proxy_orders.canceled_at IS NULL') }
scope :placed_and_open, -> {
joins(:order).not_closed
.where(spree_orders: { state: ['complete', 'resumed'] })
joins(:order).not_closed.where(spree_orders: { state: ['complete', 'resumed'] })
}
def state

View File

@@ -8,8 +8,7 @@ class Schedule < ApplicationRecord
has_many :coordinators, -> { distinct }, through: :order_cycles
scope :with_coordinator, lambda { |enterprise|
joins(:order_cycles).where('coordinator_id = ?', enterprise.id)
.select('DISTINCT schedules.*')
joins(:order_cycles).where('coordinator_id = ?', enterprise.id).select('DISTINCT schedules.*')
}
def current_or_next_order_cycle

View File

@@ -24,10 +24,10 @@ module Spree
can [:index, :read], Country
can :create, Order
can :read, Order do |order, token|
order.user == user || (order.token && token == order.token)
order.user == user || order.token && token == order.token
end
can :update, Order do |order, token|
order.user == user || (order.token && token == order.token)
order.user == user || order.token && token == order.token
end
can [:index, :read], Product
can [:index, :read], ProductProperty
@@ -191,8 +191,6 @@ module Spree
OpenFoodNetwork::Permissions.new(user).managed_product_enterprises.include? product.supplier
end
can [:admin, :index], :products_v3
can [:create], Spree::Variant
can [:admin, :index, :read, :edit,
:update, :search, :delete, :destroy], Spree::Variant do |variant|
@@ -277,7 +275,7 @@ module Spree
can [:admin, :bulk_management, :managed, :distribution], Spree::Order do
user.admin? || user.enterprises.any?(&:is_distributor)
end
can [:admin, :index, :create, :show, :poll, :generate], :invoice
can [:admin, :create, :show, :poll], :invoice
can [:admin, :visible], Enterprise
can [:admin, :index, :create, :update, :destroy], :line_item
can [:admin, :index, :create], Spree::LineItem

View File

@@ -67,8 +67,6 @@ module Spree
scope :charge, -> { where('amount >= 0') }
scope :credit, -> { where('amount < 0') }
scope :return_authorization, -> { where(originator_type: "Spree::ReturnAuthorization") }
scope :voucher, -> { where(originator_type: "Voucher") }
scope :non_voucher, -> { where.not(originator_type: "Voucher") }
scope :inclusive, -> { where(included: true) }
scope :additional, -> { where(included: false) }
scope :legacy_tax, -> { additional.tax.where(adjustable_type: "Spree::Order") }

Some files were not shown because too many files have changed in this diff Show More