mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-21 20:06:54 +00:00
Compare commits
300 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e190b87f12 | ||
|
|
ff2e0f4d45 | ||
|
|
9b0545c33f | ||
|
|
7631fd422e | ||
|
|
693789d526 | ||
|
|
d26b407801 | ||
|
|
fe257162b7 | ||
|
|
9e746d1b40 | ||
|
|
273f78b214 | ||
|
|
bd1d9892a2 | ||
|
|
cb825df75b | ||
|
|
bfcadfd7c0 | ||
|
|
255b5f1cd5 | ||
|
|
dffcd446fd | ||
|
|
1987f0b667 | ||
|
|
0b5efae8c4 | ||
|
|
36bb7cb317 | ||
|
|
49dbe1d039 | ||
|
|
c326aa6b23 | ||
|
|
ec91d717c7 | ||
|
|
da843d1ba1 | ||
|
|
2c4b3ab8fc | ||
|
|
1c7fbd1d2d | ||
|
|
8042dac74f | ||
|
|
ad1ce00223 | ||
|
|
d916ed2c96 | ||
|
|
da66a2947c | ||
|
|
646d3b8ed9 | ||
|
|
1f15f094ce | ||
|
|
adddee2c3c | ||
|
|
74e7bd5172 | ||
|
|
66859f44ca | ||
|
|
6f7a547e15 | ||
|
|
c057c72321 | ||
|
|
7a3b4d394b | ||
|
|
32e3fc0175 | ||
|
|
23c9410a25 | ||
|
|
7e9c5ea58b | ||
|
|
6c313a1b5a | ||
|
|
244a88a1cd | ||
|
|
589315780c | ||
|
|
2910082584 | ||
|
|
70b5fda632 | ||
|
|
9bf2dad343 | ||
|
|
05b3417f77 | ||
|
|
403aa6ac6f | ||
|
|
fbad3ee9f4 | ||
|
|
ddb8b2d08f | ||
|
|
42c9ee033a | ||
|
|
524634b4ea | ||
|
|
0b97171bb0 | ||
|
|
b0c7e29b0d | ||
|
|
3d7799df19 | ||
|
|
5f02d88a86 | ||
|
|
bdae8e6478 | ||
|
|
053ef05baf | ||
|
|
7fcb31d563 | ||
|
|
31a7374808 | ||
|
|
e5ce06ae39 | ||
|
|
5f64204d51 | ||
|
|
94b75540e4 | ||
|
|
6e489d7770 | ||
|
|
81b1169e77 | ||
|
|
4b558b4820 | ||
|
|
e224b8f63b | ||
|
|
80bb0606b4 | ||
|
|
499fcc791e | ||
|
|
30dae3c3ea | ||
|
|
af247c32a3 | ||
|
|
6f9dcf7e27 | ||
|
|
2d064bab64 | ||
|
|
b69eb9bdff | ||
|
|
f79c1879bd | ||
|
|
646d538a3d | ||
|
|
90288b8cbf | ||
|
|
c821b0a285 | ||
|
|
b95d798a27 | ||
|
|
e1e4aeac1f | ||
|
|
c7ae47053e | ||
|
|
5892ae1800 | ||
|
|
c37376d67e | ||
|
|
7aa9b164e6 | ||
|
|
74368f939b | ||
|
|
cb02cd39fe | ||
|
|
49ec5b2089 | ||
|
|
92ef5fe3d5 | ||
|
|
ae477b7e52 | ||
|
|
0e191e5fca | ||
|
|
64f9ea6fc0 | ||
|
|
058c6749da | ||
|
|
2d15ec4458 | ||
|
|
56eaa8bb98 | ||
|
|
118e18a78e | ||
|
|
cbced144d5 | ||
|
|
1e1f1e1e1b | ||
|
|
1d2115766a | ||
|
|
6814ef43f4 | ||
|
|
c9e8294561 | ||
|
|
82d0e1bf68 | ||
|
|
b16e541a81 | ||
|
|
c12d494de3 | ||
|
|
9be27842e1 | ||
|
|
2a7754edbf | ||
|
|
cfeafbfc51 | ||
|
|
1f8a9f9c76 | ||
|
|
b1893942ac | ||
|
|
ad59ed4d40 | ||
|
|
8491a167ed | ||
|
|
05b00f16ad | ||
|
|
78fdaa68c8 | ||
|
|
59277292fb | ||
|
|
e8813833fa | ||
|
|
354a7ab687 | ||
|
|
a5a1ee9bd9 | ||
|
|
ad3f78ef69 | ||
|
|
e02497b163 | ||
|
|
7d2d94398f | ||
|
|
0ecf004ff2 | ||
|
|
444f448207 | ||
|
|
d9381b23d7 | ||
|
|
6a9a2884d6 | ||
|
|
70edd4b898 | ||
|
|
b57a2befd9 | ||
|
|
fef9a78198 | ||
|
|
dd86736170 | ||
|
|
0d8c7ef118 | ||
|
|
20730b8768 | ||
|
|
ad7c69189b | ||
|
|
a5f44cb9b2 | ||
|
|
97d21d8cbe | ||
|
|
7afdd13b64 | ||
|
|
54c446f0a3 | ||
|
|
4454c90575 | ||
|
|
dd3a61acdf | ||
|
|
6d8ddd1eda | ||
|
|
b8e8ab15d1 | ||
|
|
bf1d2f3620 | ||
|
|
43026ddc6a | ||
|
|
18b83d2423 | ||
|
|
3a72aefc1c | ||
|
|
e855ea0dbd | ||
|
|
1eba950e19 | ||
|
|
9cd04c087e | ||
|
|
459d25e533 | ||
|
|
b06e562425 | ||
|
|
2936cfebca | ||
|
|
b8ad428b5d | ||
|
|
ca34d24847 | ||
|
|
6e581fce75 | ||
|
|
66041061fb | ||
|
|
e54c27c900 | ||
|
|
b3d3d6bf06 | ||
|
|
5876c52318 | ||
|
|
842f4ae40e | ||
|
|
342ef4e9eb | ||
|
|
210201514e | ||
|
|
2d3f18a71b | ||
|
|
9d284b7110 | ||
|
|
994f1ca6c6 | ||
|
|
f65e4797cf | ||
|
|
52aeec5ac4 | ||
|
|
7032b3f463 | ||
|
|
c26686b430 | ||
|
|
60c8f4ee20 | ||
|
|
25f396c126 | ||
|
|
0166abcd2a | ||
|
|
4cd0071dd4 | ||
|
|
32c96b72ad | ||
|
|
30701d61e2 | ||
|
|
45b712ddcd | ||
|
|
3153e99497 | ||
|
|
a2f263e081 | ||
|
|
3cb6a2617b | ||
|
|
420deca437 | ||
|
|
76aebf8a72 | ||
|
|
1ec570375f | ||
|
|
75c33b29d5 | ||
|
|
1bfff91c72 | ||
|
|
d469552afc | ||
|
|
3750898c44 | ||
|
|
d34f8900d7 | ||
|
|
e6cffde8fb | ||
|
|
910ded1a8c | ||
|
|
2555a9e710 | ||
|
|
f532c4712e | ||
|
|
3af28c4b5b | ||
|
|
63b864253d | ||
|
|
8efeec4301 | ||
|
|
bed33928e0 | ||
|
|
bb7a31b286 | ||
|
|
75b2fe1dd4 | ||
|
|
c0924fbe5e | ||
|
|
d72bc49409 | ||
|
|
06867ff7ea | ||
|
|
76a1fe7767 | ||
|
|
3363c523ea | ||
|
|
91628f8daa | ||
|
|
addf36a304 | ||
|
|
6a912b7d8c | ||
|
|
f3dfbab109 | ||
|
|
d01474ebcd | ||
|
|
a062a7b697 | ||
|
|
fe8b805e1f | ||
|
|
f3f43225cb | ||
|
|
65604f5b04 | ||
|
|
661bb29029 | ||
|
|
b725697972 | ||
|
|
b8546db1e5 | ||
|
|
bc25a5ecd6 | ||
|
|
71de96e0a6 | ||
|
|
23bcdc1cb7 | ||
|
|
60ac1c9fbe | ||
|
|
fcd4d073c4 | ||
|
|
df4cf4b768 | ||
|
|
3f39d94bd3 | ||
|
|
acfe3f6589 | ||
|
|
1717c5376b | ||
|
|
1426b6eeb7 | ||
|
|
188b2eb754 | ||
|
|
6e055ddbdf | ||
|
|
025fc784a8 | ||
|
|
fefd0239e6 | ||
|
|
0fa67c69fd | ||
|
|
44cbe55c96 | ||
|
|
56d3ac247d | ||
|
|
0ed08f8f9d | ||
|
|
667b49b7f1 | ||
|
|
dd6d1ea64b | ||
|
|
f0dd1885c9 | ||
|
|
05b6200c8f | ||
|
|
fcd6897240 | ||
|
|
c23c773942 | ||
|
|
7af960fceb | ||
|
|
45a0705379 | ||
|
|
6da1200b64 | ||
|
|
1cf31f4028 | ||
|
|
6df71f28ca | ||
|
|
9272d6d82f | ||
|
|
a8114e42a7 | ||
|
|
17e02e7304 | ||
|
|
6ba80f57b3 | ||
|
|
d90200fb3f | ||
|
|
08114b495a | ||
|
|
7b6b3d907c | ||
|
|
8011449ce7 | ||
|
|
cf9ffd8931 | ||
|
|
e6b9373570 | ||
|
|
ec44947b37 | ||
|
|
c0639b37bb | ||
|
|
38388be4da | ||
|
|
352f1ba900 | ||
|
|
7a0ecc777a | ||
|
|
aeefe841bf | ||
|
|
70757ccdef | ||
|
|
7450f8a530 | ||
|
|
618d597f6d | ||
|
|
b7f969eed9 | ||
|
|
b28e30cb6c | ||
|
|
681eee9309 | ||
|
|
6937a133ae | ||
|
|
ddc45e1cd8 | ||
|
|
28a11f1fee | ||
|
|
1c4febd332 | ||
|
|
9e1de75db6 | ||
|
|
8829f6ad03 | ||
|
|
6212cd4d07 | ||
|
|
29a24b7305 | ||
|
|
7c31c951a1 | ||
|
|
33bac6f816 | ||
|
|
a6f0a36b6d | ||
|
|
b253950075 | ||
|
|
b19b987ed0 | ||
|
|
dc84d32028 | ||
|
|
82c99891eb | ||
|
|
f30b899569 | ||
|
|
cd8b7cd239 | ||
|
|
d80481a106 | ||
|
|
174be39c5e | ||
|
|
5f694276f1 | ||
|
|
affb5d7281 | ||
|
|
87b9eeb2f1 | ||
|
|
81c75b2b71 | ||
|
|
ec6d490676 | ||
|
|
be0894653a | ||
|
|
838e88a502 | ||
|
|
4b19d38c58 | ||
|
|
7725fae992 | ||
|
|
b43fa55a7b | ||
|
|
d5c79be7d9 | ||
|
|
1b9d64ad5e | ||
|
|
c648249160 | ||
|
|
765ce68c11 | ||
|
|
020d90b957 | ||
|
|
8d407b1dc9 | ||
|
|
fe1b8aaab3 | ||
|
|
ade35f2fa2 | ||
|
|
cd01a27bdd | ||
|
|
2f9c856645 | ||
|
|
8e8878e43a | ||
|
|
a37e08c2fd |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -410,5 +410,6 @@ jobs:
|
||||
include-hidden-files: true
|
||||
- name: Compare SimpleCov results with Undercover
|
||||
run: |
|
||||
git fetch --no-tags origin master:master
|
||||
git fetch --no-tags origin ${{ github.event.pull_request.base.ref }}:master
|
||||
bundle exec undercover
|
||||
if: ${{ github.ref != 'refs/heads/master' }} # Does not run on master, as we can't fetch master in the master branch
|
||||
|
||||
@@ -16,6 +16,7 @@ AllCops:
|
||||
- node_modules/**/*
|
||||
# Excluding: inadequate Naming/FileName rule rejects GemFile name with camelcase
|
||||
- engines/web/Gemfile
|
||||
- .undercover
|
||||
|
||||
Bundler/DuplicatedGem:
|
||||
Enabled: false
|
||||
|
||||
@@ -98,6 +98,7 @@ Metrics/ClassLength:
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_producer.rb'
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
- 'app/services/permissions/order.rb'
|
||||
|
||||
# Offense count: 30
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
||||
|
||||
20
.simplecov
20
.simplecov
@@ -2,19 +2,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
SimpleCov.start 'rails' do
|
||||
# The rails profile contains some filters already:
|
||||
#
|
||||
# - "/test/"
|
||||
# - "/features/"
|
||||
# - "/spec/"
|
||||
# - "/autotest/"
|
||||
# - /^\/config\//
|
||||
# - /^\/db\//
|
||||
add_filter '/bin/'
|
||||
add_filter '/config/'
|
||||
add_filter '/jobs/application_job.rb'
|
||||
add_filter '/schemas/'
|
||||
add_filter '/lib/generators'
|
||||
add_filter '/spec/'
|
||||
add_filter '/vendor/'
|
||||
add_filter '/public'
|
||||
add_filter '/swagger'
|
||||
add_filter '/config/' # to include engine config
|
||||
add_filter '/script'
|
||||
add_filter '/log'
|
||||
add_filter '/db'
|
||||
add_filter '/lib/tasks/sample_data/'
|
||||
|
||||
formatter SimpleCov::Formatter::SimpleFormatter
|
||||
end
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#!/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
-l coverage/lcov/openfoodnetwork.lcov
|
||||
-c master
|
||||
--compare master
|
||||
|
||||
# This shouldn't be needed in undercover > 0.7.4
|
||||
#
|
||||
# * https://github.com/grodowski/undercover/issues/233
|
||||
--exclude-files "bin/*,db/*,config/*,spec/*,engines/*/config/*,engines/*/spec/*"
|
||||
|
||||
@@ -2,7 +2,9 @@ FROM ruby:3.1.4-alpine3.19 AS base
|
||||
ENV LANG=C.UTF-8 \
|
||||
LC_ALL=C.UTF-8 \
|
||||
TZ=Europe/London \
|
||||
RAILS_ROOT=/usr/src/app
|
||||
RAILS_ROOT=/usr/src/app \
|
||||
BUNDLE_PATH=/bundles \
|
||||
BUNDLE_APP_CONFIG=/bundles
|
||||
RUN apk --no-cache upgrade && \
|
||||
apk add --no-cache tzdata postgresql-client imagemagick imagemagick-jpeg && \
|
||||
apk add --no-cache --virtual wkhtmltopdf
|
||||
@@ -14,7 +16,7 @@ FROM base AS development-base
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
build-base postgresql-dev git nodejs yarn && \
|
||||
apk add --no-cache --virtual .dev-utils \
|
||||
bash curl less vim chromium-chromedriver zlib-dev openssl-dev \
|
||||
bash curl less vim chromium-chromedriver zlib-dev openssl-dev cmake\
|
||||
readline-dev yaml-dev sqlite-dev libxml2-dev libxslt-dev libffi-dev vips-dev && \
|
||||
curl -o /usr/local/bin/wait-for-it https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh && \
|
||||
chmod +x /usr/local/bin/wait-for-it
|
||||
|
||||
@@ -25,7 +25,8 @@ RUN apt-get update && apt-get install -y \
|
||||
libjemalloc-dev \
|
||||
libssl-dev \
|
||||
ca-certificates \
|
||||
gnupg
|
||||
gnupg \
|
||||
cmake
|
||||
|
||||
# Setup ENV variables
|
||||
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:/usr/local/src/nodenv/shims:/usr/local/src/nodenv/bin:$PATH
|
||||
|
||||
4
Gemfile
4
Gemfile
@@ -152,6 +152,7 @@ end
|
||||
group :test, :development do
|
||||
gem 'bullet'
|
||||
gem 'capybara'
|
||||
gem 'capybara-shadowdom'
|
||||
gem 'cuprite'
|
||||
gem 'database_cleaner', require: false
|
||||
gem 'debug', '>= 1.0.0'
|
||||
@@ -166,14 +167,13 @@ group :test, :development do
|
||||
gem 'rswag'
|
||||
gem 'shoulda-matchers'
|
||||
gem 'stimulus_reflex_testing', github: "podia/stimulus_reflex_testing", branch: :main
|
||||
gem 'timecop'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'pdf-reader'
|
||||
gem 'puffing-billy'
|
||||
gem 'rails-controller-testing'
|
||||
gem 'simplecov', require: false
|
||||
gem 'simplecov-lcov', require: false
|
||||
gem 'undercover', require: false
|
||||
gem 'vcr', require: false
|
||||
gem 'webmock', require: false
|
||||
|
||||
252
Gemfile.lock
252
Gemfile.lock
@@ -48,54 +48,58 @@ PATH
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
Ascii85 (1.1.0)
|
||||
actioncable (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
Ascii85 (2.0.1)
|
||||
actioncable (7.1.5.2)
|
||||
actionpack (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (7.1.5.2)
|
||||
actionpack (= 7.1.5.2)
|
||||
activejob (= 7.1.5.2)
|
||||
activerecord (= 7.1.5.2)
|
||||
activestorage (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionmailer (7.1.5.2)
|
||||
actionpack (= 7.1.5.2)
|
||||
actionview (= 7.1.5.2)
|
||||
activejob (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rack (~> 2.0, >= 2.2.4)
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (7.1.5.2)
|
||||
actionview (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
nokogiri (>= 1.8.5)
|
||||
racc
|
||||
rack (>= 2.2.4)
|
||||
rack-session (>= 1.0.1)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
actionpack-action_caching (1.2.2)
|
||||
actionpack (>= 4.0.0)
|
||||
actiontext (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actiontext (7.1.5.2)
|
||||
actionpack (= 7.1.5.2)
|
||||
activerecord (= 7.1.5.2)
|
||||
activestorage (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionview (7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
active_model_serializers (0.8.4)
|
||||
activemodel (>= 3.0)
|
||||
active_storage_validations (1.1.4)
|
||||
@@ -103,8 +107,8 @@ GEM
|
||||
activemodel (>= 5.2.0)
|
||||
activestorage (>= 5.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
activejob (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activejob (7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
globalid (>= 0.3.6)
|
||||
activemerchant (1.133.0)
|
||||
activesupport (>= 4.2)
|
||||
@@ -112,11 +116,12 @@ GEM
|
||||
i18n (>= 0.6.9)
|
||||
nokogiri (~> 1.4)
|
||||
rexml (~> 3.2.5)
|
||||
activemodel (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activerecord (7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activemodel (7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
activerecord (7.1.5.2)
|
||||
activemodel (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
timeout (>= 0.4.0)
|
||||
activerecord-import (1.6.0)
|
||||
activerecord (>= 4.2)
|
||||
activerecord-postgresql-adapter (0.0.1)
|
||||
@@ -128,17 +133,24 @@ GEM
|
||||
multi_json (~> 1.11, >= 1.11.2)
|
||||
rack (>= 2.0.8, < 4)
|
||||
railties (>= 6.1)
|
||||
activestorage (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activestorage (7.1.5.2)
|
||||
actionpack (= 7.1.5.2)
|
||||
activejob (= 7.1.5.2)
|
||||
activerecord (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (7.0.8)
|
||||
activesupport (7.1.5.2)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
logger (>= 1.4.2)
|
||||
minitest (>= 5.1)
|
||||
mutex_m
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0)
|
||||
acts-as-taggable-on (10.0.0)
|
||||
activerecord (>= 6.1, < 7.2)
|
||||
@@ -159,7 +171,7 @@ GEM
|
||||
angularjs-rails (1.8.0)
|
||||
arel-helpers (2.14.0)
|
||||
activerecord (>= 3.1.0, < 8)
|
||||
ast (2.4.2)
|
||||
ast (2.4.3)
|
||||
attr_required (1.0.2)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.929.0)
|
||||
@@ -177,10 +189,11 @@ GEM
|
||||
aws-sigv4 (~> 1.8)
|
||||
aws-sigv4 (1.8.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
base64 (0.2.0)
|
||||
base64 (0.3.0)
|
||||
bcp47_spec (0.2.1)
|
||||
bcrypt (3.1.20)
|
||||
bigdecimal (3.1.8)
|
||||
benchmark (0.4.1)
|
||||
bigdecimal (3.2.2)
|
||||
bindata (2.5.0)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.18.3)
|
||||
@@ -188,7 +201,7 @@ GEM
|
||||
bugsnag (6.26.4)
|
||||
concurrent-ruby (~> 1.0)
|
||||
builder (3.2.4)
|
||||
bullet (7.1.6)
|
||||
bullet (8.0.8)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
cable_ready (5.0.6)
|
||||
@@ -208,12 +221,14 @@ GEM
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (>= 1.5, < 3.0)
|
||||
xpath (~> 3.2)
|
||||
capybara-shadowdom (0.3.0)
|
||||
capybara
|
||||
caxlsx (3.3.0)
|
||||
htmlentities (~> 4.3, >= 4.3.4)
|
||||
marcel (~> 1.0)
|
||||
nokogiri (~> 1.10, >= 1.10.4)
|
||||
rubyzip (>= 1.3.0, < 3)
|
||||
cgi (0.3.6)
|
||||
cgi (0.3.7)
|
||||
childprocess (5.0.0)
|
||||
choice (0.2.0)
|
||||
chronic (0.10.2)
|
||||
@@ -228,8 +243,9 @@ GEM
|
||||
combine_pdf (1.0.26)
|
||||
matrix
|
||||
ruby-rc4 (>= 0.1.5)
|
||||
concurrent-ruby (1.3.1)
|
||||
connection_pool (2.4.1)
|
||||
concurrent-ruby (1.3.5)
|
||||
connection_pool (2.5.3)
|
||||
cookiejar (0.3.4)
|
||||
crack (1.0.0)
|
||||
bigdecimal
|
||||
rexml
|
||||
@@ -248,7 +264,7 @@ GEM
|
||||
database_cleaner-core (2.0.1)
|
||||
datafoodconsortium-connector (1.1.0)
|
||||
virtual_assembly-semantizer (~> 1.0, >= 1.0.5)
|
||||
date (3.3.4)
|
||||
date (3.4.1)
|
||||
debug (1.9.2)
|
||||
irb (~> 1.10)
|
||||
reline (>= 0.3.8)
|
||||
@@ -269,11 +285,25 @@ GEM
|
||||
digest (3.1.1)
|
||||
docile (1.4.0)
|
||||
dotenv (3.1.2)
|
||||
drb (2.2.3)
|
||||
em-http-request (1.1.7)
|
||||
addressable (>= 2.3.4)
|
||||
cookiejar (!= 0.3.1)
|
||||
em-socksify (>= 0.3)
|
||||
eventmachine (>= 1.0.3)
|
||||
http_parser.rb (>= 0.6.0)
|
||||
em-socksify (0.3.3)
|
||||
base64
|
||||
eventmachine (>= 1.0.0.beta.4)
|
||||
em-synchrony (1.0.6)
|
||||
eventmachine (>= 1.0.0.beta.1)
|
||||
email_validator (2.2.4)
|
||||
activemodel
|
||||
erubi (1.12.0)
|
||||
et-orbi (1.2.7)
|
||||
et-orbi (1.3.0)
|
||||
tzinfo
|
||||
eventmachine (1.2.7)
|
||||
eventmachine_httpserver (0.2.1)
|
||||
excon (0.81.0)
|
||||
execjs (2.7.0)
|
||||
factory_bot (6.2.0)
|
||||
@@ -323,8 +353,8 @@ GEM
|
||||
nokogiri (>= 1.5.11, < 2.0.0)
|
||||
foreman (0.88.1)
|
||||
formatador (0.2.5)
|
||||
fugit (1.8.1)
|
||||
et-orbi (~> 1, >= 1.2.7)
|
||||
fugit (1.11.1)
|
||||
et-orbi (~> 1, >= 1.2.11)
|
||||
raabro (~> 1.4)
|
||||
fuubar (2.5.1)
|
||||
rspec-core (~> 3.0)
|
||||
@@ -347,6 +377,7 @@ GEM
|
||||
hashie (5.0.0)
|
||||
highline (2.0.3)
|
||||
htmlentities (4.3.4)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (1.14.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-js (3.9.2)
|
||||
@@ -420,7 +451,8 @@ GEM
|
||||
listen (3.9.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
loofah (2.22.0)
|
||||
logger (1.7.0)
|
||||
loofah (2.24.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.8.1)
|
||||
@@ -440,7 +472,7 @@ GEM
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.6)
|
||||
minitest (5.23.1)
|
||||
minitest (5.25.5)
|
||||
monetize (1.13.0)
|
||||
money (~> 6.12)
|
||||
money (6.16.0)
|
||||
@@ -448,9 +480,10 @@ GEM
|
||||
msgpack (1.7.2)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
mutex_m (0.3.0)
|
||||
net-http (0.4.1)
|
||||
uri
|
||||
net-imap (0.4.10)
|
||||
net-imap (0.4.20)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
@@ -461,7 +494,7 @@ GEM
|
||||
net-protocol
|
||||
newrelic_rpm (9.9.0)
|
||||
nio4r (2.7.1)
|
||||
nokogiri (1.16.5)
|
||||
nokogiri (1.18.9)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nokogiri-html5-inference (0.3.0)
|
||||
@@ -505,7 +538,7 @@ GEM
|
||||
parallel (1.24.0)
|
||||
paranoia (2.6.3)
|
||||
activerecord (>= 5.1, < 7.2)
|
||||
parser (3.3.2.0)
|
||||
parser (3.3.8.0)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
paypal-sdk-core (0.3.4)
|
||||
@@ -513,9 +546,9 @@ GEM
|
||||
xml-simple
|
||||
paypal-sdk-merchant (1.117.2)
|
||||
paypal-sdk-core (~> 0.3.0)
|
||||
pdf-reader (2.12.0)
|
||||
Ascii85 (~> 1.0)
|
||||
afm (~> 0.2.1)
|
||||
pdf-reader (2.15.0)
|
||||
Ascii85 (>= 1.0, < 3.0, != 2.0.0)
|
||||
afm (>= 0.2.1, < 2)
|
||||
hashery (~> 2.0)
|
||||
ruby-rc4
|
||||
ttfunk
|
||||
@@ -527,14 +560,22 @@ GEM
|
||||
psych (5.1.2)
|
||||
stringio
|
||||
public_suffix (5.0.5)
|
||||
puffing-billy (4.0.2)
|
||||
addressable (~> 2.5)
|
||||
em-http-request (~> 1.1, >= 1.1.0)
|
||||
em-synchrony
|
||||
eventmachine (~> 1.2)
|
||||
eventmachine_httpserver
|
||||
http_parser.rb (~> 0.8.0)
|
||||
multi_json
|
||||
puma (6.5.0)
|
||||
nio4r (~> 2.0)
|
||||
query_count (1.1.1)
|
||||
activerecord (>= 4.2)
|
||||
railties (>= 4.2)
|
||||
raabro (1.4.0)
|
||||
racc (1.8.0)
|
||||
rack (2.2.11)
|
||||
racc (1.8.1)
|
||||
rack (2.2.18)
|
||||
rack-mini-profiler (2.3.4)
|
||||
rack (>= 1.2.0)
|
||||
rack-oauth2 (2.2.1)
|
||||
@@ -555,20 +596,23 @@ GEM
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rack-timeout (0.7.0)
|
||||
rails (7.0.8)
|
||||
actioncable (= 7.0.8)
|
||||
actionmailbox (= 7.0.8)
|
||||
actionmailer (= 7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actiontext (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rackup (1.0.1)
|
||||
rack (< 3)
|
||||
webrick
|
||||
rails (7.1.5.2)
|
||||
actioncable (= 7.1.5.2)
|
||||
actionmailbox (= 7.1.5.2)
|
||||
actionmailer (= 7.1.5.2)
|
||||
actionpack (= 7.1.5.2)
|
||||
actiontext (= 7.1.5.2)
|
||||
actionview (= 7.1.5.2)
|
||||
activejob (= 7.1.5.2)
|
||||
activemodel (= 7.1.5.2)
|
||||
activerecord (= 7.1.5.2)
|
||||
activestorage (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.0.8)
|
||||
railties (= 7.1.5.2)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
@@ -582,20 +626,21 @@ GEM
|
||||
activesupport (>= 4.2)
|
||||
choice (~> 0.2.0)
|
||||
ruby-graphviz (~> 1.2)
|
||||
rails-html-sanitizer (1.6.0)
|
||||
rails-html-sanitizer (1.6.1)
|
||||
loofah (~> 2.21)
|
||||
nokogiri (~> 1.14)
|
||||
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
||||
rails-i18n (7.0.9)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
rails_safe_tasks (1.0.0)
|
||||
railties (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
method_source
|
||||
railties (7.1.5.2)
|
||||
actionpack (= 7.1.5.2)
|
||||
activesupport (= 7.1.5.2)
|
||||
irb
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
zeitwerk (~> 2.5)
|
||||
thor (~> 1.0, >= 1.2.2)
|
||||
zeitwerk (~> 2.6)
|
||||
rainbow (3.1.1)
|
||||
rake (13.2.1)
|
||||
ransack (4.1.1)
|
||||
@@ -717,7 +762,7 @@ GEM
|
||||
rubyzip (2.3.2)
|
||||
rufus-scheduler (3.8.2)
|
||||
fugit (~> 1.1, >= 1.1.6)
|
||||
rugged (1.7.2)
|
||||
rugged (1.9.0)
|
||||
sanitize (6.1.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
@@ -729,6 +774,7 @@ GEM
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
sd_notify (0.1.1)
|
||||
securerandom (0.4.1)
|
||||
semantic_range (3.0.0)
|
||||
shoulda-matchers (6.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
@@ -746,7 +792,6 @@ GEM
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov_json_formatter (~> 0.1)
|
||||
simplecov-html (0.12.3)
|
||||
simplecov-lcov (0.8.0)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
spreadsheet_architect (5.0.0)
|
||||
caxlsx (>= 3.3.0, < 4)
|
||||
@@ -794,12 +839,12 @@ GEM
|
||||
temple (0.8.2)
|
||||
terminal-table (4.0.0)
|
||||
unicode-display_width (>= 1.1.1, < 4)
|
||||
thor (1.3.1)
|
||||
thor (1.4.0)
|
||||
thread-local (1.1.0)
|
||||
tilt (2.3.0)
|
||||
timecop (0.9.8)
|
||||
timeout (0.4.1)
|
||||
ttfunk (1.7.0)
|
||||
timeout (0.4.3)
|
||||
ttfunk (1.8.0)
|
||||
bigdecimal (~> 3.1)
|
||||
turbo-rails (2.0.5)
|
||||
actionpack (>= 6.0.0)
|
||||
activejob (>= 6.0.0)
|
||||
@@ -808,14 +853,17 @@ GEM
|
||||
turbo-rails (>= 1.3.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
undercover (0.6.3)
|
||||
undercover (0.7.4)
|
||||
base64
|
||||
bigdecimal
|
||||
imagen (>= 0.2.0)
|
||||
rainbow (>= 2.1, < 4.0)
|
||||
rugged (>= 0.27, < 1.8)
|
||||
rugged (>= 0.27, < 1.10)
|
||||
simplecov
|
||||
simplecov_json_formatter
|
||||
unicode-display_width (2.5.0)
|
||||
uniform_notifier (1.16.0)
|
||||
uri (0.13.0)
|
||||
uniform_notifier (1.17.0)
|
||||
uri (0.13.2)
|
||||
valid_email2 (5.2.3)
|
||||
activemodel (>= 3.2)
|
||||
mail (~> 2.5)
|
||||
@@ -855,7 +903,7 @@ GEM
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
semantic_range (>= 2.3.0)
|
||||
webrick (1.8.1)
|
||||
webrick (1.8.2)
|
||||
websocket-driver (0.7.6)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
@@ -863,7 +911,7 @@ GEM
|
||||
chronic (>= 0.6.3)
|
||||
wicked_pdf (2.8.1)
|
||||
activesupport
|
||||
wkhtmltopdf-binary (0.12.6.7)
|
||||
wkhtmltopdf-binary (0.12.6.10)
|
||||
xml-simple (1.1.8)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
@@ -895,6 +943,7 @@ DEPENDENCIES
|
||||
cable_ready
|
||||
cancancan (~> 1.15.0)
|
||||
capybara
|
||||
capybara-shadowdom
|
||||
catalog!
|
||||
coffee-rails (~> 5.0.0)
|
||||
combine_pdf
|
||||
@@ -958,6 +1007,7 @@ DEPENDENCIES
|
||||
pg (~> 1.2.3)
|
||||
private_address_check
|
||||
pry (~> 0.13.0)
|
||||
puffing-billy
|
||||
puma
|
||||
query_count
|
||||
rack-mini-profiler (< 3.0.0)
|
||||
@@ -989,7 +1039,6 @@ DEPENDENCIES
|
||||
sidekiq
|
||||
sidekiq-scheduler
|
||||
simplecov
|
||||
simplecov-lcov
|
||||
spreadsheet_architect
|
||||
spring
|
||||
spring-commands-rspec
|
||||
@@ -999,7 +1048,6 @@ DEPENDENCIES
|
||||
stimulus_reflex_testing!
|
||||
stringex (~> 2.8.5)
|
||||
stripe
|
||||
timecop
|
||||
turbo-rails
|
||||
turbo_power
|
||||
undercover
|
||||
|
||||
@@ -1,390 +0,0 @@
|
||||
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, $location, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor, SortOptions, ErrorsParser, ProductFiltersUrl) ->
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.columns = Columns.columns
|
||||
|
||||
$scope.variant_unit_options = VariantUnitManager.variantUnitOptions()
|
||||
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.pagination = BulkProducts.pagination
|
||||
$scope.per_page_options = [
|
||||
{id: 15, name: t('js.admin.orders.index.per_page', results: 15)},
|
||||
{id: 50, name: t('js.admin.orders.index.per_page', results: 50)},
|
||||
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
|
||||
]
|
||||
|
||||
$scope.q = {
|
||||
producerFilter: ""
|
||||
categoryFilter: ""
|
||||
importDateFilter: ""
|
||||
query: ""
|
||||
sorting: ""
|
||||
}
|
||||
|
||||
$scope.sorting = "name asc"
|
||||
$scope.producers = producers
|
||||
$scope.taxons = Taxons.all
|
||||
$scope.tax_categories = tax_categories
|
||||
$scope.page = 1
|
||||
$scope.per_page = 15
|
||||
$scope.products = BulkProducts.products
|
||||
$scope.DisplayProperties = DisplayProperties
|
||||
|
||||
$scope.sortOptions = SortOptions
|
||||
|
||||
$scope.initialise = ->
|
||||
$scope.q = ProductFiltersUrl.loadFromUrl($location.search())
|
||||
$scope.fetchProducts()
|
||||
|
||||
$scope.$watchCollection '[q.query, q.producerFilter, q.categoryFilter, q.importDateFilter, per_page]', ->
|
||||
$scope.page = 1 # Reset page when changing filters for new search
|
||||
|
||||
$scope.changePage = (newPage) ->
|
||||
$scope.page = newPage
|
||||
$scope.fetchProducts()
|
||||
|
||||
$scope.fetchProducts = ->
|
||||
removeClearedValues()
|
||||
params = {
|
||||
'q[name_cont]': $scope.q.query,
|
||||
'q[variants_supplier_id_eq]': $scope.q.producerFilter,
|
||||
'q[variants_primary_taxon_id_eq]': $scope.q.categoryFilter,
|
||||
'q[s]': $scope.sorting,
|
||||
import_date: $scope.q.importDateFilter,
|
||||
page: $scope.page,
|
||||
per_page: $scope.per_page
|
||||
}
|
||||
RequestMonitor.load(BulkProducts.fetch(params).$promise).then ->
|
||||
# update url with the filters used
|
||||
$location.search(ProductFiltersUrl.generate($scope.q))
|
||||
$scope.resetProducts()
|
||||
|
||||
removeClearedValues = ->
|
||||
delete $scope.q.producerFilter if $scope.q.producerFilter == "0"
|
||||
delete $scope.q.categoryFilter if $scope.q.categoryFilter == "0"
|
||||
delete $scope.q.importDateFilter if $scope.q.importDateFilter == "0"
|
||||
|
||||
$timeout ->
|
||||
if $scope.showLatestImport
|
||||
$scope.q.importDateFilter = $scope.importDates[1].id
|
||||
|
||||
$scope.resetProducts = ->
|
||||
DirtyProducts.clear()
|
||||
StatusMessage.clear()
|
||||
|
||||
$scope.updateOnHand = (product) ->
|
||||
on_demand_variants = []
|
||||
if product.variants
|
||||
on_demand_variants = (variant for id, variant of product.variants when variant.on_demand)
|
||||
|
||||
unless product.on_demand || on_demand_variants.length > 0
|
||||
product.on_hand = $scope.onHand(product)
|
||||
|
||||
|
||||
$scope.onHand = (product) ->
|
||||
onHand = 0
|
||||
if product.hasOwnProperty("variants") and product.variants instanceof Object
|
||||
for id, variant of product.variants
|
||||
onHand = onHand + parseInt(if variant.on_hand > 0 then variant.on_hand else 0)
|
||||
else
|
||||
onHand = "error"
|
||||
onHand
|
||||
|
||||
$scope.shiftTab = (tab) ->
|
||||
$scope.visibleTab.visible = false unless $scope.visibleTab == tab || $scope.visibleTab == undefined
|
||||
tab.visible = !tab.visible
|
||||
$scope.visibleTab = tab
|
||||
|
||||
$scope.resetSelectFilters = ->
|
||||
$scope.q.query = ""
|
||||
$scope.q.producerFilter = "0"
|
||||
$scope.q.categoryFilter = "0"
|
||||
$scope.q.importDateFilter = "0"
|
||||
$scope.fetchProducts()
|
||||
|
||||
$scope.$watch 'sortOptions', (sort) ->
|
||||
return unless sort && sort.predicate != ""
|
||||
|
||||
$scope.sorting = sort.getSortingExpr(defaultDirection: "asc")
|
||||
$scope.fetchProducts()
|
||||
, true
|
||||
|
||||
confirm_unsaved_changes = () ->
|
||||
(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"
|
||||
|
||||
$scope.editWarn = (product, variant) ->
|
||||
if confirm_unsaved_changes()
|
||||
$window.location.href = ProductFiltersUrl.buildUrl(editProductUrl(product, variant), $scope.q)
|
||||
|
||||
$scope.toggleShowAllVariants = ->
|
||||
showVariants = !DisplayProperties.showVariants 0
|
||||
$scope.products.forEach (product) ->
|
||||
DisplayProperties.setShowVariants product.id, showVariants
|
||||
DisplayProperties.setShowVariants 0, showVariants
|
||||
|
||||
$scope.addVariant = (product) ->
|
||||
# Set new variant category to same as last product variant category to keep compactibility with deleted variant callback to set new variant category
|
||||
newVariantId = $scope.nextVariantId();
|
||||
newVariantCategoryId = product.variants[product.variants.length - 1]?.category_id
|
||||
product.variants.push
|
||||
id: newVariantId
|
||||
unit_value: null
|
||||
unit_description: null
|
||||
on_demand: false
|
||||
display_as: null
|
||||
display_name: null
|
||||
on_hand: null
|
||||
price: null
|
||||
tax_category_id: null
|
||||
category_id: newVariantCategoryId
|
||||
DisplayProperties.setShowVariants product.id, true
|
||||
DirtyProducts.addVariantProperty(product.id, newVariantId, 'category_id', newVariantCategoryId)
|
||||
|
||||
|
||||
$scope.nextVariantId = ->
|
||||
$scope.variantIdCounter = 0 unless $scope.variantIdCounter?
|
||||
$scope.variantIdCounter -= 1
|
||||
$scope.variantIdCounter
|
||||
|
||||
$scope.deleteProduct = (product) ->
|
||||
if confirm(t('are_you_sure'))
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/v0/products/" + product.id
|
||||
).then (response) ->
|
||||
$scope.products.splice $scope.products.indexOf(product), 1
|
||||
DirtyProducts.deleteProduct product.id
|
||||
$scope.displayDirtyProducts()
|
||||
|
||||
|
||||
$scope.deleteVariant = (product, variant) ->
|
||||
if product.variants.length > 1
|
||||
if !$scope.variantSaved(variant)
|
||||
$scope.removeVariant(product, variant)
|
||||
else
|
||||
if confirm(t("are_you_sure"))
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/v0/products/" + product.id + "/variants/" + variant.id
|
||||
).then (response) ->
|
||||
$scope.removeVariant(product, variant)
|
||||
else
|
||||
alert(t("delete_product_variant"))
|
||||
|
||||
$scope.removeVariant = (product, variant) ->
|
||||
product.variants.splice product.variants.indexOf(variant), 1
|
||||
DirtyProducts.deleteVariant product.id, variant.id
|
||||
$scope.displayDirtyProducts()
|
||||
|
||||
|
||||
$scope.cloneProduct = (product) ->
|
||||
BulkProducts.cloneProduct product
|
||||
|
||||
$scope.hasVariants = (product) ->
|
||||
product.variants.length > 0
|
||||
|
||||
|
||||
$scope.hasUnit = (variant) ->
|
||||
variant.variant_unit_with_scale?
|
||||
|
||||
$scope.variantSaved = (variant) ->
|
||||
variant.hasOwnProperty('id') && variant.id > 0
|
||||
|
||||
|
||||
$scope.hasOnDemandVariants = (product) ->
|
||||
(variant for id, variant of product.variants when variant.on_demand).length > 0
|
||||
|
||||
|
||||
$scope.submitProducts = ->
|
||||
# Pack pack $scope.products, so they will match the list returned from the server,
|
||||
# then pack $scope.dirtyProducts, ensuring that the correct product info is sent to the server.
|
||||
$scope.packProduct product for id, product of $scope.products
|
||||
$scope.packProduct product for id, product of DirtyProducts.all()
|
||||
|
||||
productsToSubmit = filterSubmitProducts(DirtyProducts.all())
|
||||
if productsToSubmit.length > 0
|
||||
$scope.updateProducts productsToSubmit # Don't submit an empty list
|
||||
else
|
||||
StatusMessage.display 'alert', t("products_change")
|
||||
|
||||
|
||||
$scope.updateProducts = (productsToSubmit) ->
|
||||
$scope.displayUpdating()
|
||||
$http(
|
||||
method: "POST"
|
||||
url: "/admin/products/bulk_update"
|
||||
data:
|
||||
products: productsToSubmit
|
||||
filters:
|
||||
'q[name_cont]': $scope.q.query
|
||||
'q[variants_supplier_id_eq]': $scope.q.producerFilter
|
||||
'q[variants_primary_taxon_id_eq]': $scope.q.categoryFilter
|
||||
'q[s]': $scope.sorting
|
||||
import_date: $scope.q.importDateFilter
|
||||
page: $scope.page
|
||||
per_page: $scope.per_page
|
||||
).then((response) ->
|
||||
DirtyProducts.clear()
|
||||
BulkProducts.updateVariantLists(response.data.products || [])
|
||||
$timeout -> $scope.displaySuccess()
|
||||
).catch (response) ->
|
||||
if response.status == 400 && response.data.errors?
|
||||
errorsString = ErrorsParser.toString(response.data.errors, response.status)
|
||||
$scope.displayFailure t("products_update_error") + "\n" + errorsString
|
||||
else
|
||||
$scope.displayFailure t("products_update_error_data") + response.status
|
||||
|
||||
$scope.cancel = (destination) ->
|
||||
$window.location = destination
|
||||
|
||||
$scope.packProduct = (product) ->
|
||||
if product.variants
|
||||
for id, variant of product.variants
|
||||
$scope.packVariant variant
|
||||
|
||||
|
||||
$scope.packVariant = (variant) ->
|
||||
if variant.variant_unit_with_scale
|
||||
match = variant.variant_unit_with_scale.match(/^([^_]+)_([\d\.]+)$/)
|
||||
if match
|
||||
variant.variant_unit = match[1]
|
||||
variant.variant_unit_scale = parseFloat(match[2])
|
||||
else
|
||||
variant.variant_unit = variant.variant_unit_with_scale
|
||||
variant.variant_unit_scale = null
|
||||
|
||||
if variant.hasOwnProperty("unit_value_with_description")
|
||||
match = variant.unit_value_with_description.match(/^([\d\.\,]+(?= |$)|)( |)(.*)$/)
|
||||
if match
|
||||
variant.unit_value = parseFloat(match[1].replace(",", "."))
|
||||
variant.unit_value = null if isNaN(variant.unit_value)
|
||||
if variant.unit_value && variant.variant_unit_scale
|
||||
variant.unit_value = parseFloat(window.bigDecimal.multiply(variant.unit_value, variant.variant_unit_scale, 2))
|
||||
variant.unit_description = match[3]
|
||||
|
||||
$scope.incrementLimit = ->
|
||||
if $scope.limit < $scope.products.length
|
||||
$scope.limit = $scope.limit + 5
|
||||
|
||||
|
||||
$scope.displayUpdating = ->
|
||||
StatusMessage.display 'progress', t("saving")
|
||||
|
||||
|
||||
$scope.displaySuccess = ->
|
||||
StatusMessage.display 'success',t("products_changes_saved")
|
||||
$scope.bulk_product_form.$setPristine()
|
||||
|
||||
|
||||
$scope.displayFailure = (failMessage) ->
|
||||
StatusMessage.display 'failure', t("products_update_error_msg") + " #{failMessage}"
|
||||
|
||||
|
||||
$scope.displayDirtyProducts = ->
|
||||
count = DirtyProducts.count()
|
||||
switch count
|
||||
when 0 then StatusMessage.clear()
|
||||
when 1 then StatusMessage.display 'notice', t("one_product_unsaved")
|
||||
else StatusMessage.display 'notice', t("products_unsaved", n: count)
|
||||
|
||||
|
||||
filterSubmitProducts = (productsToFilter) ->
|
||||
filteredProducts = []
|
||||
if productsToFilter instanceof Object
|
||||
angular.forEach productsToFilter, (product) ->
|
||||
if product.hasOwnProperty("id")
|
||||
filteredProduct = {id: product.id}
|
||||
filteredVariants = []
|
||||
hasUpdatableProperty = false
|
||||
|
||||
if product.hasOwnProperty("variants")
|
||||
angular.forEach product.variants, (variant) ->
|
||||
result = filterSubmitVariant variant
|
||||
filteredVariant = result.filteredVariant
|
||||
variantHasUpdatableProperty = result.hasUpdatableProperty
|
||||
filteredVariants.push filteredVariant if variantHasUpdatableProperty
|
||||
|
||||
if product.hasOwnProperty("name")
|
||||
filteredProduct.name = product.name
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("price")
|
||||
filteredProduct.price = product.price
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("on_hand") and filteredVariants.length == 0 #only update if no variants present
|
||||
filteredProduct.on_hand = product.on_hand
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("on_demand") and filteredVariants.length == 0 #only update if no variants present
|
||||
filteredProduct.on_demand = product.on_demand
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("inherits_properties")
|
||||
filteredProduct.inherits_properties = product.inherits_properties
|
||||
hasUpdatableProperty = true
|
||||
if filteredVariants.length > 0 # Note that the name of the property changes to enable mass assignment of variants attributes with rails
|
||||
filteredProduct.variants_attributes = filteredVariants
|
||||
hasUpdatableProperty = true
|
||||
filteredProducts.push filteredProduct if hasUpdatableProperty
|
||||
|
||||
filteredProducts
|
||||
|
||||
|
||||
filterSubmitVariant = (variant) ->
|
||||
hasUpdatableProperty = false
|
||||
filteredVariant = {}
|
||||
if not variant.deleted_at? and variant.hasOwnProperty("id")
|
||||
filteredVariant.id = variant.id unless variant.id <= 0
|
||||
if variant.hasOwnProperty("sku")
|
||||
filteredVariant.sku = variant.sku
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("on_hand")
|
||||
filteredVariant.on_hand = variant.on_hand
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("on_demand")
|
||||
filteredVariant.on_demand = variant.on_demand
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("price")
|
||||
filteredVariant.price = variant.price
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("unit_value")
|
||||
filteredVariant.unit_value = variant.unit_value
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("unit_description")
|
||||
filteredVariant.unit_description = variant.unit_description
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("display_name")
|
||||
filteredVariant.display_name = variant.display_name
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("tax_category_id")
|
||||
filteredVariant.tax_category_id = variant.tax_category_id
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("category_id")
|
||||
filteredVariant.primary_taxon_id = variant.category_id
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("display_as")
|
||||
filteredVariant.display_as = variant.display_as
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("producer_id")
|
||||
filteredVariant.supplier_id = variant.producer_id
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("variant_unit_with_scale")
|
||||
filteredVariant.variant_unit = variant.variant_unit
|
||||
filteredVariant.variant_unit_scale = variant.variant_unit_scale
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("variant_unit_name")
|
||||
filteredVariant.variant_unit_name = variant.variant_unit_name
|
||||
hasUpdatableProperty = true
|
||||
|
||||
{filteredVariant: filteredVariant, hasUpdatableProperty: hasUpdatableProperty}
|
||||
|
||||
|
||||
toObjectWithIDKeys = (array) ->
|
||||
object = {}
|
||||
|
||||
for i of array
|
||||
if array[i] instanceof Object and array[i].hasOwnProperty("id")
|
||||
object[array[i].id] = angular.copy(array[i])
|
||||
object[array[i].id].variants = toObjectWithIDKeys(array[i].variants) if array[i].hasOwnProperty("variants") and array[i].variants instanceof Array
|
||||
|
||||
object
|
||||
@@ -19,18 +19,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
$scope.page = 1
|
||||
$scope.per_page = $scope.per_page_options[0].id
|
||||
$scope.filterByVariantId = null
|
||||
searchThrough = ["order_distributor_name",
|
||||
"order_bill_address_phone",
|
||||
"order_bill_address_firstname",
|
||||
"order_bill_address_lastname",
|
||||
"order_bill_address_full_name",
|
||||
"order_bill_address_full_name_reversed",
|
||||
"order_bill_address_full_name_with_comma",
|
||||
"order_bill_address_full_name_with_comma_reversed",
|
||||
"variant_supplier_name",
|
||||
"order_email",
|
||||
"order_number",
|
||||
"product_name"].join("_or_") + "_cont"
|
||||
|
||||
$scope.confirmRefresh = ->
|
||||
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))
|
||||
@@ -75,11 +63,10 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
[formattedStartDate, formattedEndDate] = $scope.formatDates($scope.startDate, $scope.endDate)
|
||||
|
||||
RequestMonitor.load LineItems.index(
|
||||
"q[#{searchThrough}]": $scope.query,
|
||||
"q[variant_id_eq]": $scope.filterByVariantId if $scope.filterByVariantId,
|
||||
"q[order_state_not_eq]": "canceled",
|
||||
"q[order_shipment_state_not_eq]": "shipped",
|
||||
"q[order_completed_at_not_null]": "true",
|
||||
"q[variant_id_eq]": $scope.filterByVariantId if $scope.filterByVariantId,
|
||||
"q[order_distributor_id_eq]": $scope.distributorFilter,
|
||||
"q[variant_supplier_id_eq]": $scope.supplierFilter,
|
||||
"q[order_order_cycle_id_eq]": $scope.orderCycleFilter,
|
||||
@@ -87,7 +74,8 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
"q[order_completed_at_lt]": if formattedEndDate then formattedEndDate else undefined,
|
||||
"q[s]": "order_completed_at desc",
|
||||
"page": $scope.page,
|
||||
"per_page": $scope.per_page
|
||||
"per_page": $scope.per_page,
|
||||
"search_query": $scope.query
|
||||
)
|
||||
|
||||
$scope.formatDates = (startDate, endDate) ->
|
||||
|
||||
@@ -5,6 +5,8 @@ angular.module("admin.orders").controller "orderCtrl", ($scope, shops, orderCycl
|
||||
|
||||
$scope.distributor_id = parseInt($attrs.ofnDistributorId)
|
||||
$scope.order_cycle_id = parseInt($attrs.ofnOrderCycleId)
|
||||
$scope.search_variants_as = $attrs.ofnSearchVariantsAs
|
||||
$scope.order_id = $attrs.ofnOrderId
|
||||
|
||||
$scope.validOrderCycle = (oc) ->
|
||||
$scope.orderCycleHasDistributor oc, parseInt($scope.distributor_id)
|
||||
|
||||
@@ -15,4 +15,4 @@ angular.module("admin.paymentMethods").controller "StripeController", ($scope, $
|
||||
permalink = shops.filter((shop) ->
|
||||
shop.id == $scope.paymentMethod.preferred_enterprise_id
|
||||
)[0].permalink
|
||||
"/admin/enterprises/#{permalink}/edit#/payment_methods"
|
||||
"/admin/enterprises/#{permalink}/edit#/payment_methods_panel"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
angular.module("ofn.admin").controller "ProductImageCtrl", ($scope, ProductImageService) ->
|
||||
$scope.imageUploader = ProductImageService.imageUploader
|
||||
$scope.imagePreview = ProductImageService.imagePreview
|
||||
|
||||
$scope.$watch 'product.image_url', (newValue, oldValue) ->
|
||||
if newValue != oldValue
|
||||
$scope.imagePreview = newValue
|
||||
$scope.uploadModal.close()
|
||||
@@ -1,6 +0,0 @@
|
||||
angular.module("ofn.admin").directive "imageModal", ($modal, ProductImageService) ->
|
||||
restrict: 'C'
|
||||
link: (scope, elem, attrs, ctrl) ->
|
||||
elem.on "click", (ev) =>
|
||||
scope.uploadModal = $modal.open(templateUrl: 'admin/modals/image_upload.html', controller: ctrl, scope: scope, windowClass: 'simple-modal')
|
||||
ProductImageService.configure(scope.product)
|
||||
@@ -1,6 +0,0 @@
|
||||
angular.module("admin.resources").factory 'ProductResource', ($resource) ->
|
||||
$resource('/admin/product/:id/:action.json', {}, {
|
||||
'index':
|
||||
url: '/api/v0/products/bulk_products.json'
|
||||
method: 'GET'
|
||||
})
|
||||
@@ -1,76 +0,0 @@
|
||||
angular.module("ofn.admin").factory "BulkProducts", (ProductResource, dataFetcher, $http) ->
|
||||
new class BulkProducts
|
||||
products: []
|
||||
pagination: {}
|
||||
|
||||
fetch: (params) ->
|
||||
ProductResource.index params, (data) =>
|
||||
@products.length = 0
|
||||
@addProducts data.products
|
||||
angular.extend(@pagination, data.pagination)
|
||||
|
||||
cloneProduct: (product) ->
|
||||
$http.post("/api/v0/products/" + product.id + "/clone").then (response) =>
|
||||
dataFetcher("/api/v0/products/" + response.data.id + "?template=bulk_show").then (newProduct) =>
|
||||
@unpackProduct newProduct
|
||||
@insertProductAfter(product, newProduct)
|
||||
|
||||
updateVariantLists: (serverProducts) ->
|
||||
for server_product in serverProducts
|
||||
product = @findProductInList(server_product.id, @products)
|
||||
product.variants = server_product.variants
|
||||
@loadVariantUnitValues product.variants
|
||||
|
||||
find: (id) ->
|
||||
@findProductInList id, @products
|
||||
|
||||
findProductInList: (id, product_list) ->
|
||||
products = (product for product in product_list when product.id == id)
|
||||
if products.length == 0 then null else products[0]
|
||||
|
||||
addProducts: (products) ->
|
||||
for product in products
|
||||
@unpackProduct product
|
||||
@products.push product
|
||||
|
||||
insertProductAfter: (product, newProduct) ->
|
||||
index = @products.indexOf(product)
|
||||
@products.splice(index + 1, 0, newProduct)
|
||||
|
||||
unpackProduct: (product) ->
|
||||
@loadVariantUnit product
|
||||
|
||||
loadVariantUnit: (product) ->
|
||||
@loadVariantUnitValues product.variants if product.variants
|
||||
|
||||
loadVariantUnitValues: (variants) ->
|
||||
for variant in variants
|
||||
@loadVariantUnitValue variant
|
||||
|
||||
loadVariantUnitValue: (variant) ->
|
||||
variant.variant_unit_with_scale =
|
||||
if variant.variant_unit && variant.variant_unit_scale && variant.variant_unit != 'items'
|
||||
"#{variant.variant_unit}_#{variant.variant_unit_scale}"
|
||||
else if variant.variant_unit
|
||||
variant.variant_unit
|
||||
else
|
||||
null
|
||||
|
||||
unit_value = @variantUnitValue variant
|
||||
unit_value = if unit_value? then unit_value else ''
|
||||
variant.unit_value_with_description = "#{unit_value} #{variant.unit_description || ''}".trim()
|
||||
|
||||
variantUnitValue: (variant) ->
|
||||
if variant.unit_value?
|
||||
if variant.variant_unit_scale
|
||||
variant_unit_value = @divideAsInteger variant.unit_value, variant.variant_unit_scale
|
||||
parseFloat(window.bigDecimal.round(variant_unit_value, 2))
|
||||
else
|
||||
variant.unit_value
|
||||
else
|
||||
null
|
||||
|
||||
# forces integer division to avoid javascript floating point imprecision
|
||||
# using one billion as the multiplier so that it works for numbers with up to 9 decimal places
|
||||
divideAsInteger: (a, b) ->
|
||||
(a * 1000000000) / (b * 1000000000)
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("admin.tagRules").directive 'newTagRuleDialog', ($rootScope, $compile, $templateCache, DialogDefaults) ->
|
||||
angular.module("admin.tagRules").directive 'newTagRuleDialog', ($rootScope, $compile, $templateCache, DialogDefaults, ruleTypes) ->
|
||||
restrict: 'A'
|
||||
scope:
|
||||
tagGroup: '='
|
||||
@@ -7,12 +7,7 @@ angular.module("admin.tagRules").directive 'newTagRuleDialog', ($rootScope, $com
|
||||
# Compile modal template
|
||||
template = $compile($templateCache.get('admin/new_tag_rule_dialog.html'))(scope)
|
||||
|
||||
scope.ruleTypes = [
|
||||
{ id: "FilterProducts", name: t('js.tag_rules.show_hide_variants') }
|
||||
{ id: "FilterShippingMethods", name: t('js.tag_rules.show_hide_shipping') }
|
||||
{ id: "FilterPaymentMethods", name: t('js.tag_rules.show_hide_payment') }
|
||||
{ id: "FilterOrderCycles", name: t('js.tag_rules.show_hide_order_cycles') }
|
||||
]
|
||||
scope.ruleTypes = ruleTypes
|
||||
|
||||
scope.ruleType = scope.ruleTypes[0].id
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ angular.module("admin.utils").directive "variantAutocomplete", ($timeout) ->
|
||||
order_cycle_id: scope.order_cycle_id
|
||||
eligible_for_subscriptions: scope.eligible_for_subscriptions
|
||||
include_out_of_stock: scope.include_out_of_stock
|
||||
search_variants_as: scope.search_variants_as
|
||||
order_id: scope.order_id
|
||||
results: (data, page) ->
|
||||
window.variants = data # this is how spree auto complete JS code picks up variants
|
||||
results: data
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
angular.module('Darkswarm').directive "darkerBackground", ->
|
||||
restrict: "A"
|
||||
link: (scope, elm, attr)->
|
||||
toggleClass = (value) ->
|
||||
elm.closest('.page-view').toggleClass("with-darker-background", value)
|
||||
|
||||
toggleClass(true)
|
||||
|
||||
# if an OrderCycle is selected, disable darker background
|
||||
scope.$watch 'order_cycle.order_cycle_id', (newvalue, oldvalue) ->
|
||||
toggleClass(false) if newvalue
|
||||
@@ -1,14 +0,0 @@
|
||||
# Allows disabling of link buttons via disabled attribute.
|
||||
# This is normally ignored, ie the link appears disabled but is still clickable.
|
||||
|
||||
angular.module('Darkswarm').directive "disableDynamically", ->
|
||||
restrict: 'A'
|
||||
|
||||
link: (scope, element, attrs) ->
|
||||
element.on 'click', (e) ->
|
||||
if attrs.disabled
|
||||
e.preventDefault()
|
||||
return
|
||||
|
||||
scope.$on "$destroy", ->
|
||||
element.off("click")
|
||||
@@ -1,7 +0,0 @@
|
||||
angular.module('Darkswarm').directive "ofnInlineAlert", ->
|
||||
restrict: 'A'
|
||||
scope: true
|
||||
link: (scope, elem, attrs) ->
|
||||
scope.visible = true
|
||||
scope.close = ->
|
||||
scope.visible = false
|
||||
@@ -1,21 +0,0 @@
|
||||
angular.module('Darkswarm').directive "ofnPageAlert", ($timeout) ->
|
||||
restrict: 'A'
|
||||
scope: true
|
||||
link: (scope, elem, attrs) ->
|
||||
moveSelectors = [".off-canvas-wrap .inner-wrap",
|
||||
".off-canvas-wrap .inner-wrap .fixed",
|
||||
".off-canvas-fixed .top-bar",
|
||||
".off-canvas-fixed ofn-flash",
|
||||
".off-canvas-fixed nav.tab-bar",
|
||||
".off-canvas-fixed .page-alert"]
|
||||
|
||||
container_elems = $(moveSelectors.join(", "))
|
||||
|
||||
# Wait a moment after page load before showing the alert. Otherwise we often miss the
|
||||
# start of the animation.
|
||||
$timeout ->
|
||||
container_elems.addClass("move-up")
|
||||
, 1000
|
||||
|
||||
scope.close = ->
|
||||
container_elems.removeClass("move-up")
|
||||
@@ -3,4 +3,4 @@
|
||||
%span.text-normal
|
||||
{{ 'admin.tags' | t }}
|
||||
%br
|
||||
%tags-with-translation.fullwidth{ object: 'object' }
|
||||
%tags-with-translation.fullwidth{ object: 'object', form: 'order_cycle_form', id: 'tags_with_translation'}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// This controller will be called "example-component--example", ie "component-subdirectory--js-file-name"
|
||||
// This controller will be called "example", ie "js-file-name" minus the "_controller.js"
|
||||
// see controller/index.js for more info
|
||||
import { Controller } from "stimulus";
|
||||
|
||||
export default class extends Controller {}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
%div{ "data-controller": "tag-list-input-component--tag-list-input" }
|
||||
%div{ "data-controller": "tag-list-input" }
|
||||
.tags-input
|
||||
.tags
|
||||
- # We use display:none instead of hidden field, so changes to the value can be picked up by the bulkFormController
|
||||
= f.text_field method.to_sym, value: tags.join(","), "data-tag-list-input-component--tag-list-input-target": "tagList", "style": "display: none"
|
||||
%ul.tag-list{"data-tag-list-input-component--tag-list-input-target": "list"}
|
||||
%template{"data-tag-list-input-component--tag-list-input-target": "template"}
|
||||
= f.text_field method.to_sym, value: tags.join(","), "data-tag-list-input-target": "tagList", "style": "display: none"
|
||||
%ul.tag-list{"data-tag-list-input-target": "list"}
|
||||
%template{"data-tag-list-input-target": "template"}
|
||||
%li.tag-item
|
||||
.tag-template
|
||||
%span
|
||||
%a.remove-button{ "data-action": "click->tag-list-input-component--tag-list-input#removeTag" }
|
||||
%a.remove-button{ "data-action": "click->tag-list-input#removeTag" }
|
||||
×
|
||||
- tags.each do |tag|
|
||||
%li.tag-item
|
||||
.tag-template
|
||||
%span=tag
|
||||
%a.remove-button{ "data-action": "click->tag-list-input-component--tag-list-input#removeTag" }
|
||||
%a.remove-button{ "data-action": "click->tag-list-input#removeTag" }
|
||||
×
|
||||
= text_field_tag "variant_add_tag_#{f.object.id}".to_sym, nil, class: "input", placeholder: placeholder, "data-action": "keydown.enter->tag-list-input-component--tag-list-input#addTag keyup->tag-list-input-component--tag-list-input#filterInput", "data-tag-list-input-component--tag-list-input-target": "newTag", **aria_label_option
|
||||
= text_field_tag "variant_add_tag_#{f.object.id}".to_sym, nil, class: "input", placeholder: placeholder, "data-action": "keydown.enter->tag-list-input#addTag keyup->tag-list-input#filterInput", "data-tag-list-input-target": "newTag", **aria_label_option
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module VerticalEllipsisMenu
|
||||
class Component < ViewComponent::Base
|
||||
end
|
||||
end
|
||||
4
app/components/vertical_ellipsis_menu_component.rb
Normal file
4
app/components/vertical_ellipsis_menu_component.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class VerticalEllipsisMenuComponent < ViewComponent::Base
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
.vertical-ellipsis-menu{ "data-controller": "vertical-ellipsis-menu--component" }
|
||||
%i.fa.fa-ellipsis-v{ "data-action": "click->vertical-ellipsis-menu--component#toggle" }
|
||||
.vertical-ellipsis-menu-content{ "data-vertical-ellipsis-menu--component-target": "content" }
|
||||
.vertical-ellipsis-menu{ "data-controller": "vertical-ellipsis-menu" }
|
||||
%i.fa.fa-ellipsis-v{ "data-action": "click->vertical-ellipsis-menu#toggle" }
|
||||
.vertical-ellipsis-menu-content{ "data-vertical-ellipsis-menu-target": "content" }
|
||||
= content
|
||||
@@ -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(line_items_search_query).result.order(:id)
|
||||
|
||||
@pagy, @line_items = pagy(@line_items) if pagination_required?
|
||||
|
||||
@@ -88,5 +88,27 @@ module Admin
|
||||
def page
|
||||
params[:page] || 1
|
||||
end
|
||||
|
||||
def line_items_search_query
|
||||
query = params.permit(q: {}).to_h[:q] || {}
|
||||
|
||||
search_fields_string = [
|
||||
spree_current_user.admin? ? "order_distributor_name" : "order_distributor_name_alias",
|
||||
"order_bill_address_phone",
|
||||
"order_bill_address_firstname",
|
||||
"order_bill_address_lastname",
|
||||
"order_bill_address_full_name",
|
||||
"order_bill_address_full_name_reversed",
|
||||
"order_bill_address_full_name_with_comma",
|
||||
"order_bill_address_full_name_with_comma_reversed",
|
||||
"variant_supplier_name",
|
||||
"order_email",
|
||||
"order_number",
|
||||
"product_name"
|
||||
].join("_or_")
|
||||
search_query = "#{search_fields_string}_cont"
|
||||
|
||||
query.merge({ search_query => params[:search_query] })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,6 +48,9 @@ module Admin
|
||||
@object = Enterprise.where(permalink: params[:id]).
|
||||
includes(users: [:ship_address, :bill_address]).first
|
||||
@object.build_custom_tab if @object.custom_tab.nil?
|
||||
|
||||
load_tag_rule_types
|
||||
|
||||
return unless params[:stimulus]
|
||||
|
||||
@enterprise.is_primary_producer = params[:is_primary_producer]
|
||||
@@ -375,6 +378,19 @@ module Admin
|
||||
@properties = Spree::Property.pluck(:name)
|
||||
end
|
||||
|
||||
def load_tag_rule_types
|
||||
# Load rule types
|
||||
@tag_rule_types = [
|
||||
{ id: "FilterShippingMethods", name: t('js.tag_rules.show_hide_shipping') },
|
||||
{ id: "FilterPaymentMethods", name: t('js.tag_rules.show_hide_payment') },
|
||||
{ id: "FilterOrderCycles", name: t('js.tag_rules.show_hide_order_cycles') }
|
||||
]
|
||||
|
||||
return unless helpers.feature?(:inventory, @object)
|
||||
|
||||
@tag_rule_types.prepend({ id: "FilterProducts", name: t('js.tag_rules.show_hide_variants') })
|
||||
end
|
||||
|
||||
def setup_property
|
||||
@enterprise.producer_properties.build
|
||||
end
|
||||
|
||||
@@ -17,7 +17,10 @@ module Admin
|
||||
end
|
||||
|
||||
def import
|
||||
return unless can_import_into_inventories?
|
||||
|
||||
@filepath = save_uploaded_file(params[:file])
|
||||
|
||||
@importer = ProductImport::ProductImporter.new(File.new(@filepath), spree_current_user,
|
||||
params[:settings])
|
||||
@original_filename = params[:file].try(:original_filename)
|
||||
@@ -36,7 +39,22 @@ module Admin
|
||||
def save_data
|
||||
return unless process_data('save')
|
||||
|
||||
render json: @importer.save_results
|
||||
json = {
|
||||
results: {
|
||||
products_created: @importer.products_created_count,
|
||||
products_updated: @importer.products_updated_count,
|
||||
products_reset: @importer.products_reset_count,
|
||||
},
|
||||
updated_ids: @importer.updated_ids,
|
||||
errors: @importer.errors.full_messages
|
||||
}
|
||||
|
||||
if helpers.feature?(:inventory, spree_current_user.enterprises)
|
||||
json[:results][:inventory_created] = @importer.inventory_created_count
|
||||
json[:results][:inventory_updated] = @importer.inventory_updated_count
|
||||
end
|
||||
|
||||
render json:
|
||||
end
|
||||
|
||||
def reset_absent_products
|
||||
@@ -154,5 +172,15 @@ module Admin
|
||||
notice: I18n.t(:product_import_no_data_in_spreadsheet_notice)
|
||||
raise 'Invalid File Path'
|
||||
end
|
||||
|
||||
# Return an error if trying to import into inventories when inventory is disable
|
||||
def can_import_into_inventories?
|
||||
return true if helpers.feature?(:inventory, spree_current_user.enterprises) ||
|
||||
params.dig(:settings, "import_into") != 'inventories'
|
||||
|
||||
redirect_to admin_product_import_url, notice: I18n.t(:product_import_inventory_disable)
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -123,6 +123,13 @@ module Admin
|
||||
@page = params[:page].presence || 1
|
||||
@per_page = params[:per_page].presence || 15
|
||||
@q = params.permit(q: {})[:q] || { s: 'name asc' }
|
||||
|
||||
# Transform on_hand sorting to include backorderable_priority (on-demand) for proper ordering
|
||||
if @q[:s] == 'on_hand asc'
|
||||
@q[:s] = ['backorderable_priority asc', @q[:s]]
|
||||
elsif @q[:s] == 'on_hand desc'
|
||||
@q[:s] = ['backorderable_priority desc', @q[:s]]
|
||||
end
|
||||
end
|
||||
|
||||
def producers
|
||||
@@ -155,8 +162,27 @@ module Admin
|
||||
product_query = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
.editable_products.merge(product_scope_with_includes).ransack(ransack_query).result
|
||||
|
||||
@pagy, @products = pagy(product_query.order(:name), limit: @per_page, page: @page,
|
||||
size: [1, 2, 2, 1])
|
||||
# Postgres requires ORDER BY expressions to appear in the SELECT list when using DISTINCT.
|
||||
# When the current ransack sort uses the computed stock columns, include them in the select
|
||||
# so the generated COUNT/DISTINCT query is valid.
|
||||
sort_columns = Array(@q && @q[:s]).flatten
|
||||
if sort_columns.any? { |s|
|
||||
s.to_s.include?('on_hand') || s.to_s.include?('backorderable_priority')
|
||||
}
|
||||
|
||||
product_query = product_query.select(
|
||||
Arel.sql('spree_products.*'),
|
||||
Spree::Product.backorderable_priority_sql,
|
||||
Spree::Product.on_hand_sql
|
||||
)
|
||||
end
|
||||
|
||||
@pagy, @products = pagy(
|
||||
product_query.order(:name),
|
||||
limit: @per_page,
|
||||
page: @page,
|
||||
size: [1, 2, 2, 1]
|
||||
)
|
||||
end
|
||||
|
||||
def product_scope
|
||||
|
||||
@@ -53,7 +53,9 @@ module Admin
|
||||
return unless @order_cycle
|
||||
|
||||
fee_calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(@shop, @order_cycle)
|
||||
|
||||
OpenFoodNetwork::ScopeVariantToHub.new(@shop).scope(@variant)
|
||||
|
||||
@variant.price + fee_calculator.indexed_fees_for(@variant)
|
||||
end
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ module Api
|
||||
distributor,
|
||||
order_cycle,
|
||||
customer,
|
||||
search_params
|
||||
search_params,
|
||||
inventory_enabled:
|
||||
).products_json
|
||||
|
||||
render plain: products
|
||||
@@ -95,9 +96,13 @@ module Api
|
||||
|
||||
def distributed_products
|
||||
OrderCycles::DistributedProductsService.new(
|
||||
distributor, order_cycle, customer
|
||||
distributor, order_cycle, customer, inventory_enabled:
|
||||
).products_relation.pluck(:id)
|
||||
end
|
||||
|
||||
def inventory_enabled
|
||||
OpenFoodNetwork::FeatureToggle.enabled?(:inventory, distributor)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -112,7 +112,9 @@ module Api
|
||||
|
||||
def scoped_variant(variant_id)
|
||||
variant = Spree::Variant.find(variant_id)
|
||||
|
||||
OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant)
|
||||
|
||||
variant
|
||||
end
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ module Api
|
||||
def error_during_processing(exception)
|
||||
Alert.raise(exception)
|
||||
|
||||
if Rails.env.development? || Rails.env.test?
|
||||
if Rails.env.local?
|
||||
render status: :unprocessable_entity,
|
||||
json: json_api_error(exception.message, meta: exception.backtrace)
|
||||
else
|
||||
|
||||
@@ -80,8 +80,6 @@ class CheckoutController < BaseController
|
||||
|
||||
@order.customer.touch :terms_and_conditions_accepted_at
|
||||
|
||||
return true if redirect_to_payment_gateway
|
||||
|
||||
# Redeem VINE voucher
|
||||
vine_voucher_redeemer = Vine::VoucherRedeemerService.new(order: @order)
|
||||
unless vine_voucher_redeemer.redeem
|
||||
@@ -94,6 +92,9 @@ class CheckoutController < BaseController
|
||||
return false
|
||||
# rubocop:enable Rails/DeprecatedActiveModelErrorsMethods
|
||||
end
|
||||
|
||||
return true if redirect_to_payment_gateway
|
||||
|
||||
@order.process_payments!
|
||||
@order.confirm!
|
||||
BackorderJob.check_stock(@order)
|
||||
|
||||
@@ -19,11 +19,6 @@ module Spree
|
||||
before_action :load_spree_api_key, only: [:index, :variant_overrides]
|
||||
before_action :strip_new_properties, only: [:create, :update]
|
||||
|
||||
def index
|
||||
@current_user = spree_current_user
|
||||
@show_latest_import = params[:latest_import] || false
|
||||
end
|
||||
|
||||
def show
|
||||
session[:return_to] ||= request.referer
|
||||
redirect_to( action: :edit )
|
||||
@@ -65,28 +60,6 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
def bulk_update
|
||||
product_set = product_set_from_params
|
||||
|
||||
product_set.collection.each { |p| authorize! :update, p }
|
||||
|
||||
if product_set.save
|
||||
redirect_to main_app.bulk_products_api_v0_products_path(bulk_index_query)
|
||||
elsif product_set.errors.present?
|
||||
render json: { errors: product_set.errors }, status: :bad_request
|
||||
else
|
||||
render body: nil, status: :internal_server_error
|
||||
end
|
||||
end
|
||||
|
||||
def clone
|
||||
@new = @product.duplicate
|
||||
raise "Clone failed" unless @new.save
|
||||
|
||||
flash[:success] = t('.success')
|
||||
redirect_to spree.admin_products_url
|
||||
end
|
||||
|
||||
def group_buy_options
|
||||
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
|
||||
end
|
||||
|
||||
@@ -117,7 +117,7 @@ module Spree
|
||||
def variant_search_params
|
||||
params.permit(
|
||||
:q, :distributor_id, :order_cycle_id, :schedule_id, :eligible_for_subscriptions,
|
||||
:include_out_of_stock
|
||||
:include_out_of_stock, :search_variants_as, :order_id
|
||||
).to_h.with_indifferent_access
|
||||
end
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ module Admin
|
||||
enterprise.in?(spree_current_user.enterprises)
|
||||
end
|
||||
|
||||
def enterprise_side_menu_items(enterprise)
|
||||
def enterprise_side_menu_items(enterprise) # rubocop:disable Metrics/CyclomaticComplexity
|
||||
is_shop = enterprise.sells != "none"
|
||||
show_properties = !!enterprise.is_primary_producer
|
||||
show_shipping_methods = can?(:manage_shipping_methods, enterprise) && is_shop
|
||||
@@ -26,24 +26,35 @@ module Admin
|
||||
show_enterprise_fees = can?(:manage_enterprise_fees,
|
||||
enterprise) && (is_shop || enterprise.is_primary_producer)
|
||||
show_connected_apps = can?(:manage_connected_apps, enterprise) &&
|
||||
feature?(:connected_apps, spree_current_user, enterprise) &&
|
||||
Spree::Config.connected_apps_enabled.present?
|
||||
(connected_apps_enabled(enterprise).present? ||
|
||||
dfc_platforms_available?)
|
||||
show_inventory_settings = feature?(:inventory, spree_current_user.enterprises) && is_shop
|
||||
|
||||
build_enterprise_side_menu_items(
|
||||
is_shop:,
|
||||
show_options = {
|
||||
show_properties:,
|
||||
show_shipping_methods:,
|
||||
show_payment_methods:,
|
||||
show_enterprise_fees:,
|
||||
show_connected_apps:,
|
||||
)
|
||||
show_inventory_settings:,
|
||||
}
|
||||
|
||||
build_enterprise_side_menu_items(is_shop:, show_options:)
|
||||
end
|
||||
|
||||
def connected_apps_enabled
|
||||
def connected_apps_enabled(enterprise)
|
||||
return [] unless feature?(:connected_apps, spree_current_user, enterprise)
|
||||
|
||||
connected_apps_enabled = Spree::Config.connected_apps_enabled&.split(',') || []
|
||||
ConnectedApp::TYPES & connected_apps_enabled
|
||||
end
|
||||
|
||||
def dfc_platforms_available?
|
||||
DfcProvider::PlatformsController::PLATFORM_IDS.keys.any? do |id|
|
||||
feature?(id, spree_current_user)
|
||||
end
|
||||
end
|
||||
|
||||
def enterprise_attachment_removal_modal_id
|
||||
attachment_removal_parameter # remove_logo|remove_promo_image|remove_white_label_logo
|
||||
end
|
||||
@@ -60,16 +71,14 @@ module Admin
|
||||
"#{enterprise_attachment_removal_panel}_panel"
|
||||
end
|
||||
|
||||
def enterprise_sells_options
|
||||
scope = "admin.enterprises.admin_index.sells_options"
|
||||
Enterprise::SELLS.map { |s| [I18n.t(s, scope:), s] }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_enterprise_side_menu_items(
|
||||
is_shop:,
|
||||
show_properties:,
|
||||
show_shipping_methods:,
|
||||
show_payment_methods:,
|
||||
show_enterprise_fees:,
|
||||
show_connected_apps:
|
||||
)
|
||||
def build_enterprise_side_menu_items(is_shop:, show_options: ) # rubocop:disable Metrics/MethodLength
|
||||
[
|
||||
{ name: 'primary_details', icon_class: "icon-home", show: true, selected: 'selected' },
|
||||
{ name: 'address', icon_class: "icon-map-marker", show: true },
|
||||
@@ -78,19 +87,24 @@ module Admin
|
||||
{ name: 'about', icon_class: "icon-pencil", show: true, form_name: "about_us" },
|
||||
{ name: 'business_details', icon_class: "icon-briefcase", show: true },
|
||||
{ name: 'images', icon_class: "icon-picture", show: true },
|
||||
{ name: 'properties', icon_class: "icon-tags", show: show_properties },
|
||||
{ name: 'shipping_methods', icon_class: "icon-truck", show: show_shipping_methods },
|
||||
{ name: 'payment_methods', icon_class: "icon-money", show: show_payment_methods },
|
||||
{ name: 'enterprise_fees', icon_class: "icon-tasks", show: show_enterprise_fees },
|
||||
{ name: 'properties', icon_class: "icon-tags", show: show_options[:show_properties] },
|
||||
{ name: 'shipping_methods', icon_class: "icon-truck",
|
||||
show: show_options[:show_shipping_methods] },
|
||||
{ name: 'payment_methods', icon_class: "icon-money",
|
||||
show: show_options[:show_payment_methods] },
|
||||
{ name: 'enterprise_fees', icon_class: "icon-tasks",
|
||||
show: show_options[:show_enterprise_fees] },
|
||||
{ name: 'vouchers', icon_class: "icon-ticket", show: is_shop },
|
||||
{ name: 'enterprise_permissions', icon_class: "icon-plug", show: true,
|
||||
href: admin_enterprise_relationships_path },
|
||||
{ name: 'inventory_settings', icon_class: "icon-list-ol", show: is_shop },
|
||||
{ name: 'inventory_settings', icon_class: "icon-list-ol",
|
||||
show: show_options[:show_inventory_settings] },
|
||||
{ name: 'tag_rules', icon_class: "icon-random", show: is_shop },
|
||||
{ name: 'shop_preferences', icon_class: "icon-shopping-cart", show: is_shop },
|
||||
{ name: 'white_label', icon_class: "icon-leaf", show: true },
|
||||
{ name: 'users', icon_class: "icon-user", show: true },
|
||||
{ name: 'connected_apps', icon_class: "icon-puzzle-piece", show: show_connected_apps },
|
||||
{ name: 'connected_apps', icon_class: "icon-puzzle-piece",
|
||||
show: show_options[:show_connected_apps] },
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,12 +32,8 @@ module Admin
|
||||
[precised_unit_value, variant.unit_description].compact_blank.join(" ")
|
||||
end
|
||||
|
||||
def products_return_to_url(url_filters)
|
||||
if feature?(:admin_style_v3, spree_current_user)
|
||||
return session[:products_return_to_url] || admin_products_url
|
||||
end
|
||||
|
||||
"#{admin_products_path}#{url_filters.empty? ? '' : "#?#{url_filters.to_query}"}"
|
||||
def products_return_to_url
|
||||
session[:products_return_to_url] || admin_products_url
|
||||
end
|
||||
|
||||
# if user hasn't saved any preferences on products page and there's only one producer;
|
||||
|
||||
@@ -5,10 +5,6 @@ module CheckoutHelper
|
||||
order.ship_address == order.bill_address
|
||||
end
|
||||
|
||||
def guest_checkout_allowed?
|
||||
current_order.distributor.allow_guest_orders?
|
||||
end
|
||||
|
||||
def checkout_adjustments_for(order, opts = {})
|
||||
exclude = opts[:exclude] || {}
|
||||
reject_zero_amount = opts.fetch(:reject_zero_amount, true)
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module LinkHelper
|
||||
def link_to_or_disabled(name = nil, options = nil, html_options = nil, &block)
|
||||
html_options, options, name = options, name, block if block_given?
|
||||
html_options ||= {}
|
||||
|
||||
if !!html_options.delete(:disabled)
|
||||
# https://www.scottohara.me/blog/2021/05/28/disabled-links.html
|
||||
html_options.merge!(
|
||||
'aria-disabled': true,
|
||||
class: (html_options[:class].to_s.split + ["disabled"]).uniq.join(" "),
|
||||
role: "link"
|
||||
)
|
||||
if block_given?
|
||||
content_tag("a", name, **html_options, &block)
|
||||
else
|
||||
content_tag("a", name, **html_options)
|
||||
end
|
||||
elsif block_given?
|
||||
link_to options, html_options, &block
|
||||
else
|
||||
link_to name, options, html_options
|
||||
end
|
||||
end
|
||||
|
||||
def link_to_service(baseurl, name, html_options = {}, &)
|
||||
return if name.blank?
|
||||
|
||||
|
||||
@@ -62,6 +62,12 @@ module ShopHelper
|
||||
true
|
||||
end
|
||||
|
||||
def shop_tab_class(tab)
|
||||
return unless (tab == "home" && show_home_tab?) || current_order(false)&.order_cycle.nil?
|
||||
|
||||
"with-darker-background"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def show_groups_tabs?
|
||||
|
||||
@@ -29,13 +29,7 @@ module Spree
|
||||
scope: [:admin, :tab]).capitalize
|
||||
|
||||
css_classes = []
|
||||
|
||||
if options[:icon] && !feature?(:admin_style_v3, spree_current_user)
|
||||
link = link_to_with_icon(options[:icon], titleized_label, destination_url)
|
||||
css_classes << 'tab-with-icon'
|
||||
else
|
||||
link = link_to(titleized_label, destination_url)
|
||||
end
|
||||
link = link_to(titleized_label, destination_url)
|
||||
|
||||
selected = if options[:match_path]
|
||||
PathChecker
|
||||
|
||||
@@ -155,8 +155,7 @@ module Spree
|
||||
end
|
||||
|
||||
def filter_by_supplier?(order)
|
||||
order.distributor&.enable_producers_to_edit_orders &&
|
||||
spree_current_user.can_manage_line_items_in_orders_only?
|
||||
can? :edit_as_producer_only, order
|
||||
end
|
||||
|
||||
def display_value_for_producer(order, value)
|
||||
|
||||
@@ -9,6 +9,7 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
include ArelHelpers::JoinAssociation
|
||||
|
||||
self.abstract_class = true
|
||||
self.include_root_in_json = true
|
||||
|
||||
def self.image_service
|
||||
ENV["S3_BUCKET"].present? ? :amazon_public : :local
|
||||
@@ -20,6 +21,10 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
if ENV["S3_BUCKET"].present? && variant.service.public?
|
||||
variant.processed.url
|
||||
else
|
||||
unless variant.blob.persisted?
|
||||
raise "ActiveStorage blob for variant is not persisted. Cannot generate URL."
|
||||
end
|
||||
|
||||
url_for(variant)
|
||||
end
|
||||
end
|
||||
|
||||
35
app/models/concerns/product_sort_by_stocks.rb
Normal file
35
app/models/concerns/product_sort_by_stocks.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ProductSortByStocks
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
@on_hand_sql = Arel.sql("(
|
||||
SELECT COALESCE(SUM(si.count_on_hand), 0)
|
||||
FROM spree_variants v
|
||||
JOIN spree_stock_items si ON si.variant_id = v.id
|
||||
WHERE v.product_id = spree_products.id
|
||||
GROUP BY v.product_id
|
||||
)")
|
||||
|
||||
@backorderable_priority_sql = Arel.sql("(
|
||||
SELECT BOOL_OR(si.backorderable)
|
||||
FROM spree_variants v
|
||||
JOIN spree_stock_items si ON si.variant_id = v.id
|
||||
WHERE v.product_id = spree_products.id
|
||||
GROUP BY v.product_id
|
||||
)")
|
||||
|
||||
class << self
|
||||
attr_reader :on_hand_sql, :backorderable_priority_sql
|
||||
end
|
||||
|
||||
ransacker :on_hand do
|
||||
@on_hand_sql
|
||||
end
|
||||
|
||||
ransacker :backorderable_priority do
|
||||
@backorderable_priority_sql
|
||||
end
|
||||
end
|
||||
end
|
||||
15
app/models/dfc_permission.rb
Normal file
15
app/models/dfc_permission.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Authorisations of a user allowing a platform to access to data.
|
||||
class DfcPermission < ApplicationRecord
|
||||
SCOPES = %w[
|
||||
ReadEnterprise ReadProducts ReadOrders
|
||||
WriteEnterprise WriteProducts WriteOrders
|
||||
].freeze
|
||||
|
||||
belongs_to :user, class_name: "Spree::User"
|
||||
belongs_to :enterprise
|
||||
|
||||
validates :grantee, presence: true
|
||||
validates :scope, presence: true, inclusion: { in: SCOPES }
|
||||
end
|
||||
@@ -75,6 +75,7 @@ class Enterprise < ApplicationRecord
|
||||
has_one :stripe_account, dependent: :destroy
|
||||
has_many :vouchers, dependent: :restrict_with_exception
|
||||
has_many :connected_apps, dependent: :destroy
|
||||
has_many :dfc_permissions, dependent: :destroy
|
||||
has_one :custom_tab, dependent: :destroy
|
||||
|
||||
delegate :latitude, :longitude, :city, :state_name, to: :address
|
||||
|
||||
@@ -286,9 +286,13 @@ class OrderCycle < ApplicationRecord
|
||||
user_id: user,
|
||||
distributor_id: distributor,
|
||||
order_cycle_id: self)
|
||||
|
||||
items = Spree::LineItem.includes(:variant).joins(:order).merge(orders)
|
||||
|
||||
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
|
||||
items = Spree::LineItem.includes(:variant).joins(:order).merge(orders).to_a
|
||||
items.each { |li| scoper.scope(li.variant) }
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
def distributor_payment_methods
|
||||
|
||||
@@ -142,20 +142,6 @@ module ProductImport
|
||||
{ entries: entries_json, reset_counts: }
|
||||
end
|
||||
|
||||
def save_results
|
||||
{
|
||||
results: {
|
||||
products_created: products_created_count,
|
||||
products_updated: products_updated_count,
|
||||
inventory_created: inventory_created_count,
|
||||
inventory_updated: inventory_updated_count,
|
||||
products_reset: products_reset_count,
|
||||
},
|
||||
updated_ids:,
|
||||
errors: errors.full_messages
|
||||
}
|
||||
end
|
||||
|
||||
def validate_entries
|
||||
@validator.validate_all(@entries)
|
||||
end
|
||||
@@ -207,6 +193,8 @@ module ProductImport
|
||||
order('is_primary_producer ASC, name').
|
||||
map { |e| @editable_enterprises[e.name] = e.id }
|
||||
|
||||
return unless OpenFoodNetwork::FeatureToggle.enabled?(:inventory, @current_user.enterprises)
|
||||
|
||||
@inventory_permissions = permissions.variant_override_enterprises_per_hub
|
||||
end
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ module Spree
|
||||
|
||||
if user.try(:admin?)
|
||||
can :manage, :all
|
||||
|
||||
# this action was needed for restrictions for distributors and suppliers
|
||||
# however, admins don't need to be restricted, so, bypassing it for admins
|
||||
cannot :edit_as_producer_only, Spree::Order
|
||||
else
|
||||
can [:index, :read], Country
|
||||
can :create, Order
|
||||
@@ -209,18 +213,20 @@ module Spree
|
||||
managed_product_enterprises.include? variant.supplier
|
||||
end
|
||||
|
||||
can [:admin, :index, :read, :update, :bulk_update, :bulk_reset], VariantOverride do |vo|
|
||||
next false unless vo.hub.present? && vo.variant&.supplier.present?
|
||||
if OpenFoodNetwork::FeatureToggle.enabled?(:inventory, user.enterprises)
|
||||
can [:admin, :index, :read, :update, :bulk_update, :bulk_reset], VariantOverride do |vo|
|
||||
next false unless vo.hub.present? && vo.variant&.supplier.present?
|
||||
|
||||
hub_auth = OpenFoodNetwork::Permissions.new(user).
|
||||
variant_override_hubs.
|
||||
include? vo.hub
|
||||
hub_auth = OpenFoodNetwork::Permissions.new(user).
|
||||
variant_override_hubs.
|
||||
include? vo.hub
|
||||
|
||||
producer_auth = OpenFoodNetwork::Permissions.new(user).
|
||||
variant_override_producers.
|
||||
include? vo.variant.supplier
|
||||
producer_auth = OpenFoodNetwork::Permissions.new(user).
|
||||
variant_override_producers.
|
||||
include? vo.variant.supplier
|
||||
|
||||
hub_auth && producer_auth
|
||||
hub_auth && producer_auth
|
||||
end
|
||||
end
|
||||
|
||||
can [:admin, :create, :update], InventoryItem do |ii|
|
||||
@@ -257,8 +263,13 @@ module Spree
|
||||
end
|
||||
|
||||
def add_order_cycle_management_abilities(user)
|
||||
can [:admin, :index], OrderCycle do |order_cycle|
|
||||
OrderCycle.visible_by(user).include?(order_cycle) ||
|
||||
order_cycle.orders.editable_by_producers(user.enterprises).exists?
|
||||
end
|
||||
|
||||
can [
|
||||
:admin, :index, :read, :edit, :update, :incoming, :outgoing, :checkout_options
|
||||
:read, :edit, :update, :incoming, :outgoing, :checkout_options
|
||||
], OrderCycle do |order_cycle|
|
||||
OrderCycle.visible_by(user).include? order_cycle
|
||||
end
|
||||
@@ -274,8 +285,37 @@ module Spree
|
||||
end
|
||||
|
||||
def add_order_management_abilities(user)
|
||||
can [:index, :create], Spree::Order
|
||||
can [:read, :update, :fire, :resend, :invoice, :print], Spree::Order do |order|
|
||||
can [:manage_order_sections], Spree::Order do |order|
|
||||
user.admin? ||
|
||||
order.distributor.nil? ||
|
||||
user.enterprises.include?(order.distributor) ||
|
||||
order.order_cycle&.coordinated_by?(user)
|
||||
end
|
||||
|
||||
can [:edit_as_producer_only], Spree::Order do |order|
|
||||
cannot?(:manage_order_sections, order) && can_edit_as_producer(order, user)
|
||||
end
|
||||
|
||||
can [:index], Spree::Order do
|
||||
user.admin? ||
|
||||
user.enterprises.any?(&:is_distributor) ||
|
||||
user.enterprises.distributors.where(enable_producers_to_edit_orders: true).exist?
|
||||
end
|
||||
|
||||
can [:create], Spree::Order
|
||||
|
||||
can [:read, :update], Spree::Order do |order|
|
||||
# We allow editing orders with a nil distributor as this state occurs
|
||||
# during the order creation process from the admin backend
|
||||
order.distributor.nil? ||
|
||||
# Enterprise User can access orders that they are a distributor for
|
||||
user.enterprises.include?(order.distributor) ||
|
||||
# Enterprise User can access orders that are placed inside a OC they coordinate
|
||||
order.order_cycle&.coordinated_by?(user) ||
|
||||
can_edit_as_producer(order, user)
|
||||
end
|
||||
|
||||
can [:fire, :resend, :invoice, :print], Spree::Order do |order|
|
||||
# We allow editing orders with a nil distributor as this state occurs
|
||||
# during the order creation process from the admin backend
|
||||
order.distributor.nil? ||
|
||||
@@ -284,22 +324,39 @@ module Spree
|
||||
# Enterprise User can access orders that are placed inside a OC they coordinate
|
||||
order.order_cycle&.coordinated_by?(user)
|
||||
end
|
||||
can [:admin, :bulk_management, :managed, :distribution], Spree::Order do
|
||||
|
||||
can [:admin, :bulk_management], Spree::Order do |order|
|
||||
user.admin? ||
|
||||
user.enterprises.any?(&:is_distributor) ||
|
||||
can_edit_as_producer(order, user)
|
||||
end
|
||||
|
||||
can [:managed, :distribution], Spree::Order do
|
||||
user.admin? || user.enterprises.any?(&:is_distributor)
|
||||
end
|
||||
can [:admin, :index, :create, :show, :poll, :generate], :invoice
|
||||
can [:admin, :visible], Enterprise
|
||||
can [:admin, :index, :create, :update, :destroy], :line_item
|
||||
can [:admin, :index, :create], Spree::LineItem
|
||||
can [:admin, :index, :create], Spree::LineItem do |item|
|
||||
user.admin? ||
|
||||
user.enterprises.any?(&:is_distributor) ||
|
||||
can_edit_as_producer(item.order, user)
|
||||
end
|
||||
can [:destroy, :update], Spree::LineItem do |item|
|
||||
order = item.order
|
||||
user.admin? ||
|
||||
user.enterprises.include?(order.distributor) ||
|
||||
order.order_cycle&.coordinated_by?(user)
|
||||
order.order_cycle&.coordinated_by?(user) ||
|
||||
can_edit_as_producer(order, user)
|
||||
end
|
||||
|
||||
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Shipment do |shipment|
|
||||
user.admin? ||
|
||||
user.enterprises.any?(&:is_distributor) ||
|
||||
can_edit_as_producer(shipment.order, user)
|
||||
end
|
||||
|
||||
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Payment
|
||||
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Shipment
|
||||
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Adjustment
|
||||
can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::ReturnAuthorization
|
||||
can [:destroy], Spree::Adjustment do |adjustment|
|
||||
@@ -350,26 +407,35 @@ module Spree
|
||||
can [:admin, :edit, :cancel, :resume], ProxyOrder do |proxy_order|
|
||||
user.enterprises.include?(proxy_order.subscription.shop)
|
||||
end
|
||||
can [:visible], Enterprise
|
||||
end
|
||||
|
||||
def can_edit_order(order, user)
|
||||
def can_edit_as_producer(order, user)
|
||||
return unless order.distributor&.enable_producers_to_edit_orders
|
||||
|
||||
order.variants.any? { |variant| user.enterprises.ids.include?(variant.supplier_id) }
|
||||
end
|
||||
|
||||
def add_manage_line_items_abilities(user)
|
||||
can [:admin, :read, :index, :edit, :update, :bulk_management], Spree::Order do |order|
|
||||
can_edit_order(order, user)
|
||||
can [
|
||||
:admin,
|
||||
:read,
|
||||
:index,
|
||||
:edit,
|
||||
:update,
|
||||
:bulk_management,
|
||||
:edit_as_producer_only
|
||||
], Spree::Order do |order|
|
||||
can_edit_as_producer(order, user)
|
||||
end
|
||||
can [:admin, :index, :create, :destroy, :update], Spree::LineItem do |item|
|
||||
can_edit_order(item.order, user)
|
||||
can_edit_as_producer(item.order, user)
|
||||
end
|
||||
can [:index, :create, :add, :read, :edit, :update], Spree::Shipment do |shipment|
|
||||
can_edit_order(shipment.order, user)
|
||||
can_edit_as_producer(shipment.order, user)
|
||||
end
|
||||
can [:admin, :index], OrderCycle do |order_cycle|
|
||||
can_edit_order(order_cycle.order, user)
|
||||
can_edit_as_producer(order_cycle.order, user)
|
||||
end
|
||||
can [:visible], Enterprise
|
||||
end
|
||||
|
||||
@@ -109,7 +109,7 @@ module Spree
|
||||
}
|
||||
|
||||
scope :editable_by_producers, ->(enterprises_ids) {
|
||||
joins(:variant, order: :distributor).where(
|
||||
joins(variant: :supplier, order: :distributor).where(
|
||||
distributor: { enable_producers_to_edit_orders: true },
|
||||
spree_variants: { supplier_id: enterprises_ids }
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ module Spree
|
||||
include SetUnusedAddressFields
|
||||
|
||||
searchable_attributes :number, :state, :shipment_state, :payment_state, :distributor_id,
|
||||
:order_cycle_id, :email, :total, :customer_id
|
||||
:order_cycle_id, :email, :total, :customer_id, :distributor_name_alias
|
||||
searchable_associations :shipping_method, :bill_address, :distributor
|
||||
searchable_scopes :complete, :incomplete, :sort_by_billing_address_name_asc,
|
||||
:sort_by_billing_address_name_desc
|
||||
@@ -181,6 +181,11 @@ module Spree
|
||||
scope :by_state, lambda { |state| where(state:) }
|
||||
scope :not_state, lambda { |state| where.not(state:) }
|
||||
|
||||
# This is used to filter line items by the distributor name on BOM page
|
||||
ransacker :distributor_name_alias do
|
||||
Arel.sql("distributor.name")
|
||||
end
|
||||
|
||||
def initialize(*_args)
|
||||
@checkout_processing = nil
|
||||
@manual_shipping_selection = nil
|
||||
|
||||
@@ -19,6 +19,7 @@ require 'open_food_network/property_merge'
|
||||
module Spree
|
||||
class Product < ApplicationRecord
|
||||
include LogDestroyPerformer
|
||||
include ProductSortByStocks
|
||||
|
||||
self.belongs_to_required_by_default = false
|
||||
# These columns have been moved to variant. Currently this is only for documentation purposes,
|
||||
@@ -30,7 +31,7 @@ module Spree
|
||||
|
||||
acts_as_paranoid
|
||||
|
||||
searchable_attributes :meta_keywords, :sku
|
||||
searchable_attributes :meta_keywords, :sku, :on_hand, :backorderable_priority
|
||||
searchable_associations :properties, :variants
|
||||
searchable_scopes :active, :with_properties
|
||||
|
||||
|
||||
@@ -194,7 +194,9 @@ module Spree
|
||||
inventory_units.group_by(&:variant).map do |variant, units|
|
||||
states = {}
|
||||
units.group_by(&:state).each { |state, iu| states[state] = iu.count }
|
||||
|
||||
scoper.scope(variant)
|
||||
|
||||
OpenStruct.new(variant:, quantity: units.length, states:)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -102,9 +102,8 @@ module Api
|
||||
end
|
||||
|
||||
def display_value_for_producer(order, value)
|
||||
filter_by_supplier =
|
||||
order.distributor&.enable_producers_to_edit_orders &&
|
||||
options[:current_user]&.can_manage_line_items_in_orders_only?
|
||||
@ability ||= Spree::Ability.new(options[:current_user])
|
||||
filter_by_supplier = @ability.can?(:edit_as_producer_only, order)
|
||||
return value unless filter_by_supplier
|
||||
|
||||
if order.distributor&.show_customer_names_to_suppliers
|
||||
|
||||
@@ -60,6 +60,7 @@ class CartService
|
||||
|
||||
def attempt_cart_add(variant, quantity, max_quantity = nil)
|
||||
scoper.scope(variant)
|
||||
|
||||
return unless valid_variant?(variant)
|
||||
|
||||
cart_add(variant, quantity, max_quantity)
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
module OrderCycles
|
||||
class DistributedProductsService # rubocop:disable Metrics/ClassLength
|
||||
def initialize(distributor, order_cycle, customer)
|
||||
def initialize(distributor, order_cycle, customer, **options)
|
||||
@distributor = distributor
|
||||
@order_cycle = order_cycle
|
||||
@customer = customer
|
||||
@options = options
|
||||
end
|
||||
|
||||
def products_relation
|
||||
@@ -26,13 +27,13 @@ module OrderCycles
|
||||
def variants_relation
|
||||
order_cycle.
|
||||
variants_distributed_by(distributor).
|
||||
merge(stocked_variants_and_overrides).
|
||||
merge(variants).
|
||||
select("DISTINCT spree_variants.*")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :distributor, :order_cycle, :customer
|
||||
attr_reader :distributor, :order_cycle, :customer, :options
|
||||
|
||||
def relation_by_sorting
|
||||
query = Spree::Product.where(id: stocked_products)
|
||||
@@ -112,10 +113,18 @@ module OrderCycles
|
||||
def stocked_products
|
||||
order_cycle.
|
||||
variants_distributed_by(distributor).
|
||||
merge(stocked_variants_and_overrides).
|
||||
merge(variants).
|
||||
select("DISTINCT spree_variants.product_id")
|
||||
end
|
||||
|
||||
def variants
|
||||
options[:inventory_enabled] ? stocked_variants_and_overrides : stocked_variants
|
||||
end
|
||||
|
||||
def stocked_variants
|
||||
Spree::Variant.joins(:stock_items).where(query_stock)
|
||||
end
|
||||
|
||||
def stocked_variants_and_overrides
|
||||
stocked_variants = Spree::Variant.
|
||||
joins("LEFT OUTER JOIN variant_overrides ON variant_overrides.variant_id = spree_variants.id
|
||||
@@ -126,6 +135,10 @@ module OrderCycles
|
||||
ProductTagRulesFilterer.new(distributor, customer, stocked_variants).call
|
||||
end
|
||||
|
||||
def query_stock
|
||||
"( #{variant_on_demand} OR #{variant_in_stock} )"
|
||||
end
|
||||
|
||||
def query_stock_with_overrides
|
||||
"( #{variant_not_overriden} AND ( #{variant_on_demand} OR #{variant_in_stock} ) )
|
||||
OR ( #{variant_overriden} AND ( #{override_on_demand} OR #{override_in_stock} ) )
|
||||
|
||||
@@ -52,6 +52,7 @@ module Orders
|
||||
next unless variant = Spree::Variant.find_by(id: li[:variant_id])
|
||||
|
||||
scoper.scope(variant)
|
||||
|
||||
li[:quantity] = stock_limited_quantity(variant.on_demand, variant.on_hand, li[:quantity])
|
||||
li[:price] = variant.price
|
||||
build_item_from(li)
|
||||
|
||||
@@ -21,16 +21,23 @@ module Permissions
|
||||
filtered_orders(orders)
|
||||
end
|
||||
|
||||
def managed_or_coordinated_orders_where_clause
|
||||
Spree::Order.where(
|
||||
managed_orders_where_values.or(coordinated_orders_where_values)
|
||||
)
|
||||
end
|
||||
|
||||
# Any orders that the user can edit
|
||||
def editable_orders
|
||||
orders = if @user.can_manage_line_items_in_orders_only?
|
||||
orders = if @user.admin?
|
||||
# It returns all orders if the user is an admin
|
||||
managed_or_coordinated_orders_where_clause
|
||||
else
|
||||
Spree::Order.joins(:distributor).where(
|
||||
id: produced_orders.select(:id),
|
||||
distributor: { enable_producers_to_edit_orders: true }
|
||||
)
|
||||
else
|
||||
Spree::Order.where(
|
||||
managed_orders_where_values.or(coordinated_orders_where_values)
|
||||
).or(
|
||||
managed_or_coordinated_orders_where_clause
|
||||
)
|
||||
end
|
||||
|
||||
@@ -43,12 +50,19 @@ module Permissions
|
||||
|
||||
# Any line items that I can edit
|
||||
def editable_line_items
|
||||
if @user.can_manage_line_items_in_orders_only?
|
||||
managed_or_coordinated_line_items_where_clause = Spree::LineItem.where(
|
||||
order_id: filtered_orders(managed_or_coordinated_orders_where_clause).select(:id)
|
||||
)
|
||||
|
||||
if @user.admin?
|
||||
# It returns all line_items if the user is an admin
|
||||
managed_or_coordinated_line_items_where_clause
|
||||
else
|
||||
Spree::LineItem.editable_by_producers(
|
||||
@permissions.managed_enterprises.select("enterprises.id")
|
||||
).or(
|
||||
managed_or_coordinated_line_items_where_clause
|
||||
)
|
||||
else
|
||||
Spree::LineItem.where(order_id: editable_orders.select(:id))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -8,11 +8,12 @@ class ProductsRenderer
|
||||
class NoProducts < RuntimeError; end
|
||||
DEFAULT_PER_PAGE = 10
|
||||
|
||||
def initialize(distributor, order_cycle, customer, args = {})
|
||||
def initialize(distributor, order_cycle, customer, args = {}, **options)
|
||||
@distributor = distributor
|
||||
@order_cycle = order_cycle
|
||||
@customer = customer
|
||||
@args = args
|
||||
@options = options
|
||||
end
|
||||
|
||||
def products_json
|
||||
@@ -28,7 +29,7 @@ class ProductsRenderer
|
||||
|
||||
private
|
||||
|
||||
attr_reader :order_cycle, :distributor, :customer, :args
|
||||
attr_reader :order_cycle, :distributor, :customer, :args, :options
|
||||
|
||||
def products
|
||||
return unless order_cycle
|
||||
@@ -40,8 +41,15 @@ class ProductsRenderer
|
||||
distributed_products.products_relation
|
||||
end
|
||||
results = filter(results)
|
||||
# Scope results with variant_overrides
|
||||
paginate(results).each { |product| product_scoper.scope(product) }
|
||||
|
||||
paginated_products = paginate(results)
|
||||
|
||||
if options[:inventory_enabled]
|
||||
# Scope results with variant_overrides
|
||||
paginated_products.each { |product| product_scoper.scope(product) }
|
||||
end
|
||||
|
||||
paginated_products
|
||||
end
|
||||
end
|
||||
|
||||
@@ -106,19 +114,22 @@ class ProductsRenderer
|
||||
end
|
||||
|
||||
def distributed_products
|
||||
OrderCycles::DistributedProductsService.new(distributor, order_cycle, customer)
|
||||
OrderCycles::DistributedProductsService.new(distributor, order_cycle, customer, **options)
|
||||
end
|
||||
|
||||
def variants_for_shop
|
||||
@variants_for_shop ||= begin
|
||||
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
|
||||
|
||||
# rubocop:disable Rails/FindEach # .each returns an array, .find_each returns nil
|
||||
distributed_products.variants_relation.
|
||||
variants = distributed_products.variants_relation.
|
||||
includes(:default_price, :product).
|
||||
where(product_id: products).
|
||||
each { |v| scoper.scope(v) } # Scope results with variant_overrides
|
||||
# rubocop:enable Rails/FindEach
|
||||
where(product_id: products)
|
||||
|
||||
if options[:inventory_enabled]
|
||||
# Scope results with variant_overrides
|
||||
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
|
||||
variants = variants.each { |v| scoper.scope(v) }
|
||||
end
|
||||
|
||||
variants
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
= enterprise_form.check_box :is_primary_producer
|
||||
= t('.producer')
|
||||
- if spree_current_user.admin?
|
||||
%td= enterprise_form.select :sells, Enterprise::SELLS, {}, class: 'select2 fullwidth'
|
||||
%td= enterprise_form.select :sells, enterprise_sells_options, {}, class: 'select2 fullwidth'
|
||||
%td= enterprise_form.check_box :visible, {}, 'public', 'hidden'
|
||||
- if spree_current_user.admin?
|
||||
%td= enterprise_form.select :owner_id, enterprise.users.map{ |e| [ e.email, e.id ] }, {}, class: "select2 fullwidth"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
- connected_apps_enabled.each do |type|
|
||||
- connected_apps_enabled(enterprise).each do |type|
|
||||
= render partial: "/admin/enterprises/form/connected_apps/#{type}",
|
||||
locals: { enterprise:, connected_app: enterprise.connected_apps.public_send(type).first }
|
||||
|
||||
= render partial: "/admin/enterprises/form/dfc_permissions" if dfc_platforms_available?
|
||||
|
||||
30
app/views/admin/enterprises/form/_dfc_permissions.html.haml
Normal file
30
app/views/admin/enterprises/form/_dfc_permissions.html.haml
Normal file
@@ -0,0 +1,30 @@
|
||||
%script{type: "module", src: "https://cdn.jsdelivr.net/npm/@startinblox/core@latest/dist/index.js"}
|
||||
%script{type: "module"}
|
||||
:plain
|
||||
window.orbit = {
|
||||
client: {
|
||||
name: "Orbit",
|
||||
logo: "https://cdn.startinblox.com/logos/ACME.svg",
|
||||
},
|
||||
components: [],
|
||||
getRoute: (route) => route,
|
||||
getDefaultRoute: () => "",
|
||||
getComponent: () => undefined,
|
||||
getComponentFromRoute: () => undefined,
|
||||
Swal: () => {},
|
||||
defaultRoute: "",
|
||||
federations: {},
|
||||
componentSet: new Set(["routing", "menu", "menu-top", "autoLogin", "solid-permissioning"]),
|
||||
};
|
||||
|
||||
:plain
|
||||
<solid-permissioning
|
||||
data-src="#{DfcProvider::Engine.routes.url_helpers.enterprise_platforms_url(@enterprise.id)}"
|
||||
scopes-uri="https://cdn.startinblox.com/owl/dfc/taxonomies/scopes.jsonld"
|
||||
noRouter
|
||||
auto-lang
|
||||
lang="en"
|
||||
auth-token="#{form_authenticity_token}">
|
||||
</solid-permissioning>
|
||||
|
||||
%script{type: "module", src: "https://cdn.jsdelivr.net/npm/@startinblox/solid-data-permissioning@latest/dist/index.js"}
|
||||
@@ -1,4 +1,6 @@
|
||||
.row{ "ng-controller": "TagRulesCtrl" }
|
||||
= render partial: "admin/json/injection_ams", locals: { ngModule: "admin.tagRules", name: "ruleTypes", json: @tag_rule_types.to_json }
|
||||
|
||||
.row{ "ng-app" => "admin.tagRules", "ng-controller": "TagRulesCtrl" }
|
||||
.eleven.columns.alpha.omega
|
||||
%ofn-sortable{ axis: "y", handle: ".header", items: '.customer_tag', position: "tagGroup.position", "after-sort": "updateRuleCounts()" }
|
||||
.no_tags{ "ng-show": "tagGroups.length == 0" }
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
%h3= t('.title')
|
||||
|
||||
= form_for [main_app, :admin, @order_cycle] do |f|
|
||||
.row
|
||||
.three.columns.alpha
|
||||
= f.label "enterprise_preferred_product_selection_from_coordinator_inventory_only", t('admin.order_cycles.edit.choose_products_from')
|
||||
.with-tip{ 'data-powertip' => t('.choose_product_tip', inventory: @order_cycle.coordinator.name) }
|
||||
%a= t('admin.whats_this')
|
||||
.four.columns
|
||||
= f.radio_button :preferred_product_selection_from_coordinator_inventory_only, true
|
||||
= f.label :preferred_product_selection_from_coordinator_inventory_only, t('.preferred_product_selection_from_coordinator_inventory_only_here')
|
||||
.four.columns.omega
|
||||
= f.radio_button :preferred_product_selection_from_coordinator_inventory_only, false
|
||||
= f.label :preferred_product_selection_from_coordinator_inventory_only, t('.preferred_product_selection_from_coordinator_inventory_only_all')
|
||||
- if feature?(:inventory, @order_cycle.coordinator)
|
||||
.row
|
||||
.three.columns.alpha
|
||||
= f.label "enterprise_preferred_product_selection_from_coordinator_inventory_only", t('admin.order_cycles.edit.choose_products_from')
|
||||
.with-tip{ 'data-powertip' => t('.choose_product_tip', inventory: @order_cycle.coordinator.name) }
|
||||
%a= t('admin.whats_this')
|
||||
.four.columns
|
||||
= f.radio_button :preferred_product_selection_from_coordinator_inventory_only, true
|
||||
= f.label :preferred_product_selection_from_coordinator_inventory_only, t('.preferred_product_selection_from_coordinator_inventory_only_here')
|
||||
.four.columns.omega
|
||||
= f.radio_button :preferred_product_selection_from_coordinator_inventory_only, false
|
||||
= f.label :preferred_product_selection_from_coordinator_inventory_only, t('.preferred_product_selection_from_coordinator_inventory_only_all')
|
||||
|
||||
.row
|
||||
.alpha.three.columns
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
%td.receival-details
|
||||
= text_field_tag 'order_cycle_incoming_exchange_{{ $index }}_receival_instructions', '', 'id' => 'order_cycle_incoming_exchange_{{ $index }}_receival_instructions', 'placeholder' => t('.receival_instructions_placeholder'), 'ng-model' => 'exchange.receival_instructions'
|
||||
- if type == 'distributor'
|
||||
%td.tags.panel-toggle.text-center{ name: "tags", "ng-if": 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' }
|
||||
%td.tags.panel-toggle.text-center{ name: "tags", id: "tags", "ng-if": 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' }
|
||||
{{ exchange.tags.length }}
|
||||
%td.collection-details
|
||||
= text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'ng-init' => 'setPickupTimeFieldDirty($index, exchange.pickup_time)', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', 'required' => 'required', 'placeholder' => t('.pickup_time_placeholder'), 'ng-model' => 'exchange.pickup_time', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator', 'maxlength' => 35
|
||||
@@ -36,5 +36,5 @@
|
||||
- if type == 'distributor'
|
||||
%tr.panel-row{ object: "exchange",
|
||||
panels: "{products: 'exchange_products_distributed', tags: 'exchange_tags'}",
|
||||
locals: "$index,exchangeTotalVariants,order_cycle,exchange,enterprises,setExchangeVariants,incomingExchangeVariantsFor,variantSuppliedToOrderCycle,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
|
||||
locals: "$index,exchangeTotalVariants,order_cycle,exchange,enterprises,setExchangeVariants,incomingExchangeVariantsFor,variantSuppliedToOrderCycle,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading,order_cycle_form",
|
||||
colspan: 5 }
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
%h6= t('admin.product_import.index.choose_import_type')
|
||||
%br
|
||||
- options = { "#{t('admin.product_import.index.product_list')}" => :product_list }
|
||||
- options = options.merge("#{t('admin.product_import.index.inventories')}" => :inventories) if feature?(:inventory, spree_current_user.enterprises)
|
||||
= select_tag "settings[import_into]",
|
||||
options_for_select({"#{t('admin.product_import.index.product_list')}" => :product_list, "#{t('admin.product_import.index.inventories')}" => :inventories}),
|
||||
options_for_select(options),
|
||||
{ "data-controller": "tom-select", class: "primary inline no-search", "ng-model": "settings.import_into" }
|
||||
%br
|
||||
%br
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
%i.icon-external-link
|
||||
= t('admin.product_import.index.product_list_template')
|
||||
|
||||
%a.download{href: '/inventory_template.csv'}
|
||||
%i.icon-external-link
|
||||
= t('admin.product_import.index.inventory_template')
|
||||
- if feature?(:inventory, spree_current_user.enterprises)
|
||||
%a.download{href: '/inventory_template.csv'}
|
||||
%i.icon-external-link
|
||||
= t('admin.product_import.index.inventory_template')
|
||||
|
||||
%h5= t('admin.product_import.index.category_values')
|
||||
|
||||
@@ -27,4 +28,4 @@
|
||||
%strong= t('admin.product_import.index.shipping_categories')
|
||||
|
||||
- @shipping_categories.each do |sc|
|
||||
%span.category= sc
|
||||
%span.category= sc
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
%td.col-inherits_properties.align-left
|
||||
.content= product.inherits_properties ? 'YES' : 'NO' #TODO: consider using https://github.com/RST-J/human_attribute_values, else use I18n.t (also below)
|
||||
%td.align-right
|
||||
= render(VerticalEllipsisMenu::Component.new) do
|
||||
= render(VerticalEllipsisMenuComponent.new) do
|
||||
= link_to t('admin.products_page.actions.edit'), edit_admin_product_path(product), 'data-turbo': false
|
||||
= link_to t('admin.products_page.actions.clone'), admin_clone_product_path(product), 'data-turbo-method': :post
|
||||
%a{ "data-controller": "modal-link", "data-action": "click->modal-link#setModalDataSetOnConfirm click->modal-link#open",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
= form_with url: bulk_update_admin_products_path, method: :post, id: "products-form",
|
||||
= form_with url: admin_products_bulk_update_path, method: :post, id: "products-form",
|
||||
builder: BulkFormBuilder,
|
||||
html: { data: { 'turbo-frame': "_self",
|
||||
controller: "bulk-form",
|
||||
@@ -59,7 +59,8 @@
|
||||
%th.align-left.col-unit_scale.with-input= t('admin.products_page.columns.unit_scale')
|
||||
%th.align-left.col-unit.with-input= t('admin.products_page.columns.unit')
|
||||
%th.align-left.col-price.with-input= t('admin.products_page.columns.price')
|
||||
%th.align-left.col-on_hand.with-input= t('admin.products_page.columns.on_hand')
|
||||
= render partial: 'spree/admin/shared/stimulus_sortable_header',
|
||||
locals: { column: :on_hand, sorted: params.dig(:q, :s), default: 'name asc' }
|
||||
%th.align-left.col-producer= t('admin.products_page.columns.producer')
|
||||
%th.align-left.col-category= t('admin.products_page.columns.category')
|
||||
%th.align-left.col-tax_category= t('admin.products_page.columns.tax_category')
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
%td.col-inherits_properties.align-left
|
||||
-# empty
|
||||
%td.align-right
|
||||
= render(VerticalEllipsisMenu::Component.new) do
|
||||
= render(VerticalEllipsisMenuComponent.new) do
|
||||
- if variant.persisted?
|
||||
= link_to t('admin.products_page.actions.edit'), edit_admin_product_variant_path(variant.product, variant)
|
||||
- if variant.product.variants.size > 1
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
.pagination{ "data-controller": "search" }
|
||||
- if pagy.prev
|
||||
%button.page.prev{ data: { action: 'click->search#changePage', page: pagy.prev } }
|
||||
- if feature?(:admin_style_v3, spree_current_user)
|
||||
%i.icon-chevron-left{ data: { action: 'click->search#changePage', page: pagy.prev } }
|
||||
- else
|
||||
!= pagy_t('pagy.prev')
|
||||
%i.icon-chevron-left{ data: { action: 'click->search#changePage', page: pagy.prev } }
|
||||
- else
|
||||
%button.page.disabled{disabled: "disabled"}!= pagy_t('pagy.prev')
|
||||
|
||||
@@ -22,9 +19,6 @@
|
||||
|
||||
- if pagy.next
|
||||
%button.page.next{ data: { action: 'click->search#changePage', page: pagy.next } }
|
||||
- if feature?(:admin_style_v3, spree_current_user)
|
||||
%i.icon-chevron-right{ data: { action: 'click->search#changePage', page: pagy.next } }
|
||||
- else
|
||||
!= pagy_t('pagy.next')
|
||||
%i.icon-chevron-right{ data: { action: 'click->search#changePage', page: pagy.next } }
|
||||
- else
|
||||
%button.page.disabled.pagination-next{disabled: "disabled"}!= pagy_t('pagy.next')
|
||||
|
||||
28
app/views/checkout/_delivery_details.html.haml
Normal file
28
app/views/checkout/_delivery_details.html.haml
Normal file
@@ -0,0 +1,28 @@
|
||||
%div
|
||||
.summary-subtitle
|
||||
= t("checkout.step3.delivery_details.address")
|
||||
%span
|
||||
= @order.ship_address.firstname
|
||||
= @order.ship_address.lastname
|
||||
%div
|
||||
= @order.ship_address.phone
|
||||
%div
|
||||
= @order.user.email if @order.user
|
||||
%br
|
||||
%div
|
||||
= @order.ship_address.address1
|
||||
- unless @order.ship_address.address2.blank?
|
||||
%div
|
||||
= @order.ship_address.address2
|
||||
%div
|
||||
= @order.ship_address.city
|
||||
%div
|
||||
= @order.ship_address.state
|
||||
%div
|
||||
= @order.ship_address.zipcode
|
||||
%div
|
||||
= @order.ship_address.country
|
||||
- if @order.special_instructions.present?
|
||||
%br
|
||||
%em
|
||||
= @order.special_instructions
|
||||
@@ -91,7 +91,7 @@
|
||||
"data-toggle-show": shipping_method.require_ship_address
|
||||
= shipping_method_form.label shipping_method.id, shipping_method.name, {for: "shipping_method_" + shipping_method.id.to_s }
|
||||
%em.fees= payment_or_shipping_price(shipping_method, @order)
|
||||
- display_ship_address = display_ship_address || (ship_method_is_selected && shipping_method.require_ship_address)
|
||||
- display_ship_address ||= ship_method_is_selected && shipping_method.require_ship_address
|
||||
.checkout-input{"data-shippingmethod-target": "shippingMethodDescription", "data-shippingmethodid": shipping_method.id , style: "display: #{ship_method_is_selected ? 'block' : 'none'}" }
|
||||
#distributor_address.panel
|
||||
- if shipping_method.description.present?
|
||||
|
||||
@@ -11,34 +11,7 @@
|
||||
= @order.shipping_method.name
|
||||
%em.fees= payment_or_shipping_price(@order.shipping_method, @order)
|
||||
.two-columns
|
||||
%div
|
||||
.summary-subtitle
|
||||
= t("checkout.step3.delivery_details.address")
|
||||
%span
|
||||
= @order.ship_address.firstname
|
||||
= @order.ship_address.lastname
|
||||
%div
|
||||
= @order.ship_address.phone
|
||||
%div
|
||||
= @order.user.email if @order.user
|
||||
%br
|
||||
%div
|
||||
= @order.ship_address.address1
|
||||
- unless @order.ship_address.address2.blank?
|
||||
%div
|
||||
= @order.ship_address.address2
|
||||
%div
|
||||
= @order.ship_address.city
|
||||
%div
|
||||
= @order.ship_address.state
|
||||
%div
|
||||
= @order.ship_address.zipcode
|
||||
%div
|
||||
= @order.ship_address.country
|
||||
- if @order.special_instructions.present?
|
||||
%br
|
||||
%em
|
||||
= @order.special_instructions
|
||||
= render "delivery_details" if @order.shipping_method.delivery?
|
||||
- if @order.shipping_method.description.present?
|
||||
%div
|
||||
.summary-subtitle
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
%form{ name: 'about', novalidate: true, "ng-controller": "RegistrationFormCtrl", "ng-submit": "selectIfValid('images', about)" }
|
||||
.row
|
||||
.small-12.columns
|
||||
.alert-box.info{ "ofn-inline-alert": true, "ng-show": "visible" }
|
||||
.alert-box.info{ "data-controller": "toggle-control", "data-toggle-control-target": "content", style: "display: block;" }
|
||||
%h6{ "ng-bind" => "'registration.steps.about.success' | t:{enterprise: enterprise.name}" }
|
||||
%span= t(".registration_exit_message")
|
||||
%a.close{ "ng-click": "close()" } ×
|
||||
%a.close{ "data-action": "toggle-control#toggleDisplay" } ×
|
||||
|
||||
.small-12.large-8.columns
|
||||
.row
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.text-center.page-alert.fixed{ "ofn-page-alert" => true }
|
||||
.text-center.page-alert.fixed{ "data-controller" => "page-alert" }
|
||||
.alert-box
|
||||
= render 'shared/page_alert'
|
||||
%a.close{ "ng-click": "close()" } ×
|
||||
%a.close{ "data-action" => "page-alert#close" } ×
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.closed-shop-header
|
||||
.row
|
||||
.small-12.columns
|
||||
.content{ "darker-background" => true }
|
||||
.content
|
||||
%h4
|
||||
.warning-sign
|
||||
.rectangle
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.content{ "darker-background" => true }
|
||||
.content
|
||||
.row.footer-pad
|
||||
.small-12.columns{ "data-controller": "login-modal" }
|
||||
%strong
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.content.footer-pad{ "darker-background" => true, "ng-show" => "order_cycle.order_cycle_id == null" }
|
||||
.content.footer-pad{ "ng-show" => "order_cycle.order_cycle_id == null" }
|
||||
.row
|
||||
.small-12.columns
|
||||
.select-oc-message
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
.columns.large-4.show-for-large-up
|
||||
= render partial: "shopping_shared/order_cycles"
|
||||
- shop_tabs.each do |tab|
|
||||
%div{id: "#{tab[:name]}_panel", "data-tabs-and-panels-target": "panel #{'default' if tab[:default]} #{'shop' if tab[:shop]}" }
|
||||
.page-view
|
||||
%div{id: "#{tab[:name]}_panel", "data-tabs-and-panels-target": "panel #{'default' if tab[:default]} #{'shop' if tab[:shop]}" }
|
||||
.page-view{ class: shop_tab_class(tab[:name]) }
|
||||
- if tab[:custom]
|
||||
= render "shopping_shared/tabs/custom"
|
||||
- else
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
- if @order.shipments.any?
|
||||
= render :partial => "spree/admin/orders/shipment", :collection => @order.shipments, :locals => { :order => @order }
|
||||
|
||||
- if spree_current_user.can_manage_orders?
|
||||
- if can?(:manage_order_sections, @order)
|
||||
- if @order.line_items.exists?
|
||||
= render partial: "spree/admin/orders/note", locals: { order: @order }
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@
|
||||
- if local_assigns[:success]
|
||||
%i.success.icon-ok-sign{"data-controller": "ephemeral"}
|
||||
= render AdminTooltipComponent.new(text: t('spree.admin.orders.index.edit'), link_text: "", link: edit_admin_order_path(order), link_class: "icon_link with-tip icon-edit no-text")
|
||||
- if spree_current_user.can_manage_orders? && order.ready_to_ship?
|
||||
- if can?(:manage_order_sections, order) && order.ready_to_ship?
|
||||
%form
|
||||
= render ShipOrderComponent.new(order: order)
|
||||
= render partial: 'admin/shared/tooltip_button', locals: {button_class: "icon-road icon_link with-tip no-text", reflex_data_id: order.id.to_s, tooltip_text: t('spree.admin.orders.index.ship'), shipment: true}
|
||||
|
||||
- if can?(:update, Spree::Payment) && order.payment_required? && order.pending_payments.reject(&:requires_authorization?).any?
|
||||
- if can?(:manage_order_sections, order) && can?(:update, Spree::Payment) && order.payment_required? && order.pending_payments.reject(&:requires_authorization?).any?
|
||||
= render partial: 'admin/shared/tooltip_button', locals: {button_class: "icon-capture icon_link no-text", button_reflex: "click->Admin::OrdersReflex#capture", reflex_data_id: order.id.to_s, tooltip_text: t('spree.admin.orders.index.capture')}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
- content_for :page_actions do
|
||||
- if can?(:fire, @order)
|
||||
%li= event_links(@order)
|
||||
- if spree_current_user.can_manage_orders?
|
||||
- if can?(:manage_order_sections, @order)
|
||||
= render partial: 'spree/admin/shared/order_links'
|
||||
- if can?(:admin, Spree::Order)
|
||||
%li
|
||||
@@ -14,7 +14,7 @@
|
||||
= t(:back_to_orders_list)
|
||||
|
||||
= render partial: "spree/admin/shared/order_page_title"
|
||||
- if spree_current_user.can_manage_orders?
|
||||
- if can?(:manage_order_sections, @order)
|
||||
= render partial: "spree/admin/shared/order_tabs", locals: { current: 'Order Details' }
|
||||
|
||||
%div
|
||||
@@ -22,7 +22,13 @@
|
||||
|
||||
= admin_inject_shops(@shops, module: 'admin.orders')
|
||||
= admin_inject_order_cycles(@order_cycles)
|
||||
%div{"ng-controller" => "orderCtrl", "ofn-distributor-id" => @order.distributor_id, "ofn-order-cycle-id" => @order.order_cycle_id}
|
||||
%div{
|
||||
"ng-controller" => "orderCtrl",
|
||||
"ofn-distributor-id" => @order.distributor_id,
|
||||
"ofn-order-cycle-id" => @order.order_cycle_id,
|
||||
"ofn-search-variants-as" => (can?(:manage_order_sections, @order) ? 'hub' : 'supplier'),
|
||||
"ofn-order-id" => @order.id,
|
||||
}
|
||||
|
||||
= render :partial => 'add_product' if can?(:update, @order)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
= admin_inject_available_units
|
||||
|
||||
- content_for :page_actions do
|
||||
%li= button_link_to t('admin.products.back_to_products_list'), products_return_to_url(@url_filters), :icon => 'icon-arrow-left'
|
||||
%li= button_link_to t('admin.products.back_to_products_list'), products_return_to_url, :icon => 'icon-arrow-left'
|
||||
%li#new_product_link
|
||||
= button_link_to t(:new_product), new_object_url, { :icon => 'icon-plus', :id => 'admin_new_product' }
|
||||
|
||||
@@ -18,6 +18,6 @@
|
||||
|
||||
= button t(:update), 'icon-refresh'
|
||||
|
||||
= button_link_to t(:cancel), products_return_to_url(@url_filters), icon: 'icon-remove'
|
||||
= button_link_to t(:cancel), products_return_to_url, icon: 'icon-remove'
|
||||
|
||||
#product-preview-modal-container
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
= render 'spree/admin/products/index/header'
|
||||
= render 'spree/admin/products/index/data'
|
||||
= admin_inject_available_units
|
||||
|
||||
%div{ "ng-app": 'ofn.admin', "ng-controller": 'AdminProductEditCtrl', "ng-init": 'initialise()' }
|
||||
|
||||
= render 'spree/admin/products/index/filters'
|
||||
%div{ 'ng-cloak' => true }
|
||||
= render 'spree/admin/products/index/actions'
|
||||
= render 'spree/admin/products/index/indicators'
|
||||
= render 'spree/admin/products/index/products'
|
||||
|
||||
%div{'ng-show' => "!RequestMonitor.loading && products.length > 0" }
|
||||
= render partial: 'admin/shared/angular_pagination'
|
||||
@@ -1,11 +0,0 @@
|
||||
.controls.sixteen.columns.alpha{ 'ng-hide' => 'RequestMonitor.loading || products.length == 0' }
|
||||
.ten.columns.alpha.index-controls
|
||||
.per-page{'ng-show' => '!RequestMonitor.loading && products.length > 0'}
|
||||
%input.per-page-select.ofn-select2{type: 'number', data: 'per_page_options', 'min-search' => 999, 'ng-model' => 'per_page', 'ng-change' => 'fetchProducts()'}
|
||||
|
||||
%span.per-page-feedback
|
||||
{{ 'spree.admin.orders.index.results_found' | t:{number: pagination.results} }}
|
||||
{{ 'spree.admin.orders.index.viewing' | t:{start: ((pagination.page -1) * pagination.per_page) +1, end: ((pagination.page -1) * pagination.per_page) + products.length} }}
|
||||
|
||||
.six.columns.omega
|
||||
%columns-dropdown{ action: "#{controller_name}_#{action_name}" }
|
||||
@@ -1,5 +0,0 @@
|
||||
= admin_inject_producers(@producers)
|
||||
= admin_inject_taxons(@taxons)
|
||||
= admin_inject_tax_categories(@tax_categories)
|
||||
= admin_inject_spree_api_key(@spree_api_key)
|
||||
= admin_inject_column_preferences
|
||||
@@ -1,33 +0,0 @@
|
||||
%fieldset
|
||||
%legend{align: 'center'}= t(:search)
|
||||
|
||||
.filters.sixteen.columns.alpha.omega
|
||||
.quick_search.three.columns.alpha
|
||||
%label{ for: 'quick_filter' }
|
||||
%br
|
||||
%input.quick-search.fullwidth{ name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress": "$event.keyCode === 13 && fetchProducts()", "ng-model": 'q.query' }
|
||||
.one.columns
|
||||
.filter_select.three.columns
|
||||
%label{ for: 'producer_filter' }= t 'producer'
|
||||
%br
|
||||
%select.fullwidth{ id: 'producer_filter', "ofn-select2-min-search": 5, "ng-model": 'q.producerFilter', "ng-options": 'producer.id as producer.name for producer in producers' }
|
||||
.filter_select.three.columns
|
||||
%label{ for: 'category_filter' }= t 'category'
|
||||
%br
|
||||
%select.fullwidth{ id: 'category_filter', "ofn-select2-min-search": 5, "ng-model": 'q.categoryFilter', "ng-options": 'taxon.id as taxon.name for taxon in taxons' }
|
||||
.filter_select.three.columns
|
||||
%label{ for: 'import_filter' }= t 'import_date'
|
||||
%br
|
||||
%select.fullwidth{ id: 'import_date_filter', "ofn-select2-min-search": 5, "ng-model": 'q.importDateFilter', "ng-init": "importDates = #{@import_dates}; showLatestImport = #{@show_latest_import}", "ng-options": 'date.id as date.name for date in importDates' }
|
||||
|
||||
.filter_clear.three.columns.omega
|
||||
%label{ for: 'clear_all_filters' }
|
||||
%br
|
||||
%input.fullwidth.red{ :type => 'button', :id => 'clear_all_filters', :value => t('admin.clear_filters'), 'ng-click' => "resetSelectFilters()" }
|
||||
|
||||
.clearfix
|
||||
|
||||
.actions.filter-actions
|
||||
%div
|
||||
%a.button.icon-search{'ng-click' => 'fetchProducts()'}
|
||||
= t(:filter_results)
|
||||
@@ -1,10 +0,0 @@
|
||||
- content_for :page_title do
|
||||
= t('.title')
|
||||
|
||||
- content_for :page_actions do
|
||||
%div{ :class => "toolbar" }
|
||||
%ul{ :class => "actions header-action-links inline-menu" }
|
||||
%li#new_product_link
|
||||
= button_link_to t(:new_product), new_object_url, { :icon => 'icon-plus', :id => 'admin_new_product' }
|
||||
|
||||
= render partial: 'spree/admin/shared/product_sub_menu'
|
||||
@@ -1,14 +0,0 @@
|
||||
.sixteen.columns.alpha#loading{ 'ng-if' => 'RequestMonitor.loading' }
|
||||
= render partial: "components/admin_spinner"
|
||||
%h1
|
||||
= t('.title')
|
||||
|
||||
.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length == 0' }
|
||||
%h1#no_results= t('.no_products')
|
||||
|
||||
.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length != 0' }
|
||||
%h1#no_results
|
||||
= t('.no_results')
|
||||
'
|
||||
{{q.query}}
|
||||
'
|
||||
@@ -1,14 +0,0 @@
|
||||
.sixteen.columns.alpha{ 'ng-hide' => 'RequestMonitor.loading || products.length == 0' }
|
||||
%form{ name: 'bulk_product_form', autocomplete: "off" }
|
||||
%save-bar{ dirty: "bulk_product_form.$dirty", persist: "false" }
|
||||
%input.red{ type: "button", value: t(:save_changes), "ng-click": "submitProducts()", "ng-disabled": "!bulk_product_form.$dirty" }
|
||||
%input{ type: "button", value: t(:close), 'ng-click' => "cancel('#{admin_products_path}')" }
|
||||
|
||||
%table.index#listing_products.bulk
|
||||
|
||||
= render 'spree/admin/products/index/products_head'
|
||||
|
||||
%tbody{ 'ng-repeat' => 'product in products', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" }
|
||||
|
||||
= render 'spree/admin/products/index/products_product'
|
||||
= render 'spree/admin/products/index/products_variant'
|
||||
@@ -1,41 +0,0 @@
|
||||
%colgroup
|
||||
%col.actions
|
||||
%col.image{ "ng-show": 'columns.image.visible' }
|
||||
%col.producer{ "ng-show": 'columns.producer.visible' }
|
||||
%col.sku{ "ng-show": 'columns.sku.visible' }
|
||||
%col.name{ "ng-show": 'columns.name.visible' }
|
||||
%col.unit{ "ng-show": 'columns.unit.visible' }
|
||||
%col.display_as{ "ng-show": 'columns.unit.visible' }
|
||||
%col.price{ "ng-show": 'columns.price.visible' }
|
||||
%col.on_hand{ "ng-show": 'columns.on_hand.visible' }
|
||||
%col.on_demand{ "ng-show": 'columns.on_demand.visible' }
|
||||
%col.category{ "ng-show": 'columns.category.visible' }
|
||||
%col.tax_category{ "ng-show": 'columns.tax_category.visible' }
|
||||
%col.inherits_properties{ "ng-show": 'columns.inherits_properties.visible' }
|
||||
%col.import_date{ "ng-show": 'columns.import_date.visible' }
|
||||
%col.actions
|
||||
%col.actions
|
||||
%col.actions
|
||||
|
||||
%thead
|
||||
%tr{ "ng-controller": "ColumnsCtrl" }
|
||||
%th.left-actions
|
||||
%a{ 'ng-click' => 'toggleShowAllVariants()', :style => 'color: red; cursor: pointer' }
|
||||
= t(:expand_all)
|
||||
%th.image{ 'ng-show' => 'columns.image.visible' }
|
||||
%th.producer{ 'ng-show' => 'columns.producer.visible' }=t('admin.producer')
|
||||
%th.sku{ 'ng-show' => 'columns.sku.visible' }=t('admin.sku')
|
||||
%th.name{ 'ng-show' => 'columns.name.visible' }
|
||||
= render partial: 'spree/admin/shared/sortable_header', locals: {column_name: 'name'}
|
||||
%th.unit{ 'ng-show' => 'columns.unit.visible' }=t('.unit')
|
||||
%th.display_as{ 'ng-show' => 'columns.unit.visible' }=t('.display_as')
|
||||
%th.price{ 'ng-show' => 'columns.price.visible' }=t('admin.price')
|
||||
%th.on_hand{ 'ng-show' => 'columns.on_hand.visible' }=t('admin.on_hand')
|
||||
%th.on_demand{ 'ng-show' => 'columns.on_demand.visible' }=t('admin.on_demand?')
|
||||
%th.category{ 'ng-show' => 'columns.category.visible' }=t('.category')
|
||||
%th.tax_category{ 'ng-show' => 'columns.tax_category.visible' }=t('.tax_category')
|
||||
%th.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' }=t('.inherits_properties?')
|
||||
%th.import_date{ 'ng-show' => 'columns.import_date.visible' }=t('.import_date')
|
||||
%th.actions
|
||||
%th.actions
|
||||
%th.actions
|
||||
@@ -1,33 +0,0 @@
|
||||
%tr.product{ :id => "p_{{product.id}}" }
|
||||
%td.left-actions
|
||||
%a{ 'ofn-toggle-variants' => 'true', :class => "view-variants", 'ng-show' => 'hasVariants(product)' }
|
||||
%a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "!hasVariants(product) && hasUnit(product)" }
|
||||
%td.image{ 'ng-show' => 'columns.image.visible' }
|
||||
%a{class: 'image-modal'}
|
||||
%img{'ng-src' => '{{ product.thumb_url }}'}
|
||||
%td.producer{ 'ng-show' => 'columns.producer.visible' }
|
||||
%td.sku{ 'ng-show' => 'columns.sku.visible' }
|
||||
%input{ 'ng-model' => "product.sku", :name => 'product_sku', 'ofn-track-product' => 'sku', :type => 'text' }
|
||||
%td.name{ 'ng-show' => 'columns.name.visible' }
|
||||
%input{ 'ng-model' => "product.name", :name => 'product_name', 'ofn-track-product' => 'name', :type => 'text' }
|
||||
%td.unit{ 'ng-show' => 'columns.unit.visible' }
|
||||
%td.display_as{ 'ng-show' => 'columns.unit.visible' }
|
||||
%td.price{ 'ng-show' => 'columns.price.visible' }
|
||||
%input{ 'ng-model' => 'product.price', 'ofn-decimal' => :true, :name => 'price', 'ofn-track-product' => 'price', :type => 'text', 'ng-hide' => 'hasVariants(product)' }
|
||||
%td.on_hand{ 'ng-show' => 'columns.on_hand.visible' }
|
||||
%span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-if' => '!hasOnDemandVariants(product) && (hasVariants(product) || product.on_demand)' }
|
||||
%input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ofn-track-product' => 'on_hand', 'ng-if' => '!(hasVariants(product) || product.on_demand)', :type => 'number' }
|
||||
%td.on_demand{ 'ng-show' => 'columns.on_demand.visible' }
|
||||
%input.field{ 'ng-model' => 'product.on_demand', :name => 'on_demand', 'ofn-track-product' => 'on_demand', :type => 'checkbox', 'ng-hide' => 'hasVariants(product)' }
|
||||
%td.category{ 'ng-if' => 'columns.category.visible' }
|
||||
%td.tax_category{ 'ng-if' => 'columns.tax_category.visible' }
|
||||
%td.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' }
|
||||
%input{ 'ng-model' => 'product.inherits_properties', :name => 'inherits_properties', 'ofn-track-product' => 'inherits_properties', type: "checkbox" }
|
||||
%td.import_date{ 'ng-show' => 'columns.import_date.visible' }
|
||||
%span {{(product.import_date | date:"MMMM dd, yyyy HH:mm") || ""}}
|
||||
%td.actions
|
||||
%a{ 'ng-click' => 'editWarn(product)', :class => "edit-product icon-edit no-text", 'ofn-with-tip' => t(:edit) }
|
||||
%td.actions
|
||||
%a{ 'ng-click' => 'cloneProduct(product)', :class => "clone-product icon-copy no-text", 'ofn-with-tip' => t(:clone) }
|
||||
%td.actions
|
||||
%a{ 'ng-click' => 'deleteProduct(product)', :class => "delete-product icon-trash no-text", 'ofn-with-tip' => t(:remove) }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user