mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-21 20:06:54 +00:00
Compare commits
369 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12fde5b9fc | ||
|
|
0da6275d41 | ||
|
|
52d2d1d23c | ||
|
|
4dfc020eaa | ||
|
|
8ef52f97e5 | ||
|
|
68a7f547b1 | ||
|
|
7a0f2d47a3 | ||
|
|
4affd01b7b | ||
|
|
0432c73f19 | ||
|
|
103902c006 | ||
|
|
d2933e35f1 | ||
|
|
0b5e341ae7 | ||
|
|
7d36c3b5aa | ||
|
|
d511763733 | ||
|
|
a059c11d0f | ||
|
|
cecc19ae1d | ||
|
|
d4311a848e | ||
|
|
d64573f7fd | ||
|
|
a9fe6ec1b5 | ||
|
|
f14bbc5ed9 | ||
|
|
5ba8efec2c | ||
|
|
3bf38b7c08 | ||
|
|
a591e0736f | ||
|
|
3080eb9dfd | ||
|
|
ed98a16eec | ||
|
|
7a924bd9ca | ||
|
|
7639e19184 | ||
|
|
71aff7e1d2 | ||
|
|
2506667bca | ||
|
|
f87a553230 | ||
|
|
3171b60d6f | ||
|
|
516398fbd6 | ||
|
|
4a1b74c136 | ||
|
|
b9edea7c0e | ||
|
|
b9053f9fd2 | ||
|
|
f297cff8c7 | ||
|
|
7563d38b4b | ||
|
|
83523a676f | ||
|
|
abcfb5ce8d | ||
|
|
8ee6d1c320 | ||
|
|
1bc19ad6a4 | ||
|
|
191b8064ed | ||
|
|
e28274db14 | ||
|
|
02dec1d6cd | ||
|
|
df04c837a5 | ||
|
|
7a6c085b63 | ||
|
|
a4317b70f4 | ||
|
|
cb90fb052d | ||
|
|
18e5f9ba64 | ||
|
|
e9a5b889de | ||
|
|
cc57b0c200 | ||
|
|
a8040e986d | ||
|
|
2269f824c0 | ||
|
|
c45a3c2303 | ||
|
|
1327d80446 | ||
|
|
43a3ac0a7b | ||
|
|
b948312de7 | ||
|
|
7063de4734 | ||
|
|
56c1a9cca2 | ||
|
|
9555bfcc93 | ||
|
|
e455a47135 | ||
|
|
e15e71a3a7 | ||
|
|
91d959b7d6 | ||
|
|
108f57a705 | ||
|
|
af42159e09 | ||
|
|
8e55c39ca0 | ||
|
|
6cfb060184 | ||
|
|
0cfb7269c8 | ||
|
|
cbec495620 | ||
|
|
55eea21bb0 | ||
|
|
f63c7cf54f | ||
|
|
13633e8bea | ||
|
|
093edb66d3 | ||
|
|
fb25ddd219 | ||
|
|
d54850f097 | ||
|
|
f8451a2511 | ||
|
|
1a88549954 | ||
|
|
f79182253a | ||
|
|
8cfd7c610b | ||
|
|
486b5e9edc | ||
|
|
8fe3abfd45 | ||
|
|
0e7dafea46 | ||
|
|
4c9cc7460a | ||
|
|
c9e3f58aed | ||
|
|
4136306abf | ||
|
|
9f1eaf0b66 | ||
|
|
4771612adb | ||
|
|
7ce3dfe365 | ||
|
|
6f13707b9d | ||
|
|
64cb104434 | ||
|
|
1606f9900f | ||
|
|
9f0e8b0b2e | ||
|
|
d796e96470 | ||
|
|
ba7f78ef60 | ||
|
|
988e146240 | ||
|
|
e6d9ec7bd7 | ||
|
|
3acc53a389 | ||
|
|
9c9fc999de | ||
|
|
d824c84ce6 | ||
|
|
1a301f3dbb | ||
|
|
fad4f3b22a | ||
|
|
6080c99850 | ||
|
|
2944acff8a | ||
|
|
2068a59b72 | ||
|
|
3063439ed1 | ||
|
|
6bed05c721 | ||
|
|
a3ee6674ea | ||
|
|
e59077e63e | ||
|
|
f9f8d85841 | ||
|
|
0042690e18 | ||
|
|
5f1111b52b | ||
|
|
25ded0d23c | ||
|
|
a5458150ca | ||
|
|
96eebbabf3 | ||
|
|
c58e6fa964 | ||
|
|
fbe8f5195c | ||
|
|
f587bbb7d5 | ||
|
|
2793693a7c | ||
|
|
07e2317369 | ||
|
|
042162eda8 | ||
|
|
795f13d73a | ||
|
|
f1814f1b67 | ||
|
|
4ab7b78cb8 | ||
|
|
11631c3a33 | ||
|
|
0212381362 | ||
|
|
e6ca6bacac | ||
|
|
59df45b8cf | ||
|
|
5892e85869 | ||
|
|
c59326743b | ||
|
|
cb3397fd1a | ||
|
|
2bd4de3e29 | ||
|
|
b2c5be775e | ||
|
|
d22212ccfa | ||
|
|
dd600cd163 | ||
|
|
f189ca8004 | ||
|
|
db7146014c | ||
|
|
c6af55d9ae | ||
|
|
0f588dbe0b | ||
|
|
9c14d8ff36 | ||
|
|
a104bf8efd | ||
|
|
a039ef13f4 | ||
|
|
7ad8951375 | ||
|
|
99e59595b4 | ||
|
|
3264355f12 | ||
|
|
7e3b6e2b5d | ||
|
|
a9598c5d97 | ||
|
|
3e5b7ebbf1 | ||
|
|
1a9c3007b0 | ||
|
|
2f7fd1482a | ||
|
|
0416521772 | ||
|
|
cfe3f72d0e | ||
|
|
00478cc57c | ||
|
|
d5e42ee1e5 | ||
|
|
9451f1b66d | ||
|
|
ff584f9be9 | ||
|
|
a589ba38da | ||
|
|
a4a2f98b6e | ||
|
|
f5ddbfbac3 | ||
|
|
a66a4c3edb | ||
|
|
8179252924 | ||
|
|
0bec492208 | ||
|
|
5cf50f0adf | ||
|
|
197fb36524 | ||
|
|
62e6f09d94 | ||
|
|
79b2460664 | ||
|
|
66f3656bb5 | ||
|
|
6b087adab8 | ||
|
|
3653b88da6 | ||
|
|
3223bf930d | ||
|
|
2b3bc6d1ff | ||
|
|
24d7672abb | ||
|
|
89628c27f3 | ||
|
|
9adbdc377d | ||
|
|
883cd81058 | ||
|
|
0d7d029255 | ||
|
|
ce31a059bf | ||
|
|
17bac20c65 | ||
|
|
066243057f | ||
|
|
ea40547fd7 | ||
|
|
0ebc6d4b1e | ||
|
|
62c2e4709a | ||
|
|
c5229dd763 | ||
|
|
99d4190814 | ||
|
|
f086c02e13 | ||
|
|
b726f961fc | ||
|
|
4c17cf0087 | ||
|
|
9fe143cf94 | ||
|
|
e2d783c385 | ||
|
|
81cb162884 | ||
|
|
c7b6dd2677 | ||
|
|
d1fd73fd2b | ||
|
|
b0221d264e | ||
|
|
0f64badc74 | ||
|
|
7ccfdc8d21 | ||
|
|
4799293996 | ||
|
|
58a93c27ae | ||
|
|
0202b59634 | ||
|
|
9f351607d1 | ||
|
|
c45e3c9cca | ||
|
|
71bf3f5f71 | ||
|
|
ef142de5f2 | ||
|
|
f64e8bf50e | ||
|
|
e8d68e3b89 | ||
|
|
1b29d474d0 | ||
|
|
baae58ecb6 | ||
|
|
6411871ecb | ||
|
|
22833ae79b | ||
|
|
ac20b0e7fb | ||
|
|
e9e6aa77d8 | ||
|
|
29e30c388e | ||
|
|
54a40fe79c | ||
|
|
7840118dea | ||
|
|
5e27bd6d6d | ||
|
|
d4512904ea | ||
|
|
52dc288470 | ||
|
|
302de04e73 | ||
|
|
41767936d6 | ||
|
|
0ccf30202e | ||
|
|
00f36e4686 | ||
|
|
4d77f30bc0 | ||
|
|
f38b1b95f0 | ||
|
|
97ef93b840 | ||
|
|
6db15a0a20 | ||
|
|
c8395a487a | ||
|
|
852adfd436 | ||
|
|
1e948735fb | ||
|
|
0ef4247914 | ||
|
|
2673a6efee | ||
|
|
6ffe7f1a99 | ||
|
|
6d1fb63a21 | ||
|
|
9bcd303f4f | ||
|
|
38c327dae0 | ||
|
|
51177b833e | ||
|
|
cc3368704a | ||
|
|
2d53fbbe8c | ||
|
|
3959f16d65 | ||
|
|
fb28826d92 | ||
|
|
beaa8ffa27 | ||
|
|
da6d035a1d | ||
|
|
5cb77c443b | ||
|
|
8d16f496f4 | ||
|
|
82b274e522 | ||
|
|
484cdd1e07 | ||
|
|
bb2e6324bd | ||
|
|
89056e13ed | ||
|
|
df0458743b | ||
|
|
ba1ad0a6dd | ||
|
|
4e7b397c5a | ||
|
|
842e191c5f | ||
|
|
1476859c83 | ||
|
|
c6fb7dafec | ||
|
|
80069731ed | ||
|
|
feaa928674 | ||
|
|
dfa3d40665 | ||
|
|
00c2b95a0e | ||
|
|
4a82a26830 | ||
|
|
f1831fc6bb | ||
|
|
4c91a5571a | ||
|
|
ccb7a305bc | ||
|
|
0c87afefce | ||
|
|
d546817f0a | ||
|
|
e0e833b2f3 | ||
|
|
c8d359a0da | ||
|
|
210757641c | ||
|
|
68bf599a1a | ||
|
|
a10966b66b | ||
|
|
08003f2003 | ||
|
|
7cc034c2bc | ||
|
|
15bcde36cb | ||
|
|
6e69960ee9 | ||
|
|
a7a03b04a9 | ||
|
|
5759dcee48 | ||
|
|
a9672011a5 | ||
|
|
808aa188ab | ||
|
|
ad7fc61228 | ||
|
|
3e32e5c16e | ||
|
|
f67a2120f4 | ||
|
|
a4ee562387 | ||
|
|
165f4f9201 | ||
|
|
0d1740ba4a | ||
|
|
cbb3f202ca | ||
|
|
ff076fd7be | ||
|
|
d30192207b | ||
|
|
19afc1a6da | ||
|
|
4ef682915c | ||
|
|
e693e8ac37 | ||
|
|
07fe8e8390 | ||
|
|
6e58ffa927 | ||
|
|
8a8e612fd3 | ||
|
|
1e8ad4748a | ||
|
|
4c346549f5 | ||
|
|
427e44aee9 | ||
|
|
8dfc62b206 | ||
|
|
ef4a99eadc | ||
|
|
8337e43e64 | ||
|
|
689eb88332 | ||
|
|
698210c849 | ||
|
|
e2087bcc8c | ||
|
|
b066b87141 | ||
|
|
d88a766de4 | ||
|
|
6c7910c8fa | ||
|
|
f83d469833 | ||
|
|
1cea5327e9 | ||
|
|
6df7ec9dbd | ||
|
|
0f7357166d | ||
|
|
26c854df5b | ||
|
|
a43ef174f6 | ||
|
|
99f8bf039a | ||
|
|
bdb2c6efb3 | ||
|
|
21512e9722 | ||
|
|
008921c201 | ||
|
|
0882887abe | ||
|
|
0eb4574b37 | ||
|
|
b54edff4cc | ||
|
|
0d6911f41c | ||
|
|
498acaefbc | ||
|
|
50093c325a | ||
|
|
4288428c70 | ||
|
|
dc122a9450 | ||
|
|
ec1b5a7a92 | ||
|
|
df2306cf82 | ||
|
|
585135d27e | ||
|
|
06fabe491d | ||
|
|
052cc2848f | ||
|
|
b5b9d42764 | ||
|
|
d8e2239438 | ||
|
|
403ca3e538 | ||
|
|
ed65c80869 | ||
|
|
ca6ec9ee68 | ||
|
|
04fa4d3587 | ||
|
|
eedbcb6f1e | ||
|
|
61e3798533 | ||
|
|
bca978aacc | ||
|
|
190cb1dede | ||
|
|
9c8c71bd08 | ||
|
|
2b37592280 | ||
|
|
172a79acc7 | ||
|
|
179ec6a3e9 | ||
|
|
f1c055805f | ||
|
|
5cadb0d16f | ||
|
|
6875e9a41e | ||
|
|
b6f1d6e395 | ||
|
|
6e51be095b | ||
|
|
1c7237869a | ||
|
|
14433e7764 | ||
|
|
5fdb86ae43 | ||
|
|
6615469f8b | ||
|
|
d281837dd9 | ||
|
|
7c63e59ae9 | ||
|
|
7c19fe418e | ||
|
|
46d0023018 | ||
|
|
43cf249184 | ||
|
|
537dbd3dc0 | ||
|
|
7c54781d90 | ||
|
|
3b4241c70d | ||
|
|
a290e83501 | ||
|
|
6e0019227b | ||
|
|
7845b6c6fb | ||
|
|
e03e3ae50a | ||
|
|
305d7b1125 | ||
|
|
5ef751d409 | ||
|
|
683232dd10 | ||
|
|
5f1360e226 | ||
|
|
de1d7f2877 | ||
|
|
215f2897f3 | ||
|
|
2d240f23a8 | ||
|
|
f8b0ad29df | ||
|
|
af1eff9427 | ||
|
|
5def2f6f5f |
19
.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Release task
|
||||
about: Track the process of a new release
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Steps:
|
||||
|
||||
- [ ] Include translations
|
||||
- [ ] Draft: https://github.com/openfoodfoundation/openfoodnetwork/releases/new <!-- replace the URL -->
|
||||
- [ ] Test: https://semaphoreci.com/openfoodfoundation/openfoodnetwork-2/branches/master <!-- replace the URL -->
|
||||
- [ ] Publish and notify #global-community
|
||||
- [ ] Deploy and notify #instance-managers
|
||||
- [ ] Nudge next release manager
|
||||
|
||||
The full process is described at https://github.com/openfoodfoundation/openfoodnetwork/wiki/Releasing.
|
||||
@@ -4,6 +4,7 @@
|
||||
#
|
||||
# The configuration is split into three files. Look into those files for more details.
|
||||
#
|
||||
require: rubocop-rails
|
||||
inherit_from:
|
||||
|
||||
# The automatically generated todo list to ignore all current violations.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#
|
||||
# This process probably doesn't need repeating. Otherwise there is plenty
|
||||
# of room for improvements and automation.
|
||||
Metrics/LineLength:
|
||||
Layout/LineLength:
|
||||
Max: 100
|
||||
Exclude:
|
||||
- Gemfile
|
||||
@@ -32,7 +32,6 @@ Metrics/LineLength:
|
||||
- app/controllers/admin/enterprises_controller.rb
|
||||
- app/controllers/admin/inventory_items_controller.rb
|
||||
- app/controllers/admin/manager_invitations_controller.rb
|
||||
- app/controllers/admin/order_cycles_controller.rb
|
||||
- app/controllers/admin/product_import_controller.rb
|
||||
- app/controllers/admin/proxy_orders_controller.rb
|
||||
- app/controllers/admin/schedules_controller.rb
|
||||
@@ -43,10 +42,8 @@ Metrics/LineLength:
|
||||
- app/controllers/application_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/adjustments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/base_controller_decorator.rb
|
||||
- app/controllers/spree/admin/orders_controller_decorator.rb
|
||||
- app/controllers/spree/admin/payments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/reports_controller_decorator.rb
|
||||
- app/controllers/spree/credit_cards_controller.rb
|
||||
- app/controllers/spree/paypal_controller_decorator.rb
|
||||
- app/controllers/stripe/callbacks_controller.rb
|
||||
@@ -117,12 +114,10 @@ Metrics/LineLength:
|
||||
- lib/open_food_network/enterprise_issue_validator.rb
|
||||
- lib/open_food_network/group_buy_report.rb
|
||||
- lib/open_food_network/lettuce_share_report.rb
|
||||
- lib/open_food_network/order_and_distributor_report.rb
|
||||
- lib/open_food_network/order_cycle_form_applicator.rb
|
||||
- lib/open_food_network/order_cycle_management_report.rb
|
||||
- lib/open_food_network/payments_report.rb
|
||||
- lib/open_food_network/reports/bulk_coop_allocation_report.rb
|
||||
- lib/open_food_network/reports/line_items.rb
|
||||
- lib/open_food_network/sales_tax_report.rb
|
||||
- lib/open_food_network/variant_and_line_item_naming.rb
|
||||
- lib/open_food_network/xero_invoices_report.rb
|
||||
@@ -147,6 +142,7 @@ Metrics/LineLength:
|
||||
- spec/controllers/admin/subscriptions_controller_spec.rb
|
||||
- spec/controllers/admin/variant_overrides_controller_spec.rb
|
||||
- spec/controllers/api/base_controller_spec.rb
|
||||
- spec/controllers/api/exchange_products_controller_spec.rb
|
||||
- spec/controllers/api/logos_controller_spec.rb
|
||||
- spec/controllers/api/order_cycles_controller_spec.rb
|
||||
- spec/controllers/api/orders_controller_spec.rb
|
||||
@@ -308,6 +304,7 @@ Metrics/LineLength:
|
||||
- spec/serializers/api/admin/exchange_serializer_spec.rb
|
||||
- spec/serializers/api/admin/for_order_cycle/enterprise_serializer_spec.rb
|
||||
- spec/serializers/api/admin/for_order_cycle/supplied_product_serializer_spec.rb
|
||||
- spec/serializers/api/admin/order_cycle_serializer_spec.rb
|
||||
- spec/serializers/api/admin/subscription_customer_serializer_spec.rb
|
||||
- spec/serializers/api/admin/variant_override_serializer_spec.rb
|
||||
- spec/serializers/api/current_order_serializer_spec.rb
|
||||
@@ -315,11 +312,13 @@ Metrics/LineLength:
|
||||
- spec/serializers/api/order_serializer_spec.rb
|
||||
- spec/services/cart_service_spec.rb
|
||||
- spec/services/embedded_page_service_spec.rb
|
||||
- spec/services/exchange_products_renderer_spec.rb
|
||||
- spec/services/order_cycle_distributed_products_spec.rb
|
||||
- spec/services/order_cycle_distributed_variants_spec.rb
|
||||
- spec/services/order_cycle_form_spec.rb
|
||||
- spec/services/order_factory_spec.rb
|
||||
- spec/services/order_syncer_spec.rb
|
||||
- spec/services/permissions/order_spec.rb
|
||||
- spec/services/product_tag_rules_filterer_spec.rb
|
||||
- spec/services/products_renderer_spec.rb
|
||||
- spec/services/subscription_estimator_spec.rb
|
||||
@@ -365,11 +364,12 @@ Metrics/AbcSize:
|
||||
- app/controllers/spree/admin/image_settings_controller.rb
|
||||
- app/controllers/spree/admin/orders/customer_details_controller_decorator.rb
|
||||
- app/controllers/spree/admin/orders_controller_decorator.rb
|
||||
- app/controllers/spree/admin/overview_controller_decorator.rb
|
||||
- app/controllers/spree/admin/overview_controller.rb
|
||||
- app/controllers/spree/admin/payment_methods_controller.rb
|
||||
- app/controllers/spree/admin/payments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/products_controller_decorator.rb
|
||||
- app/controllers/spree/admin/reports_controller_decorator.rb
|
||||
- app/controllers/spree/admin/reports_controller.rb
|
||||
- app/controllers/spree/admin/resource_controller.rb
|
||||
- app/controllers/spree/admin/search_controller_decorator.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/admin/users_controller.rb
|
||||
@@ -568,7 +568,8 @@ Metrics/MethodLength:
|
||||
- app/controllers/spree/admin/payment_methods_controller.rb
|
||||
- app/controllers/spree/admin/payments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/products_controller_decorator.rb
|
||||
- app/controllers/spree/admin/reports_controller_decorator.rb
|
||||
- app/controllers/spree/admin/reports_controller.rb
|
||||
- app/controllers/spree/admin/resource_controller.rb
|
||||
- app/controllers/spree/admin/search_controller_decorator.rb
|
||||
- app/controllers/spree/admin/tax_categories_controller.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
@@ -647,7 +648,10 @@ Metrics/ClassLength:
|
||||
- app/controllers/admin/subscriptions_controller.rb
|
||||
- app/controllers/api/products_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/base_controller.rb
|
||||
- app/controllers/spree/admin/payment_methods_controller.rb
|
||||
- app/controllers/spree/admin/reports_controller.rb
|
||||
- app/controllers/spree/admin/resource_controller.rb
|
||||
- app/controllers/spree/admin/users_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/models/enterprise.rb
|
||||
@@ -703,6 +707,7 @@ Metrics/ModuleLength:
|
||||
- spec/models/spree/payment_spec.rb
|
||||
- spec/models/spree/product_spec.rb
|
||||
- spec/models/spree/variant_spec.rb
|
||||
- spec/services/permissions/order_spec.rb
|
||||
- spec/support/request/web_helper.rb
|
||||
|
||||
Metrics/ParameterLists:
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
# rubocop locally, the default configuration file `.rubocop.yml` loads
|
||||
# our "todo lists" to ignore all current violations.
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.2
|
||||
TargetRailsVersion: 3.2
|
||||
Exclude:
|
||||
- 'bin/**/*'
|
||||
@@ -41,7 +40,7 @@ Layout/MultilineMethodCallIndentation:
|
||||
Enabled: true
|
||||
EnforcedStyle: indented
|
||||
|
||||
Metrics/LineLength:
|
||||
Layout/LineLength:
|
||||
Max: 100
|
||||
|
||||
## TEMPORARY/CONTESTED SETTINGS
|
||||
|
||||
1201
.rubocop_todo.yml
1201
.rubocop_todo.yml
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
2.2.10
|
||||
2.3.7
|
||||
|
||||
13
Dockerfile
13
Dockerfile
@@ -6,16 +6,19 @@ RUN apt-get update && apt-get install -y curl git build-essential software-prope
|
||||
# Setup ENV variables
|
||||
ENV PATH /usr/local/src/rbenv/shims:/usr/local/src/rbenv/bin:$PATH
|
||||
ENV RBENV_ROOT /usr/local/src/rbenv
|
||||
ENV RUBY_VERSION 2.1.9
|
||||
ENV CONFIGURE_OPTS --disable-install-doc
|
||||
ENV BUNDLE_PATH /bundles
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY .ruby-version .
|
||||
|
||||
# Rbenv & Ruby part
|
||||
RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
|
||||
git clone https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \
|
||||
${RBENV_ROOT}/plugins/ruby-build/install.sh && \
|
||||
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh && \
|
||||
rbenv install $RUBY_VERSION && \
|
||||
rbenv global $RUBY_VERSION && \
|
||||
rbenv install $(cat .ruby-version) && \
|
||||
rbenv global $(cat .ruby-version) && \
|
||||
gem install bundler --version=1.17.2
|
||||
|
||||
# Postgres
|
||||
@@ -24,8 +27,4 @@ RUN sh -c "echo 'deb https://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main'
|
||||
apt-get update && \
|
||||
apt-get install -yqq --no-install-recommends postgresql-client-9.5 libpq-dev
|
||||
|
||||
ENV BUNDLE_PATH /bundles
|
||||
|
||||
COPY . /usr/src/app/
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
20
Gemfile
20
Gemfile
@@ -1,9 +1,9 @@
|
||||
source 'https://rubygems.org'
|
||||
ruby "2.2.10"
|
||||
ruby "2.3.7"
|
||||
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
|
||||
|
||||
gem 'i18n', '~> 0.6.11'
|
||||
gem 'i18n-js', '~> 3.4.2'
|
||||
gem 'i18n-js', '~> 3.5.1'
|
||||
gem 'rails', '~> 3.2.22'
|
||||
gem 'rails-i18n', '~> 3.0.0'
|
||||
gem 'rails_safe_tasks', '~> 1.0'
|
||||
@@ -84,7 +84,7 @@ gem 'paper_trail', '~> 5.2.3'
|
||||
gem 'paperclip', '~> 3.4.1'
|
||||
gem 'rack-rewrite'
|
||||
gem 'rack-ssl', require: 'rack/ssl'
|
||||
gem 'roadie-rails', '~> 1.1.1'
|
||||
gem 'roadie-rails', '~> 1.3.0'
|
||||
gem 'spinjs-rails'
|
||||
|
||||
gem 'combine_pdf'
|
||||
@@ -93,7 +93,7 @@ gem 'wkhtmltopdf-binary'
|
||||
|
||||
gem 'foreigner'
|
||||
gem 'immigrant'
|
||||
gem 'roo', '~> 2.7.0'
|
||||
gem 'roo', '~> 2.8.2'
|
||||
gem 'roo-xls', '~> 1.1.0'
|
||||
|
||||
gem 'whenever', require: false
|
||||
@@ -106,10 +106,7 @@ group :assets do
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
gem 'compass-rails'
|
||||
|
||||
gem 'mini_racer', '0.1.15'
|
||||
# Previously we found that libv8 6.7.288.46.1 breakis the compilation of mini_racer.
|
||||
# Now we see that we need to set the version explicitly. Nothing else depends on libv8.
|
||||
gem 'libv8', '6.3.292.48.1'
|
||||
gem 'mini_racer', '0.2.4'
|
||||
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
|
||||
@@ -135,7 +132,7 @@ group :test, :development do
|
||||
# Pretty printed test output
|
||||
gem 'atomic'
|
||||
gem 'awesome_print'
|
||||
gem 'capybara', '>= 2.15.4'
|
||||
gem 'capybara', '>= 2.18.0' # 3.0 requires nokogiri 1.8
|
||||
gem 'database_cleaner', '0.7.1', require: false
|
||||
gem "factory_bot_rails", require: false
|
||||
gem 'fuubar', '~> 2.5.0'
|
||||
@@ -148,7 +145,7 @@ group :test, :development do
|
||||
gem 'shoulda-matchers'
|
||||
gem 'timecop'
|
||||
gem 'unicorn-rails'
|
||||
gem 'webdrivers', '3.8.1'
|
||||
gem 'webdrivers'
|
||||
end
|
||||
|
||||
group :test do
|
||||
@@ -163,7 +160,8 @@ group :development do
|
||||
gem 'debugger-linecache'
|
||||
gem "newrelic_rpm", "~> 3.0"
|
||||
gem 'pry-byebug', '>= 3.4.3'
|
||||
gem 'rubocop', '>= 0.49.1'
|
||||
gem 'rubocop'
|
||||
gem 'rubocop-rails'
|
||||
gem 'spring', '1.7.2'
|
||||
gem 'spring-commands-rspec'
|
||||
|
||||
|
||||
99
Gemfile.lock
99
Gemfile.lock
@@ -129,7 +129,7 @@ GEM
|
||||
activesupport (= 3.2.22.5)
|
||||
arel (~> 3.0.2)
|
||||
tzinfo (~> 0.3.29)
|
||||
activerecord-import (1.0.3)
|
||||
activerecord-import (1.0.4)
|
||||
activerecord (>= 3.2)
|
||||
activerecord-postgresql-adapter (0.0.1)
|
||||
pg
|
||||
@@ -178,8 +178,7 @@ GEM
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (>= 2.0, < 4.0)
|
||||
childprocess (0.9.0)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
childprocess (3.0.0)
|
||||
chronic (0.10.2)
|
||||
chunky_png (1.3.10)
|
||||
climate_control (0.2.0)
|
||||
@@ -213,10 +212,9 @@ GEM
|
||||
sass-rails (< 5.1)
|
||||
sprockets (< 4.0)
|
||||
concurrent-ruby (1.1.5)
|
||||
connection_pool (2.2.2)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
css_parser (1.6.0)
|
||||
css_parser (1.7.0)
|
||||
addressable
|
||||
daemons (1.3.1)
|
||||
dalli (2.7.10)
|
||||
@@ -225,7 +223,7 @@ GEM
|
||||
activerecord (>= 3.2.0, < 5.0)
|
||||
fog (~> 1.0)
|
||||
rails (>= 3.2.0, < 5.0)
|
||||
ddtrace (0.28.0)
|
||||
ddtrace (0.31.0)
|
||||
msgpack
|
||||
debugger-linecache (1.2.0)
|
||||
deface (1.0.2)
|
||||
@@ -256,17 +254,17 @@ GEM
|
||||
dry-inflector (0.1.2)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.2.7)
|
||||
excon (0.62.0)
|
||||
excon (0.71.1)
|
||||
execjs (2.7.0)
|
||||
factory_bot (4.10.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_bot_rails (4.10.0)
|
||||
factory_bot (~> 4.10.0)
|
||||
railties (>= 3.0.0)
|
||||
faraday (0.15.4)
|
||||
faraday (0.17.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffaker (1.22.1)
|
||||
ffi (1.10.0)
|
||||
ffi (1.11.3)
|
||||
figaro (1.1.1)
|
||||
thor (~> 0.14)
|
||||
fission (0.5.0)
|
||||
@@ -439,7 +437,7 @@ GEM
|
||||
httparty (0.16.2)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (0.6.11)
|
||||
i18n-js (3.4.2)
|
||||
i18n-js (3.5.1)
|
||||
i18n (>= 0.6.6)
|
||||
immigrant (0.3.6)
|
||||
activerecord (>= 3.0)
|
||||
@@ -467,7 +465,7 @@ GEM
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.7.0)
|
||||
launchy (~> 2.2)
|
||||
libv8 (6.3.292.48.1)
|
||||
libv8 (7.3.492.27.1)
|
||||
mail (2.5.5)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
@@ -475,18 +473,16 @@ GEM
|
||||
mime-types (1.25.1)
|
||||
mini_mime (1.0.1)
|
||||
mini_portile2 (2.1.0)
|
||||
mini_racer (0.1.15)
|
||||
libv8 (~> 6.3)
|
||||
mini_racer (0.2.4)
|
||||
libv8 (>= 6.3)
|
||||
momentjs-rails (2.20.1)
|
||||
railties (>= 3.1)
|
||||
money (5.1.1)
|
||||
i18n (~> 0.6.0)
|
||||
msgpack (1.3.1)
|
||||
multi_json (1.13.1)
|
||||
multi_json (1.14.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.1.1)
|
||||
net-http-persistent (3.1.0)
|
||||
connection_pool (~> 2.2)
|
||||
newrelic_rpm (3.18.1.330)
|
||||
nokogiri (1.6.8.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
@@ -496,7 +492,7 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
oj (3.7.12)
|
||||
oj (3.10.0)
|
||||
orm_adapter (0.5.0)
|
||||
paper_trail (5.2.3)
|
||||
activerecord (>= 3.0, < 6.0)
|
||||
@@ -507,10 +503,10 @@ GEM
|
||||
activesupport (>= 3.0.0)
|
||||
cocaine (~> 0.5.0)
|
||||
mime-types
|
||||
parallel (1.18.0)
|
||||
parallel (1.19.1)
|
||||
paranoia (1.3.4)
|
||||
activerecord (~> 3.1)
|
||||
parser (2.6.5.0)
|
||||
parser (2.7.0.2)
|
||||
ast (~> 2.4.0)
|
||||
paypal-sdk-core (0.2.10)
|
||||
multi_json (~> 1.0)
|
||||
@@ -534,7 +530,7 @@ GEM
|
||||
rack (1.4.7)
|
||||
rack-cache (1.9.0)
|
||||
rack (>= 0.4)
|
||||
rack-mini-profiler (1.0.0)
|
||||
rack-mini-profiler (1.1.4)
|
||||
rack (>= 1.2.0)
|
||||
rack-protection (1.5.5)
|
||||
rack
|
||||
@@ -564,7 +560,7 @@ GEM
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.0)
|
||||
rake (12.3.3)
|
||||
rake (13.0.0)
|
||||
ransack (0.7.2)
|
||||
actionpack (~> 3.0)
|
||||
activerecord (~> 3.0)
|
||||
@@ -585,12 +581,12 @@ GEM
|
||||
roadie (3.4.0)
|
||||
css_parser (~> 1.4)
|
||||
nokogiri (~> 1.5)
|
||||
roadie-rails (1.1.1)
|
||||
railties (>= 3.0, < 5.1)
|
||||
roadie-rails (1.3.0)
|
||||
railties (>= 3.0, < 5.3)
|
||||
roadie (~> 3.1)
|
||||
roo (2.7.1)
|
||||
roo (2.8.2)
|
||||
nokogiri (~> 1)
|
||||
rubyzip (~> 1.1, < 2.0.0)
|
||||
rubyzip (>= 1.2.1, < 2.0.0)
|
||||
roo-xls (1.1.0)
|
||||
nokogiri
|
||||
roo (>= 2.0.0beta1, < 3)
|
||||
@@ -615,16 +611,19 @@ GEM
|
||||
rspec-expectations (~> 3.9.0)
|
||||
rspec-mocks (~> 3.9.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-retry (0.6.1)
|
||||
rspec-retry (0.6.2)
|
||||
rspec-core (> 3.3)
|
||||
rspec-support (3.9.0)
|
||||
rubocop (0.68.1)
|
||||
rubocop (0.79.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.5, != 2.5.1.1)
|
||||
parser (>= 2.7.0.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 1.6)
|
||||
unicode-display_width (>= 1.4.0, < 1.7)
|
||||
rubocop-rails (2.4.1)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 0.72.0)
|
||||
ruby-ole (1.2.12.1)
|
||||
ruby-progressbar (1.10.1)
|
||||
ruby-rc4 (0.1.5)
|
||||
@@ -638,9 +637,9 @@ GEM
|
||||
select2-rails (3.4.9)
|
||||
sass-rails
|
||||
thor (~> 0.14)
|
||||
selenium-webdriver (3.141.0)
|
||||
childprocess (~> 0.5)
|
||||
rubyzip (~> 1.2, >= 1.2.2)
|
||||
selenium-webdriver (3.142.7)
|
||||
childprocess (>= 0.5, < 4.0)
|
||||
rubyzip (>= 1.2.2)
|
||||
shoulda-matchers (2.8.0)
|
||||
activesupport (>= 3.0.0)
|
||||
simplecov (0.17.1)
|
||||
@@ -666,10 +665,8 @@ GEM
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
state_machine (1.2.0)
|
||||
stringex (1.5.1)
|
||||
stripe (4.24.0)
|
||||
faraday (~> 0.13)
|
||||
net-http-persistent (~> 3.0)
|
||||
test-unit (3.3.4)
|
||||
stripe (5.11.0)
|
||||
test-unit (3.3.5)
|
||||
power_assert
|
||||
thor (0.20.3)
|
||||
tilt (1.4.1)
|
||||
@@ -682,11 +679,11 @@ GEM
|
||||
turbo-sprockets-rails3 (0.3.14)
|
||||
railties (> 3.2.8, < 4.0.0)
|
||||
sprockets (>= 2.2.0)
|
||||
tzinfo (0.3.55)
|
||||
tzinfo (0.3.56)
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (1.5.0)
|
||||
unicorn (5.5.1)
|
||||
unicode-display_width (1.6.0)
|
||||
unicorn (5.5.2)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
unicorn-rails (2.2.1)
|
||||
@@ -699,10 +696,10 @@ GEM
|
||||
railties (>= 3.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
webdrivers (3.8.1)
|
||||
webdrivers (4.2.0)
|
||||
nokogiri (~> 1.6)
|
||||
rubyzip (~> 1.0)
|
||||
selenium-webdriver (~> 3.0)
|
||||
rubyzip (>= 1.3.0)
|
||||
selenium-webdriver (>= 3.0, < 4.0)
|
||||
webmock (3.7.6)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
@@ -734,7 +731,7 @@ DEPENDENCIES
|
||||
blockenspiel
|
||||
bugsnag
|
||||
byebug (~> 9.0.0)
|
||||
capybara (>= 2.15.4)
|
||||
capybara (>= 2.18.0)
|
||||
coffee-rails (~> 3.2.1)
|
||||
combine_pdf
|
||||
compass-rails
|
||||
@@ -763,7 +760,7 @@ DEPENDENCIES
|
||||
gmaps4rails
|
||||
haml
|
||||
i18n (~> 0.6.11)
|
||||
i18n-js (~> 3.4.2)
|
||||
i18n-js (~> 3.5.1)
|
||||
immigrant
|
||||
jquery-migrate-rails
|
||||
jquery-rails (= 3.0.4)
|
||||
@@ -772,8 +769,7 @@ DEPENDENCIES
|
||||
kaminari (~> 0.14.1)
|
||||
knapsack
|
||||
letter_opener (>= 1.4.1)
|
||||
libv8 (= 6.3.292.48.1)
|
||||
mini_racer (= 0.1.15)
|
||||
mini_racer (= 0.2.4)
|
||||
momentjs-rails
|
||||
newrelic_rpm (~> 3.0)
|
||||
nokogiri (>= 1.6.7.1)
|
||||
@@ -793,12 +789,13 @@ DEPENDENCIES
|
||||
rails-i18n (~> 3.0.0)
|
||||
rails_safe_tasks (~> 1.0)
|
||||
redcarpet
|
||||
roadie-rails (~> 1.1.1)
|
||||
roo (~> 2.7.0)
|
||||
roadie-rails (~> 1.3.0)
|
||||
roo (~> 2.8.2)
|
||||
roo-xls (~> 1.1.0)
|
||||
rspec-rails (>= 3.5.2)
|
||||
rspec-retry
|
||||
rubocop (>= 0.49.1)
|
||||
rubocop
|
||||
rubocop-rails
|
||||
sass (~> 3.3)
|
||||
sass-rails (~> 3.2.3)
|
||||
selenium-webdriver
|
||||
@@ -821,14 +818,14 @@ DEPENDENCIES
|
||||
unicorn
|
||||
unicorn-rails
|
||||
web!
|
||||
webdrivers (= 3.8.1)
|
||||
webdrivers
|
||||
webmock
|
||||
whenever
|
||||
wicked_pdf
|
||||
wkhtmltopdf-binary
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.2.10p489
|
||||
ruby 2.3.7p456
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
|
||||
@@ -142,7 +142,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
if confirm("Are you sure?")
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/products/" + product.id + "/soft_delete"
|
||||
url: "/api/products/" + product.id
|
||||
).success (data) ->
|
||||
$scope.products.splice $scope.products.indexOf(product), 1
|
||||
DirtyProducts.deleteProduct product.id
|
||||
@@ -157,7 +157,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
if confirm(t("are_you_sure"))
|
||||
$http(
|
||||
method: "DELETE"
|
||||
url: "/api/products/" + product.permalink_live + "/variants/" + variant.id + "/soft_delete"
|
||||
url: "/api/products/" + product.permalink_live + "/variants/" + variant.id
|
||||
).success (data) ->
|
||||
$scope.removeVariant(product, variant)
|
||||
else
|
||||
|
||||
@@ -1,92 +1,12 @@
|
||||
angular.module('admin.orderCycles')
|
||||
.controller 'AdminCreateOrderCycleCtrl', ($scope, $filter, $window, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, ocInstance, StatusMessage) ->
|
||||
$scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id)
|
||||
$scope.supplier_enterprises = Enterprise.producer_enterprises
|
||||
$scope.distributor_enterprises = Enterprise.hub_enterprises
|
||||
$scope.supplied_products = Enterprise.supplied_products
|
||||
$scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id)
|
||||
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
|
||||
.controller 'AdminCreateOrderCycleCtrl', ($scope, $controller, $filter, $window, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, ocInstance, StatusMessage) ->
|
||||
$controller('AdminOrderCycleBasicCtrl', {$scope: $scope, ocInstance: ocInstance})
|
||||
|
||||
$scope.OrderCycle = OrderCycle
|
||||
$scope.order_cycle = OrderCycle.new({ coordinator_id: ocInstance.coordinator_id})
|
||||
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.$watch 'order_cycle_form.$dirty', (newValue) ->
|
||||
StatusMessage.display 'notice', t("admin.unsaved_changes") if newValue
|
||||
|
||||
$scope.$watch 'order_cycle_form.$valid', (isValid) ->
|
||||
StatusMessage.setValidation(isValid)
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded && !RequestMonitor.loading
|
||||
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
$scope.enterpriseTotalVariants = (enterprise) ->
|
||||
Enterprise.totalVariants(enterprise)
|
||||
|
||||
$scope.productSuppliedToOrderCycle = (product) ->
|
||||
OrderCycle.productSuppliedToOrderCycle(product)
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangeVariantsFor = (enterprise_id) ->
|
||||
$filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id])
|
||||
|
||||
$scope.exchangeDirection = (exchange) ->
|
||||
OrderCycle.exchangeDirection(exchange)
|
||||
|
||||
$scope.enterprisesWithFees = ->
|
||||
$scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier($scope.new_supplier_id)
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor($scope.new_distributor_id)
|
||||
|
||||
$scope.removeExchange = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchange(exchange)
|
||||
$scope.order_cycle_form.$dirty = true
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
|
||||
$scope.addExchangeFee = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addExchangeFee(exchange)
|
||||
|
||||
$scope.removeExchangeFee = ($event, exchange, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchangeFee(exchange, index)
|
||||
|
||||
$scope.removeDistributionOfVariant = (variant_id) ->
|
||||
OrderCycle.removeDistributionOfVariant(variant_id)
|
||||
$scope.enterprises = Enterprise.index(coordinator_id: ocInstance.coordinator_id)
|
||||
$scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id)
|
||||
|
||||
$scope.submit = ($event, destination) ->
|
||||
$event.preventDefault()
|
||||
StatusMessage.display 'progress', t('js.saving')
|
||||
OrderCycle.create(destination)
|
||||
|
||||
$scope.cancel = (destination) ->
|
||||
$window.location = destination
|
||||
|
||||
@@ -1,91 +1,17 @@
|
||||
angular.module('admin.orderCycles')
|
||||
.controller 'AdminEditOrderCycleCtrl', ($scope, $filter, $location, $window, OrderCycle, Enterprise, EnterpriseFee, StatusMessage, Schedules, RequestMonitor, ocInstance) ->
|
||||
.controller 'AdminEditOrderCycleCtrl', ($scope, $controller, $filter, $location, $window, OrderCycle, Enterprise, EnterpriseFee, StatusMessage, Schedules, RequestMonitor, ocInstance) ->
|
||||
$controller('AdminOrderCycleBasicCtrl', {$scope: $scope, ocInstance: ocInstance})
|
||||
|
||||
order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
|
||||
$scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id)
|
||||
$scope.supplier_enterprises = Enterprise.producer_enterprises
|
||||
$scope.distributor_enterprises = Enterprise.hub_enterprises
|
||||
$scope.supplied_products = Enterprise.supplied_products
|
||||
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: order_cycle_id)
|
||||
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
|
||||
|
||||
$scope.OrderCycle = OrderCycle
|
||||
$scope.order_cycle = OrderCycle.load(order_cycle_id)
|
||||
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.$watch 'order_cycle_form.$dirty', (newValue) ->
|
||||
StatusMessage.display 'notice', t("admin.unsaved_changes") if newValue
|
||||
|
||||
$scope.$watch 'order_cycle_form.$valid', (isValid) ->
|
||||
StatusMessage.setValidation(isValid)
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded && !RequestMonitor.loading
|
||||
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
$scope.enterpriseTotalVariants = (enterprise) ->
|
||||
Enterprise.totalVariants(enterprise)
|
||||
|
||||
$scope.productSuppliedToOrderCycle = (product) ->
|
||||
OrderCycle.productSuppliedToOrderCycle(product)
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangeVariantsFor = (enterprise_id) ->
|
||||
$filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id])
|
||||
|
||||
$scope.exchangeDirection = (exchange) ->
|
||||
OrderCycle.exchangeDirection(exchange)
|
||||
|
||||
$scope.enterprisesWithFees = ->
|
||||
$scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier($scope.new_supplier_id)
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor($scope.new_distributor_id)
|
||||
|
||||
$scope.removeExchange = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchange(exchange)
|
||||
$scope.order_cycle_form.$dirty = true
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
$scope.enterprises = Enterprise.index(order_cycle_id: order_cycle_id)
|
||||
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: order_cycle_id)
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
$scope.order_cycle_form.$dirty = true
|
||||
|
||||
$scope.addExchangeFee = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addExchangeFee(exchange)
|
||||
|
||||
$scope.removeExchangeFee = ($event, exchange, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchangeFee(exchange, index)
|
||||
$scope.order_cycle_form.$dirty = true
|
||||
|
||||
$scope.removeDistributionOfVariant = (variant_id) ->
|
||||
OrderCycle.removeDistributionOfVariant(variant_id)
|
||||
|
||||
$scope.submit = (destination) ->
|
||||
$event.preventDefault()
|
||||
StatusMessage.display 'progress', t('js.saving')
|
||||
@@ -94,6 +20,3 @@ angular.module('admin.orderCycles')
|
||||
$event.preventDefault()
|
||||
StatusMessage.display 'progress', t('js.saving')
|
||||
OrderCycle.update(destination, $scope.order_cycle_form)
|
||||
|
||||
$scope.cancel = (destination) ->
|
||||
$window.location = destination
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
angular.module('admin.orderCycles').controller 'AdminOrderCycleIncomingCtrl', ($scope, $controller, $location, Enterprise, OrderCycle, ExchangeProduct, ocInstance) ->
|
||||
$controller('AdminOrderCycleExchangesCtrl', {$scope: $scope, ocInstance: ocInstance, $location: $location})
|
||||
|
||||
$scope.view = 'incoming'
|
||||
|
||||
$scope.exchangeTotalVariants = (exchange) ->
|
||||
return unless $scope.enterprises? && $scope.enterprises[exchange.enterprise_id]?
|
||||
|
||||
enterprise = $scope.enterprises[exchange.enterprise_id]
|
||||
return enterprise.numVariants if enterprise.numVariants?
|
||||
|
||||
enterprise.numVariants = 0
|
||||
params = { exchange_id: exchange.id, enterprise_id: exchange.enterprise_id, order_cycle_id: $scope.order_cycle.id, incoming: true}
|
||||
ExchangeProduct.countVariants params, (variants_count) ->
|
||||
enterprise.numVariants = variants_count
|
||||
$scope.setSelectAllVariantsCheckboxValue(exchange, enterprise.numVariants)
|
||||
|
||||
return enterprise.numVariants
|
||||
|
||||
$scope.addSupplier = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addSupplier $scope.new_supplier_id
|
||||
|
||||
# To select all variants we first need to load them all from the server
|
||||
#
|
||||
# This is only needed in Incoming exchanges as here we use supplied_products,
|
||||
# in Outgoing Exchanges the variants are loaded as part of the Exchange payload
|
||||
$scope.selectAllVariants = (exchange, selected) ->
|
||||
$scope.loadAllExchangeProducts(exchange).then ->
|
||||
$scope.setExchangeVariants(exchange, $scope.suppliedVariants(exchange.enterprise_id), selected)
|
||||
$scope.$apply()
|
||||
@@ -0,0 +1,40 @@
|
||||
angular.module('admin.orderCycles')
|
||||
.controller 'AdminOrderCycleBasicCtrl', ($scope, $filter, $window, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, ocInstance, StatusMessage) ->
|
||||
$scope.StatusMessage = StatusMessage
|
||||
$scope.OrderCycle = OrderCycle
|
||||
|
||||
$scope.$watch 'order_cycle_form.$dirty', (newValue) ->
|
||||
StatusMessage.display 'notice', t("admin.unsaved_changes") if newValue
|
||||
|
||||
$scope.$watch 'order_cycle_form.$valid', (isValid) ->
|
||||
StatusMessage.setValidation(isValid)
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded && !RequestMonitor.loading
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
|
||||
$scope.cancel = (destination) ->
|
||||
$window.location = destination
|
||||
|
||||
# Used in panels/exchange_products_supplied.html
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
# Used in panels/exchange_products_supplied.html and panels/exchange_products_distributed.html
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
# The following methods are specific to the general settings pages:
|
||||
# - simple create, simple edit and general settings pages
|
||||
|
||||
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
@@ -0,0 +1,76 @@
|
||||
angular.module('admin.orderCycles')
|
||||
.controller 'AdminOrderCycleExchangesCtrl', ($scope, $controller, $filter, $window, $location, $timeout, OrderCycle, ExchangeProduct, Enterprise, EnterpriseFee, Schedules, RequestMonitor, ocInstance, StatusMessage) ->
|
||||
$controller('AdminEditOrderCycleCtrl', {$scope: $scope, ocInstance: ocInstance, $location: $location})
|
||||
|
||||
$scope.supplier_enterprises = Enterprise.producer_enterprises
|
||||
$scope.distributor_enterprises = Enterprise.hub_enterprises
|
||||
|
||||
$scope.productsLoading = ->
|
||||
RequestMonitor.loading
|
||||
|
||||
$scope.setSelectAllVariantsCheckboxValue = (exchange, totalNumberOfVariants) ->
|
||||
exchange.select_all_variants = $scope.exchangeSelectedVariants(exchange) >= totalNumberOfVariants
|
||||
|
||||
$scope.exchangeSelectedVariants = (exchange) ->
|
||||
OrderCycle.exchangeSelectedVariants(exchange)
|
||||
|
||||
$scope.exchangeDirection = (exchange) ->
|
||||
OrderCycle.exchangeDirection(exchange)
|
||||
|
||||
$scope.enterprisesWithFees = ->
|
||||
$scope.enterprises[id] for id in OrderCycle.participatingEnterpriseIds() when $scope.enterpriseFeesForEnterprise(id).length > 0
|
||||
|
||||
$scope.removeExchange = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchange(exchange)
|
||||
$scope.order_cycle_form.$dirty = true
|
||||
|
||||
$scope.addExchangeFee = ($event, exchange) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addExchangeFee(exchange)
|
||||
|
||||
$scope.removeExchangeFee = ($event, exchange, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeExchangeFee(exchange, index)
|
||||
$scope.order_cycle_form.$dirty = true
|
||||
|
||||
$scope.setPickupTimeFieldDirty = (index) ->
|
||||
$timeout ->
|
||||
pickup_time_field_name = "order_cycle_outgoing_exchange_" + index + "_pickup_time"
|
||||
$scope.order_cycle_form[pickup_time_field_name].$setDirty()
|
||||
|
||||
$scope.removeDistributionOfVariant = (variant_id) ->
|
||||
OrderCycle.removeDistributionOfVariant(variant_id)
|
||||
|
||||
$scope.loadExchangeProducts = (exchange, page = 1) ->
|
||||
enterprise = $scope.enterprises[exchange.enterprise_id]
|
||||
enterprise.supplied_products ?= []
|
||||
|
||||
return if enterprise.last_page_loaded? && enterprise.last_page_loaded >= page
|
||||
enterprise.last_page_loaded = page
|
||||
|
||||
incoming = true if $scope.view == 'incoming'
|
||||
params = { exchange_id: exchange.id, enterprise_id: exchange.enterprise_id, order_cycle_id: $scope.order_cycle.id, incoming: incoming, page: page}
|
||||
ExchangeProduct.index params, (products, num_of_pages, num_of_products) ->
|
||||
enterprise.num_of_pages = num_of_pages
|
||||
enterprise.num_of_products = num_of_products
|
||||
enterprise.supplied_products.push products...
|
||||
|
||||
$scope.loadMoreExchangeProducts = (exchange) ->
|
||||
$scope.loadExchangeProducts(exchange, $scope.enterprises[exchange.enterprise_id].last_page_loaded + 1)
|
||||
|
||||
$scope.loadAllExchangeProducts = (exchange) ->
|
||||
enterprise = $scope.enterprises[exchange.enterprise_id]
|
||||
|
||||
if enterprise.last_page_loaded < enterprise.num_of_pages
|
||||
for page_to_load in [(enterprise.last_page_loaded + 1)..enterprise.num_of_pages]
|
||||
RequestMonitor.load $scope.loadExchangeProducts(exchange, page_to_load).$promise
|
||||
|
||||
RequestMonitor.loadQueue
|
||||
|
||||
# initialize exchange products panel if not yet done
|
||||
$scope.exchangeProdutsPanelInitialized = []
|
||||
$scope.initializeExchangeProductsPanel = (exchange) ->
|
||||
return if $scope.exchangeProdutsPanelInitialized[exchange.enterprise_id]
|
||||
RequestMonitor.load $scope.loadExchangeProducts(exchange).$promise
|
||||
$scope.exchangeProdutsPanelInitialized[exchange.enterprise_id] = true
|
||||
@@ -0,0 +1,24 @@
|
||||
angular.module('admin.orderCycles').controller 'AdminOrderCycleOutgoingCtrl', ($scope, $controller, $filter, $location, OrderCycle, ocInstance, StatusMessage) ->
|
||||
$controller('AdminOrderCycleExchangesCtrl', {$scope: $scope, ocInstance: ocInstance, $location: $location})
|
||||
|
||||
$scope.view = 'outgoing'
|
||||
|
||||
$scope.variantSuppliedToOrderCycle = (variant) ->
|
||||
OrderCycle.variantSuppliedToOrderCycle(variant)
|
||||
|
||||
$scope.incomingExchangeVariantsFor = (enterprise_id) ->
|
||||
$filter('filterExchangeVariants')(OrderCycle.incomingExchangesVariants(), $scope.order_cycle.visible_variants_for_outgoing_exchanges[enterprise_id])
|
||||
|
||||
$scope.exchangeTotalVariants = (exchange) ->
|
||||
totalNumberOfVariants = $scope.incomingExchangeVariantsFor(exchange.enterprise_id).length
|
||||
$scope.setSelectAllVariantsCheckboxValue(exchange, totalNumberOfVariants)
|
||||
totalNumberOfVariants
|
||||
|
||||
$scope.addDistributor = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addDistributor $scope.new_distributor_id
|
||||
|
||||
$scope.submit = ($event, destination) ->
|
||||
$event.preventDefault()
|
||||
StatusMessage.display 'progress', t('js.saving')
|
||||
OrderCycle.update(destination, $scope.order_cycle_form) if OrderCycle.confirmNoDistributors()
|
||||
@@ -1,58 +1,38 @@
|
||||
angular.module('admin.orderCycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, $window, OrderCycle, Enterprise, EnterpriseFee, StatusMessage, Schedules, RequestMonitor, ocInstance) ->
|
||||
$scope.StatusMessage = StatusMessage
|
||||
$scope.OrderCycle = OrderCycle
|
||||
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
|
||||
angular.module('admin.orderCycles').controller "AdminSimpleCreateOrderCycleCtrl", ($scope, $controller, $window, OrderCycle, Enterprise, EnterpriseFee, ExchangeProduct, StatusMessage, Schedules, RequestMonitor, ocInstance) ->
|
||||
$controller('AdminOrderCycleBasicCtrl', {$scope: $scope, ocInstance: ocInstance})
|
||||
|
||||
$scope.order_cycle = OrderCycle.new {coordinator_id: ocInstance.coordinator_id}, =>
|
||||
# TODO: make this a get method, which only fetches one enterprise
|
||||
$scope.enterprises = Enterprise.index {coordinator_id: ocInstance.coordinator_id}, (enterprises) =>
|
||||
$scope.init(enterprises)
|
||||
$scope.enterprise_fees = EnterpriseFee.index(coordinator_id: ocInstance.coordinator_id)
|
||||
|
||||
$scope.$watch 'order_cycle_form.$dirty', (newValue) ->
|
||||
StatusMessage.display 'notice', t("admin.unsaved_changes") if newValue
|
||||
|
||||
$scope.$watch 'order_cycle_form.$valid', (isValid) ->
|
||||
StatusMessage.setValidation(isValid)
|
||||
|
||||
$scope.init = (enterprises) ->
|
||||
enterprise = enterprises[Object.keys(enterprises)[0]]
|
||||
OrderCycle.addSupplier enterprise.id
|
||||
OrderCycle.addDistributor enterprise.id
|
||||
$scope.outgoing_exchange = OrderCycle.order_cycle.outgoing_exchanges[0]
|
||||
|
||||
# All variants start as checked
|
||||
OrderCycle.setExchangeVariants(OrderCycle.order_cycle.incoming_exchanges[0],
|
||||
Enterprise.suppliedVariants(enterprise.id), true)
|
||||
|
||||
OrderCycle.order_cycle.coordinator_id = enterprise.id
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded && !RequestMonitor.loading
|
||||
OrderCycle.addDistributor enterprise.id, $scope.setOutgoingExchange
|
||||
OrderCycle.addSupplier enterprise.id, $scope.loadExchangeProducts
|
||||
|
||||
$scope.setOutgoingExchange = ->
|
||||
$scope.outgoing_exchange = OrderCycle.order_cycle.outgoing_exchanges[0]
|
||||
|
||||
$scope.loadExchangeProducts = ->
|
||||
$scope.incoming_exchange = OrderCycle.order_cycle.incoming_exchanges[0]
|
||||
|
||||
params = { enterprise_id: $scope.incoming_exchange.enterprise_id, incoming: true }
|
||||
ExchangeProduct.index params, $scope.storeProductsAndSelectAllVariants
|
||||
|
||||
$scope.storeProductsAndSelectAllVariants = (products) ->
|
||||
$scope.enterprises[$scope.incoming_exchange.enterprise_id].supplied_products = products
|
||||
|
||||
# All variants start as checked
|
||||
OrderCycle.setExchangeVariants($scope.incoming_exchange,
|
||||
Enterprise.suppliedVariants($scope.incoming_exchange.enterprise_id), true)
|
||||
|
||||
$scope.removeDistributionOfVariant = angular.noop
|
||||
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
|
||||
$scope.submit = ($event, destination) ->
|
||||
$event.preventDefault()
|
||||
StatusMessage.display 'progress', t('js.saving')
|
||||
OrderCycle.mirrorIncomingToOutgoingProducts()
|
||||
OrderCycle.create(destination)
|
||||
|
||||
$scope.cancel = (destination) ->
|
||||
$window.location = destination
|
||||
OrderCycle.create(destination) if OrderCycle.confirmNoDistributors()
|
||||
|
||||
@@ -1,51 +1,27 @@
|
||||
angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $location, $window, OrderCycle, Enterprise, EnterpriseFee, Schedules, RequestMonitor, StatusMessage, ocInstance) ->
|
||||
angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl", ($scope, $controller, $location, $window, OrderCycle, Enterprise, EnterpriseFee, ExchangeProduct, Schedules, RequestMonitor, StatusMessage, ocInstance) ->
|
||||
$controller('AdminOrderCycleBasicCtrl', {$scope: $scope, ocInstance: ocInstance})
|
||||
|
||||
$scope.orderCycleId = ->
|
||||
$location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
|
||||
|
||||
$scope.StatusMessage = StatusMessage
|
||||
$scope.enterprises = Enterprise.index(order_cycle_id: $scope.orderCycleId())
|
||||
$scope.enterprise_fees = EnterpriseFee.index(order_cycle_id: $scope.orderCycleId())
|
||||
$scope.schedules = Schedules.index({enterprise_id: ocInstance.coordinator_id})
|
||||
$scope.OrderCycle = OrderCycle
|
||||
$scope.order_cycle = OrderCycle.load $scope.orderCycleId(), (order_cycle) =>
|
||||
$scope.init()
|
||||
|
||||
$scope.$watch 'order_cycle_form.$dirty', (newValue) ->
|
||||
StatusMessage.display 'notice', t("admin.unsaved_changes") if newValue
|
||||
|
||||
$scope.$watch 'order_cycle_form.$valid', (isValid) ->
|
||||
StatusMessage.setValidation(isValid)
|
||||
|
||||
$scope.loaded = ->
|
||||
Enterprise.loaded && EnterpriseFee.loaded && OrderCycle.loaded && !RequestMonitor.loading
|
||||
|
||||
$scope.init = ->
|
||||
$scope.outgoing_exchange = OrderCycle.order_cycle.outgoing_exchanges[0]
|
||||
$scope.loadExchangeProducts()
|
||||
|
||||
$scope.enterpriseFeesForEnterprise = (enterprise_id) ->
|
||||
EnterpriseFee.forEnterprise(parseInt(enterprise_id))
|
||||
$scope.loadExchangeProducts = ->
|
||||
exchange = OrderCycle.order_cycle.incoming_exchanges[0]
|
||||
ExchangeProduct.index { exchange_id: exchange.id }, (products) ->
|
||||
$scope.enterprises[exchange.enterprise_id].supplied_products = products
|
||||
|
||||
$scope.removeDistributionOfVariant = angular.noop
|
||||
|
||||
$scope.setExchangeVariants = (exchange, variants, selected) ->
|
||||
OrderCycle.setExchangeVariants(exchange, variants, selected)
|
||||
|
||||
$scope.suppliedVariants = (enterprise_id) ->
|
||||
Enterprise.suppliedVariants(enterprise_id)
|
||||
|
||||
$scope.addCoordinatorFee = ($event) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.addCoordinatorFee()
|
||||
|
||||
$scope.removeCoordinatorFee = ($event, index) ->
|
||||
$event.preventDefault()
|
||||
OrderCycle.removeCoordinatorFee(index)
|
||||
|
||||
$scope.submit = ($event, destination) ->
|
||||
$event.preventDefault()
|
||||
StatusMessage.display 'progress', t('js.saving')
|
||||
OrderCycle.mirrorIncomingToOutgoingProducts()
|
||||
OrderCycle.update(destination, $scope.order_cycle_form)
|
||||
|
||||
$scope.cancel = (destination) ->
|
||||
$window.location = destination
|
||||
OrderCycle.update(destination, $scope.order_cycle_form) if OrderCycle.confirmNoDistributors()
|
||||
|
||||
@@ -12,7 +12,6 @@ angular.module('admin.orderCycles').factory('Enterprise', ($resource) ->
|
||||
enterprises: {}
|
||||
producer_enterprises: []
|
||||
hub_enterprises: []
|
||||
supplied_products: []
|
||||
loaded: false
|
||||
|
||||
index: (params={}, callback=null) ->
|
||||
@@ -22,9 +21,6 @@ angular.module('admin.orderCycles').factory('Enterprise', ($resource) ->
|
||||
@producer_enterprises.push(enterprise) if enterprise.is_primary_producer
|
||||
@hub_enterprises.push(enterprise) if enterprise.sells == 'any'
|
||||
|
||||
for product in enterprise.supplied_products
|
||||
@supplied_products.push(product)
|
||||
|
||||
@loaded = true
|
||||
(callback || angular.noop)(@enterprises)
|
||||
|
||||
@@ -39,13 +35,4 @@ angular.module('admin.orderCycles').factory('Enterprise', ($resource) ->
|
||||
variant.id for variant in product.variants
|
||||
else
|
||||
[product.master_id]
|
||||
|
||||
totalVariants: (enterprise) ->
|
||||
numVariants = 0
|
||||
|
||||
if enterprise
|
||||
counts = for product in enterprise.supplied_products
|
||||
numVariants += if product.variants.length == 0 then 1 else product.variants.length
|
||||
|
||||
numVariants
|
||||
})
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
angular.module('admin.orderCycles').factory('ExchangeProduct', ($resource) ->
|
||||
ExchangeProductResource = $resource('/api/exchanges/:exchange_id/products.json', {}, {
|
||||
'index': { method: 'GET' }
|
||||
'variant_count': { method: 'GET', params: { action_name: "variant_count" }}
|
||||
})
|
||||
{
|
||||
ExchangeProductResource: ExchangeProductResource
|
||||
|
||||
index: (params={}, callback=null) ->
|
||||
ExchangeProductResource.index params, (data) =>
|
||||
(callback || angular.noop)(data.products, data.pagination.pages, data.pagination.results)
|
||||
|
||||
countVariants: (params={}, callback=null) ->
|
||||
ExchangeProductResource.variant_count params, (data) =>
|
||||
(callback || angular.noop)(data.count)
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, StatusMessage, Panels) ->
|
||||
angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $timeout, StatusMessage, Panels) ->
|
||||
OrderCycleResource = $resource '/admin/order_cycles/:action_name/:order_cycle_id.json', {}, {
|
||||
'index': { method: 'GET', isArray: true}
|
||||
'new' : { method: 'GET', params: { action_name: "new" } }
|
||||
@@ -44,11 +44,15 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
|
||||
@removeDistributionOfVariant(variant.id) if exchange.incoming
|
||||
|
||||
|
||||
addSupplier: (new_supplier_id) ->
|
||||
this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []})
|
||||
addSupplier: (new_supplier_id, callback) ->
|
||||
this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []})
|
||||
$timeout ->
|
||||
(callback || angular.noop)()
|
||||
|
||||
addDistributor: (new_distributor_id) ->
|
||||
this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: []})
|
||||
addDistributor: (new_distributor_id, callback) ->
|
||||
this.order_cycle.outgoing_exchanges.push({ enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: [] })
|
||||
$timeout ->
|
||||
(callback || angular.noop)()
|
||||
|
||||
removeExchange: (exchange) ->
|
||||
if exchange.incoming
|
||||
@@ -71,18 +75,6 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
|
||||
removeExchangeFee: (exchange, index) ->
|
||||
exchange.enterprise_fees.splice(index, 1)
|
||||
|
||||
productSuppliedToOrderCycle: (product) ->
|
||||
product_variant_ids = (variant.id for variant in product.variants)
|
||||
variant_ids = [product.master_id].concat(product_variant_ids)
|
||||
incomingExchangesVariants = this.incomingExchangesVariants()
|
||||
|
||||
# TODO: This is an O(n^2) implementation of set intersection and thus is slooow.
|
||||
# Use a better algorithm if needed.
|
||||
# Also, incomingExchangesVariants is called every time, when it only needs to be
|
||||
# called once per change to incoming variants. Some sort of caching?
|
||||
ids = (variant_id for variant_id in variant_ids when incomingExchangesVariants.indexOf(variant_id) != -1)
|
||||
ids.length > 0
|
||||
|
||||
variantSuppliedToOrderCycle: (variant) ->
|
||||
this.incomingExchangesVariants().indexOf(variant.id) != -1
|
||||
|
||||
@@ -143,15 +135,18 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
|
||||
delete(service.order_cycle.exchanges)
|
||||
service.loaded = true
|
||||
|
||||
(callback || angular.noop)(service.order_cycle)
|
||||
$timeout ->
|
||||
(callback || angular.noop)(service.order_cycle)
|
||||
|
||||
this.order_cycle
|
||||
|
||||
create: (destination) ->
|
||||
return unless @confirmNoDistributors()
|
||||
oc = new OrderCycleResource({order_cycle: this.dataForSubmit()})
|
||||
oc.$create (data) ->
|
||||
$window.location = destination
|
||||
if destination? && destination.length != 0
|
||||
$window.location = destination
|
||||
else if data.edit_path?
|
||||
$window.location = data.edit_path
|
||||
, (response) ->
|
||||
if response.data.errors?
|
||||
StatusMessage.display('failure', response.data.errors[0])
|
||||
@@ -159,7 +154,6 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
|
||||
StatusMessage.display('failure', t('js.order_cycles.create_failure'))
|
||||
|
||||
update: (destination, form) ->
|
||||
return unless @confirmNoDistributors()
|
||||
oc = new OrderCycleResource({order_cycle: this.dataForSubmit()})
|
||||
oc.$update {order_cycle_id: this.order_cycle.id, reloading: (if destination? then 1 else 0)}, (data) =>
|
||||
form.$setPristine() if form
|
||||
@@ -173,7 +167,6 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, S
|
||||
else
|
||||
StatusMessage.display('failure', t('js.order_cycles.update_failure'))
|
||||
|
||||
|
||||
confirmNoDistributors: ->
|
||||
if @order_cycle.outgoing_exchanges.length == 0
|
||||
confirm t('js.order_cycles.no_distributors')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor, Orders, SortOptions, $window, $filter) ->
|
||||
angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, RequestMonitor, Orders, SortOptions, $window, $filter) ->
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.pagination = Orders.pagination
|
||||
$scope.orders = Orders.all
|
||||
@@ -13,6 +13,7 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor,
|
||||
$scope.selected = false
|
||||
$scope.select_all = false
|
||||
$scope.poll = 0
|
||||
$scope.rowStatus = {}
|
||||
|
||||
$scope.initialise = ->
|
||||
$scope.per_page = 15
|
||||
@@ -69,6 +70,23 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor,
|
||||
$scope.fetchResults()
|
||||
, true
|
||||
|
||||
$scope.capturePayment = (order) ->
|
||||
$scope.rowAction('capture', order)
|
||||
|
||||
$scope.shipOrder = (order) ->
|
||||
$scope.rowAction('ship', order)
|
||||
|
||||
$scope.rowAction = (action, order) ->
|
||||
$scope.rowStatus[order.id] = "loading"
|
||||
|
||||
Orders[action](order).$promise.then (data) ->
|
||||
$scope.rowStatus[order.id] = "success"
|
||||
$timeout(->
|
||||
$scope.rowStatus[order.id] = null
|
||||
, 1500)
|
||||
, (error) ->
|
||||
$scope.rowStatus[order.id] = "error"
|
||||
|
||||
$scope.changePage = (newPage) ->
|
||||
$scope.page = newPage
|
||||
$scope.fetchResults(newPage)
|
||||
|
||||
@@ -5,4 +5,14 @@ angular.module("admin.resources").factory 'OrderResource', ($resource) ->
|
||||
method: 'GET'
|
||||
'update':
|
||||
method: 'PUT'
|
||||
'capture':
|
||||
url: '/api/orders/:id/capture.json'
|
||||
method: 'PUT'
|
||||
params:
|
||||
id: '@id'
|
||||
'ship':
|
||||
url: '/api/orders/:id/ship.json'
|
||||
method: 'PUT'
|
||||
params:
|
||||
id: '@id'
|
||||
})
|
||||
|
||||
@@ -44,5 +44,19 @@ angular.module("admin.resources").factory 'Orders', ($q, OrderResource, RequestM
|
||||
changed.push attr unless attr is "$$hashKey"
|
||||
changed
|
||||
|
||||
capture: (order) ->
|
||||
@processAction('capture', order)
|
||||
|
||||
ship: (order) ->
|
||||
@processAction('ship', order)
|
||||
|
||||
processAction: (action, order) ->
|
||||
OrderResource[action] {id: order.number}, (data) =>
|
||||
if data.id
|
||||
angular.merge(order, data)
|
||||
data
|
||||
, (response) =>
|
||||
response.data
|
||||
|
||||
resetAttribute: (order, attribute) ->
|
||||
order[attribute] = @pristineByID[order.id][attribute]
|
||||
|
||||
@@ -20,4 +20,4 @@ angular.module("admin.subscriptions").controller "ProductsPanelController", ($sc
|
||||
keys = Object.keys(response.data.errors)
|
||||
StatusMessage.display 'failure', response.data.errors[keys[0]][0]
|
||||
else
|
||||
StatusMessage.display 'success', t('js.changes_saved')
|
||||
StatusMessage.display 'failure', t('js.admin.subscriptions.error_saving')
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
.row.exchange-distributed-products
|
||||
.sixteen.columns.alpha.omega
|
||||
.row.exchange-distributed-products{'ng-init' => 'initializeExchangeProductsPanel(exchange)'}
|
||||
.sixteen.columns.alpha.omega{ 'ng-show' => 'enterprises[exchange.enterprise_id].supplied_products.length != 0' }
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_header.html'" }
|
||||
|
||||
.exchange-select-all-variants
|
||||
%label
|
||||
%input{ type: 'checkbox', name: 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants',
|
||||
@@ -7,11 +9,10 @@
|
||||
'ng-model' => 'exchange.select_all_variants',
|
||||
'ng-change' => 'setExchangeVariants(exchange, incomingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)',
|
||||
'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' }
|
||||
{{ 'admin.select_all' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.select_all_products' | t:{ total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
|
||||
.exchange-products
|
||||
-# Scope product list based on permissions the current user has to view variants in this exchange
|
||||
.exchange-product{'ng-repeat' => 'product in supplied_products | filter:productSuppliedToOrderCycle | visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges | orderBy:"name"' }
|
||||
.exchange-products{ 'ng-hide' => 'productsLoading()' }
|
||||
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products | filter:visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges' }
|
||||
.exchange-product-details
|
||||
%label
|
||||
%img{'ng-src' => '{{ product.image_url }}'}
|
||||
@@ -26,3 +27,5 @@
|
||||
'id' => 'order_cycle_outgoing_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
'ng-disabled' => '!order_cycle.editable_variants_for_outgoing_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_outgoing_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' }
|
||||
{{ variant.label }}
|
||||
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_footer.html'" }
|
||||
@@ -0,0 +1,11 @@
|
||||
.pagination{ 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages && !productsLoading()'}
|
||||
.button{ 'ng-click' => 'loadMoreExchangeProducts(exchange)' }
|
||||
{{ 'js.admin.panels.exchange_products.load_more_products' | t }}
|
||||
.button{ 'ng-click' => 'loadAllExchangeProducts(exchange)' }
|
||||
{{ 'js.admin.panels.exchange_products.load_all_products' | t }}
|
||||
|
||||
.sixteen.columns.alpha#loading{ 'ng-show' => 'productsLoading()' }
|
||||
%br
|
||||
%img.spinner{ src: "/assets/spinning-circles.svg" }
|
||||
%h1
|
||||
{{ 'js.admin.panels.exchange_products.loading_products' | t }}
|
||||
@@ -0,0 +1,5 @@
|
||||
.exchange-load-all-variants
|
||||
%div
|
||||
{{ 'js.admin.panels.exchange_products.products_loaded' | t:{ num_of_products_loaded: enterprises[exchange.enterprise_id].supplied_products.length, total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
%a{ 'ng-click' => 'loadAllExchangeProducts(exchange)', 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages' }
|
||||
{{ 'js.admin.panels.exchange_products.load_all_products' | t }}
|
||||
@@ -0,0 +1,12 @@
|
||||
.row.exchange-supplied-products
|
||||
.sixteen.columns.alpha.omega
|
||||
.exchange-select-all-variants
|
||||
%label
|
||||
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants',
|
||||
value: 1,
|
||||
'ng-model' => 'exchange.select_all_variants',
|
||||
'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)',
|
||||
'id' => 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants' }
|
||||
{{ 'admin.select_all' | t }}
|
||||
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_supplied_list.html'" }
|
||||
@@ -0,0 +1,16 @@
|
||||
.row.exchange-supplied-products{'ng-init' => 'initializeExchangeProductsPanel(exchange)'}
|
||||
.sixteen.columns.alpha.omega{ 'ng-show' => 'enterprises[exchange.enterprise_id].supplied_products.length != 0' }
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_header.html'" }
|
||||
|
||||
.exchange-select-all-variants
|
||||
%label
|
||||
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants',
|
||||
value: 1,
|
||||
'ng-model' => 'exchange.select_all_variants',
|
||||
'ng-change' => 'selectAllVariants(exchange, exchange.select_all_variants)',
|
||||
'id' => 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants' }
|
||||
{{ 'js.admin.panels.exchange_products.select_all_products' | t:{ total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_supplied_list.html'" }
|
||||
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_panel_footer.html'" }
|
||||
@@ -0,0 +1,16 @@
|
||||
.exchange-products{ 'ng-hide' => 'productsLoading()' }
|
||||
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'}
|
||||
.exchange-product-details
|
||||
%label
|
||||
%img{'ng-src' => '{{ product.image_url }}'}
|
||||
{{ product.name }}
|
||||
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants'}
|
||||
%label
|
||||
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
value: 1,
|
||||
'ng-model' => 'exchange.variants[variant.id]',
|
||||
'ofn-sync-distributions' => '{{ variant.id }}',
|
||||
'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' }
|
||||
{{ variant.label }}
|
||||
@@ -1,29 +0,0 @@
|
||||
.row.exchange-supplied-products
|
||||
.sixteen.columns.alpha.omega
|
||||
.exchange-select-all-variants
|
||||
%label
|
||||
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants',
|
||||
value: 1,
|
||||
'ng-model' => 'exchange.select_all_variants',
|
||||
'ng-change' => 'setExchangeVariants(exchange, suppliedVariants(exchange.enterprise_id), exchange.select_all_variants)',
|
||||
'id' => 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants' }
|
||||
{{ 'admin.select_all' | t }}
|
||||
|
||||
.exchange-products
|
||||
-# No need to scope product list based on permissions, because if an incoming exchange is visible,
|
||||
-# then all of the variants within it should be visible. May change in the future?
|
||||
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products'}
|
||||
.exchange-product-details
|
||||
%label
|
||||
%img{'ng-src' => '{{ product.image_url }}'}
|
||||
{{ product.name }}
|
||||
|
||||
.exchange-product-variant{'ng-repeat' => 'variant in product.variants'}
|
||||
%label
|
||||
%input{ type: 'checkbox', name: 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
value: 1,
|
||||
'ng-model' => 'exchange.variants[variant.id]',
|
||||
'ofn-sync-distributions' => '{{ variant.id }}',
|
||||
'id' => 'order_cycle_incoming_exchange_{{ $parent.$parent.$index }}_variants_{{ variant.id }}',
|
||||
'ng-disabled' => '!order_cycle.editable_variants_for_incoming_exchanges.hasOwnProperty(exchange.enterprise_id) || order_cycle.editable_variants_for_incoming_exchanges[exchange.enterprise_id].indexOf(variant.id) < 0' }
|
||||
{{ variant.label }}
|
||||
@@ -1,9 +1,9 @@
|
||||
#save-bar.animate-show{ ng: { show: 'dirty || persist || StatusMessage.active()' } }
|
||||
.container
|
||||
.eight.columns.alpha
|
||||
.seven.columns.alpha
|
||||
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage == ''", style: 'StatusMessage.statusMessage.style' } }
|
||||
{{ StatusMessage.statusMessage.text || " " }}
|
||||
%h5#status-message{ ng: { show: "StatusMessage.invalidMessage !== ''" }, style: 'color: #da5354' }
|
||||
{{ StatusMessage.invalidMessage || " " }}
|
||||
.eight.columns.omega.text-right{ ng: { transclude: true } }
|
||||
.nine.columns.omega.text-right{ ng: { transclude: true } }
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
@import '../variables';
|
||||
|
||||
.row-loading {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.row-loading-icons {
|
||||
margin-left: 3em;
|
||||
position: absolute;
|
||||
|
||||
.spinner {
|
||||
border: 0;
|
||||
width: 2.3em;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 2.3em;
|
||||
opacity: .75;
|
||||
|
||||
&::before {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&.success {
|
||||
color: $spree-green;
|
||||
}
|
||||
|
||||
&.error {
|
||||
color: $warning-red;
|
||||
}
|
||||
}
|
||||
}
|
||||
4
app/assets/stylesheets/admin/components/tooltip.css.scss
Normal file
4
app/assets/stylesheets/admin/components/tooltip.css.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
#powerTip {
|
||||
max-width: 240px;
|
||||
white-space: normal;
|
||||
}
|
||||
@@ -19,6 +19,9 @@ ul.wizard-progress {
|
||||
line-height: 30px;
|
||||
padding: 0 25px 0 40px;
|
||||
position: relative;
|
||||
a {
|
||||
color: #494949;
|
||||
}
|
||||
&:first-child {
|
||||
padding-left: 25px;
|
||||
border-top-left-radius: 3px;
|
||||
@@ -62,6 +65,9 @@ ul.wizard-progress {
|
||||
&.current {
|
||||
background-color: $color_selected;
|
||||
color: #fff;
|
||||
a {
|
||||
color: #fff;
|
||||
}
|
||||
&:after {
|
||||
background-color: $color_selected;
|
||||
}
|
||||
|
||||
@@ -104,9 +104,13 @@ form.order_cycle {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.exchange-load-all-variants {
|
||||
margin: 0px 5px 20px 5px;
|
||||
}
|
||||
|
||||
.exchange-select-all-variants {
|
||||
clear: both;
|
||||
margin: 5px;
|
||||
margin: 15px 5px 25px 5px;
|
||||
}
|
||||
|
||||
.exchange-products {
|
||||
@@ -209,6 +213,7 @@ table#listing_enterprise_groups {
|
||||
#loading {
|
||||
text-align: center;
|
||||
img.spinner {
|
||||
border: 0px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,18 @@ module Admin
|
||||
#
|
||||
def index
|
||||
order_params = params[:q].andand.delete :order
|
||||
orders = OpenFoodNetwork::Permissions.new(spree_current_user).editable_orders.ransack(order_params).result
|
||||
line_items = OpenFoodNetwork::Permissions.new(spree_current_user).editable_line_items.where(order_id: orders).ransack(params[:q])
|
||||
render_as_json line_items.result.reorder('order_id ASC, id ASC')
|
||||
|
||||
order_permissions = ::Permissions::Order.new(spree_current_user)
|
||||
orders = order_permissions.
|
||||
editable_orders.ransack(order_params).result
|
||||
|
||||
line_items = order_permissions.
|
||||
editable_line_items.where(order_id: orders).
|
||||
includes(variant: { option_values: :option_type }).
|
||||
ransack(params[:q]).result.
|
||||
reorder('spree_line_items.order_id ASC, spree_line_items.id ASC')
|
||||
|
||||
render_as_json line_items
|
||||
end
|
||||
|
||||
# PUT /admin/bulk_line_items/:id.json
|
||||
|
||||
@@ -28,15 +28,12 @@ module Admin
|
||||
|
||||
def bulk_update
|
||||
@enterprise_fee_set = EnterpriseFeeSet.new(params[:enterprise_fee_set])
|
||||
if @enterprise_fee_set.save
|
||||
redirect_path = main_app.admin_enterprise_fees_path
|
||||
if params.key? :enterprise_id
|
||||
redirect_path = main_app.admin_enterprise_fees_path(enterprise_id: params[:enterprise_id])
|
||||
end
|
||||
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
|
||||
|
||||
if @enterprise_fee_set.save
|
||||
redirect_to redirect_path, notice: I18n.t(:enterprise_fees_update_notice)
|
||||
else
|
||||
render :index
|
||||
redirect_to redirect_path,
|
||||
flash: { error: @enterprise_fee_set.errors.full_messages.to_sentence }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -73,5 +70,13 @@ module Admin
|
||||
def current_enterprise
|
||||
Enterprise.find params[:enterprise_id] if params.key? :enterprise_id
|
||||
end
|
||||
|
||||
def redirect_path
|
||||
if params.key? :enterprise_id
|
||||
return main_app.admin_enterprise_fees_path(enterprise_id: params[:enterprise_id])
|
||||
end
|
||||
|
||||
main_app.admin_enterprise_fees_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,7 +66,7 @@ module Admin
|
||||
|
||||
if @enterprise.update_attributes(attributes)
|
||||
flash[:success] = I18n.t(:enterprise_register_success_notice, enterprise: @enterprise.name)
|
||||
redirect_to admin_path
|
||||
redirect_to admin_dashboard_path
|
||||
else
|
||||
flash[:error] = I18n.t(:enterprise_register_error, enterprise: @enterprise.name)
|
||||
render :welcome, layout: "spree/layouts/bare_admin"
|
||||
@@ -148,7 +148,8 @@ module Admin
|
||||
|
||||
unless enterprises.empty?
|
||||
enterprises.includes(
|
||||
supplied_products: [:supplier, :variants, master: [:images]]
|
||||
supplied_products:
|
||||
[:supplier, master: [:images], variants: { option_values: :option_type }]
|
||||
)
|
||||
end
|
||||
when :index
|
||||
|
||||
@@ -2,6 +2,7 @@ module Admin
|
||||
class OrderCyclesController < ResourceController
|
||||
include OrderCyclesHelper
|
||||
|
||||
prepend_before_filter :set_order_cycle_id, only: [:incoming, :outgoing]
|
||||
before_filter :load_data_for_index, only: :index
|
||||
before_filter :require_coordinator, only: :new
|
||||
before_filter :remove_protected_attrs, only: [:update]
|
||||
@@ -12,9 +13,10 @@ module Admin
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
render_as_json @collection, ams_prefix: params[:ams_prefix],
|
||||
current_user: spree_current_user,
|
||||
subscriptions_count: SubscriptionsCount.new(@collection)
|
||||
render_as_json @collection,
|
||||
ams_prefix: params[:ams_prefix],
|
||||
current_user: spree_current_user,
|
||||
subscriptions_count: SubscriptionsCount.new(@collection)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -42,12 +44,17 @@ module Admin
|
||||
|
||||
if @order_cycle_form.save
|
||||
flash[:notice] = I18n.t(:order_cycles_create_notice)
|
||||
render json: { success: true }
|
||||
render json: { success: true,
|
||||
edit_path: main_app.admin_order_cycle_incoming_path(@order_cycle) }
|
||||
else
|
||||
render json: { errors: @order_cycle.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def set_order_cycle_id
|
||||
params[:id] = params[:order_cycle_id]
|
||||
end
|
||||
|
||||
def update
|
||||
@order_cycle_form = OrderCycleForm.new(@order_cycle, params, spree_current_user)
|
||||
|
||||
@@ -64,9 +71,10 @@ module Admin
|
||||
|
||||
def bulk_update
|
||||
if order_cycle_set.andand.save
|
||||
render_as_json @order_cycles, ams_prefix: 'index',
|
||||
current_user: spree_current_user,
|
||||
subscriptions_count: SubscriptionsCount.new(@collection)
|
||||
render_as_json @order_cycles,
|
||||
ams_prefix: 'index',
|
||||
current_user: spree_current_user,
|
||||
subscriptions_count: SubscriptionsCount.new(@collection)
|
||||
else
|
||||
order_cycle = order_cycle_set.collection.find{ |oc| oc.errors.present? }
|
||||
render json: { errors: order_cycle.errors.full_messages }, status: :unprocessable_entity
|
||||
@@ -94,20 +102,8 @@ module Admin
|
||||
return Enterprise.where("1=0") unless json_request?
|
||||
return order_cycles_from_set if params[:order_cycle_set]
|
||||
|
||||
ocs = if params[:as] == "distributor"
|
||||
OrderCycle.preload(:schedules).ransack(params[:q]).result.
|
||||
involving_managed_distributors_of(spree_current_user).order('updated_at DESC')
|
||||
elsif params[:as] == "producer"
|
||||
OrderCycle.preload(:schedules).ransack(params[:q]).result.
|
||||
involving_managed_producers_of(spree_current_user).order('updated_at DESC')
|
||||
else
|
||||
OrderCycle.preload(:schedules).ransack(params[:q]).result.accessible_by(spree_current_user)
|
||||
end
|
||||
|
||||
ocs.undated |
|
||||
ocs.soonest_closing |
|
||||
ocs.soonest_opening |
|
||||
ocs.closed
|
||||
ocs = order_cycles
|
||||
ocs.undated | ocs.soonest_closing | ocs.soonest_opening | ocs.closed
|
||||
end
|
||||
|
||||
def collection_actions
|
||||
@@ -116,21 +112,60 @@ module Admin
|
||||
|
||||
private
|
||||
|
||||
def order_cycles
|
||||
if params[:as] == "distributor"
|
||||
order_cycles_as_distributor
|
||||
elsif params[:as] == "producer"
|
||||
order_cycles_as_producer
|
||||
else
|
||||
order_cycles_as_both
|
||||
end
|
||||
end
|
||||
|
||||
def order_cycles_as_distributor
|
||||
OrderCycle.
|
||||
preload(:schedules).
|
||||
ransack(params[:q]).
|
||||
result.
|
||||
involving_managed_distributors_of(spree_current_user).
|
||||
order('updated_at DESC')
|
||||
end
|
||||
|
||||
def order_cycles_as_producer
|
||||
OrderCycle.
|
||||
preload(:schedules).
|
||||
ransack(params[:q]).
|
||||
result.
|
||||
involving_managed_producers_of(spree_current_user).
|
||||
order('updated_at DESC')
|
||||
end
|
||||
|
||||
def order_cycles_as_both
|
||||
OrderCycle.
|
||||
preload(:schedules).
|
||||
ransack(params[:q]).
|
||||
result.
|
||||
accessible_by(spree_current_user)
|
||||
end
|
||||
|
||||
def load_data_for_index
|
||||
if json_request?
|
||||
# Split ransack params into all those that currently exist and new ones to limit returned ocs to recent or undated
|
||||
# Split ransack params into all those that currently exist and new ones
|
||||
# to limit returned ocs to recent or undated
|
||||
orders_close_at_gt = params[:q].andand.delete(:orders_close_at_gt) || 31.days.ago
|
||||
params[:q] = {
|
||||
g: [params.delete(:q) || {}, { m: 'or', orders_close_at_gt: orders_close_at_gt, orders_close_at_null: true }]
|
||||
g: [params.delete(:q) || {}, { m: 'or',
|
||||
orders_close_at_gt: orders_close_at_gt,
|
||||
orders_close_at_null: true }]
|
||||
}
|
||||
@collection = collection
|
||||
end
|
||||
end
|
||||
|
||||
def require_coordinator
|
||||
if params[:coordinator_id] && @order_cycle.coordinator = permitted_coordinating_enterprises_for(@order_cycle).find_by_id(params[:coordinator_id])
|
||||
return
|
||||
end
|
||||
@order_cycle.coordinator =
|
||||
permitted_coordinating_enterprises_for(@order_cycle).find_by_id(params[:coordinator_id])
|
||||
return if params[:coordinator_id] && @order_cycle.coordinator
|
||||
|
||||
available_coordinators = permitted_coordinating_enterprises_for(@order_cycle)
|
||||
case available_coordinators.count
|
||||
@@ -140,7 +175,9 @@ module Admin
|
||||
when 1
|
||||
@order_cycle.coordinator = available_coordinators.first
|
||||
else
|
||||
flash[:error] = I18n.t(:order_cycles_no_permission_to_create_error) if params[:coordinator_id]
|
||||
if params[:coordinator_id]
|
||||
flash[:error] = I18n.t(:order_cycles_no_permission_to_create_error)
|
||||
end
|
||||
render :set_coordinator
|
||||
end
|
||||
end
|
||||
@@ -164,7 +201,9 @@ module Admin
|
||||
params[:order_cycle].delete :coordinator_id
|
||||
|
||||
unless Enterprise.managed_by(spree_current_user).include?(@order_cycle.coordinator)
|
||||
params[:order_cycle].delete_if{ |k, _v| [:name, :orders_open_at, :orders_close_at].include? k.to_sym }
|
||||
params[:order_cycle].delete_if do |k, _v|
|
||||
[:name, :orders_open_at, :orders_close_at].include? k.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -189,7 +228,8 @@ module Admin
|
||||
def require_order_cycle_set_params
|
||||
return if params[:order_cycle_set]
|
||||
|
||||
render json: { errors: t('admin.order_cycles.bulk_update.no_data') }, status: :unprocessable_entity
|
||||
render json: { errors: t('admin.order_cycles.bulk_update.no_data') },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def ams_prefix_whitelist
|
||||
|
||||
@@ -22,7 +22,7 @@ module Admin
|
||||
redirect_to main_app.edit_admin_enterprise_path(stripe_account.enterprise)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
flash[:error] = "Failed to disconnect Stripe."
|
||||
redirect_to spree.admin_path
|
||||
redirect_to spree.admin_dashboard_path
|
||||
end
|
||||
|
||||
def status
|
||||
|
||||
92
app/controllers/api/exchange_products_controller.rb
Normal file
92
app/controllers/api/exchange_products_controller.rb
Normal file
@@ -0,0 +1,92 @@
|
||||
# This controller lists products that can be added to an exchange
|
||||
module Api
|
||||
class ExchangeProductsController < Api::BaseController
|
||||
DEFAULT_PAGE = 1
|
||||
DEFAULT_PER_PAGE = 100
|
||||
|
||||
skip_authorization_check only: [:index]
|
||||
|
||||
# If exchange_id is present in the URL:
|
||||
# Lists Products that can be added to that Exchange
|
||||
#
|
||||
# If exchange_id is not present in the URL:
|
||||
# Lists Products of the Enterprise given that can be added to the given Order Cycle
|
||||
# In this case parameters are: enterprise_id, order_cycle_id and incoming
|
||||
# (order_cycle_id is not necessary for incoming exchanges)
|
||||
def index
|
||||
if params[:exchange_id].present?
|
||||
load_data_from_exchange
|
||||
else
|
||||
load_data_from_other_params
|
||||
end
|
||||
|
||||
render_variant_count && return if params[:action_name] == "variant_count"
|
||||
|
||||
render_paginated_products paginated_products
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_variant_count
|
||||
render text: {
|
||||
count: Spree::Variant.
|
||||
not_master.
|
||||
where(product_id: products).
|
||||
count
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def products
|
||||
ExchangeProductsRenderer.
|
||||
new(@order_cycle, spree_current_user).
|
||||
exchange_products(@incoming, @enterprise)
|
||||
end
|
||||
|
||||
def paginated_products
|
||||
products.
|
||||
page(params[:page] || DEFAULT_PAGE).
|
||||
per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
end
|
||||
|
||||
def load_data_from_exchange
|
||||
exchange = Exchange.find_by_id(params[:exchange_id])
|
||||
|
||||
@order_cycle = exchange.order_cycle
|
||||
@incoming = exchange.incoming
|
||||
@enterprise = exchange.sender
|
||||
end
|
||||
|
||||
def load_data_from_other_params
|
||||
@enterprise = Enterprise.find_by_id(params[:enterprise_id])
|
||||
|
||||
if params[:order_cycle_id]
|
||||
@order_cycle = OrderCycle.find_by_id(params[:order_cycle_id])
|
||||
elsif !params[:incoming]
|
||||
raise "order_cycle_id is required to list products for new outgoing exchange"
|
||||
end
|
||||
@incoming = params[:incoming]
|
||||
end
|
||||
|
||||
def render_paginated_products(paginated_products)
|
||||
serializer = ActiveModel::ArraySerializer.new(
|
||||
paginated_products,
|
||||
each_serializer: Api::Admin::ForOrderCycle::SuppliedProductSerializer,
|
||||
order_cycle: @order_cycle
|
||||
)
|
||||
|
||||
render text: {
|
||||
products: serializer,
|
||||
pagination: pagination_data(paginated_products)
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def pagination_data(paginated_products)
|
||||
{
|
||||
results: paginated_products.total_count,
|
||||
pages: paginated_products.num_pages,
|
||||
page: (params[:page] || DEFAULT_PAGE).to_i,
|
||||
per_page: (params[:per_page] || DEFAULT_PER_PAGE).to_i
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -16,8 +16,38 @@ module Api
|
||||
}
|
||||
end
|
||||
|
||||
def ship
|
||||
authorize! :admin, order
|
||||
|
||||
if order.ship
|
||||
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
|
||||
else
|
||||
render json: { error: I18n.t('api.orders.failed_to_update') }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def capture
|
||||
authorize! :admin, order
|
||||
|
||||
pending_payment = order.pending_payments.first
|
||||
|
||||
return payment_capture_failed unless order.payment_required? && pending_payment
|
||||
|
||||
if pending_payment.capture!
|
||||
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
|
||||
else
|
||||
payment_capture_failed
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
error_during_processing(e)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def payment_capture_failed
|
||||
render json: { error: t(:payment_processing_failed) }, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def serialized_orders(orders)
|
||||
ActiveModel::ArraySerializer.new(
|
||||
orders,
|
||||
|
||||
@@ -42,8 +42,8 @@ module Api
|
||||
def destroy
|
||||
authorize! :delete, Spree::Product
|
||||
@product = find_product(params[:id])
|
||||
@product.update_attribute(:deleted_at, Time.zone.now)
|
||||
@product.variants_including_master.update_all(deleted_at: Time.zone.now)
|
||||
authorize! :delete, @product
|
||||
@product.destroy
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :no_content
|
||||
end
|
||||
|
||||
@@ -71,14 +71,6 @@ module Api
|
||||
render_paged_products @products
|
||||
end
|
||||
|
||||
def soft_delete
|
||||
authorize! :delete, Spree::Product
|
||||
@product = find_product(params[:product_id])
|
||||
authorize! :delete, @product
|
||||
@product.destroy
|
||||
render json: @product, serializer: Api::Admin::ProductSerializer, status: :no_content
|
||||
end
|
||||
|
||||
# POST /api/products/:product_id/clone
|
||||
#
|
||||
def clone
|
||||
@@ -115,7 +107,8 @@ module Api
|
||||
def product_query_includes
|
||||
[
|
||||
master: [:images],
|
||||
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides]
|
||||
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides,
|
||||
{ option_values: :option_type }]
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ module Api
|
||||
before_filter :product
|
||||
|
||||
def index
|
||||
@variants = scope.includes(:option_values).ransack(params[:q]).result
|
||||
@variants = scope.includes(option_values: :option_type).ransack(params[:q]).result
|
||||
render json: @variants, each_serializer: Api::VariantSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
@variant = scope.includes(:option_values).find(params[:id])
|
||||
@variant = scope.includes(option_values: :option_type).find(params[:id])
|
||||
render json: @variant, serializer: Api::VariantSerializer
|
||||
end
|
||||
|
||||
@@ -35,18 +35,12 @@ module Api
|
||||
end
|
||||
end
|
||||
|
||||
def soft_delete
|
||||
@variant = scope.find(params[:variant_id])
|
||||
authorize! :delete, @variant
|
||||
|
||||
VariantDeleter.new.delete(@variant)
|
||||
render json: @variant, serializer: Api::VariantSerializer, status: :no_content
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize! :delete, Spree::Variant
|
||||
@variant = scope.find(params[:id])
|
||||
@variant.destroy
|
||||
authorize! :delete, @variant
|
||||
|
||||
VariantDeleter.new.delete(@variant)
|
||||
render json: @variant, serializer: Api::VariantSerializer, status: :no_content
|
||||
end
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ require 'open_food_network/address_finder'
|
||||
class CheckoutController < Spree::CheckoutController
|
||||
layout 'darkswarm'
|
||||
|
||||
# We need pessimistic locking to avoid race conditions.
|
||||
# Otherwise we fail on duplicate indexes or end up with negative stock.
|
||||
prepend_around_filter CurrentOrderLocker, only: :update
|
||||
|
||||
prepend_before_filter :check_hub_ready_for_checkout
|
||||
prepend_before_filter :check_order_cycle_expiry
|
||||
prepend_before_filter :require_order_cycle
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
module Spree
|
||||
module Admin
|
||||
AdjustmentsController.class_eval do
|
||||
class AdjustmentsController < ResourceController
|
||||
belongs_to 'spree/order', find_by: :number
|
||||
destroy.after :reload_order
|
||||
|
||||
prepend_before_filter :set_included_tax, only: [:create, :update]
|
||||
before_filter :set_default_tax_rate, only: :edit
|
||||
before_filter :enable_updates, only: :update
|
||||
|
||||
private
|
||||
|
||||
def reload_order
|
||||
@order.reload
|
||||
end
|
||||
|
||||
def collection
|
||||
parent.adjustments.eligible
|
||||
end
|
||||
|
||||
# Choose a default tax rate to show on the edit form. The adjustment stores its included
|
||||
# tax in dollars, but doesn't store the source of the tax (ie. TaxRate that generated it).
|
||||
# We guess which tax rate here, choosing:
|
||||
@@ -15,28 +26,34 @@ module Spree
|
||||
# When we have to go with 2, we show an error message to ask the admin to check that the
|
||||
# correct tax is being applied.
|
||||
def set_default_tax_rate
|
||||
if @adjustment.included_tax > 0
|
||||
trs = TaxRate.match(@order)
|
||||
tr_yielding_matching_tax = trs.select { |tr| tr.compute_tax(@adjustment.amount) == @adjustment.included_tax }.first.andand.id
|
||||
tr_valid_for_order = TaxRate.match(@order).first.andand.id
|
||||
return if @adjustment.included_tax <= 0
|
||||
|
||||
@tax_rate_id = tr_yielding_matching_tax || tr_valid_for_order
|
||||
tax_rates = TaxRate.match(@order)
|
||||
tax_rate_with_matching_tax = find_tax_rate_with_matching_tax(tax_rates)
|
||||
tax_rate_valid_for_order = tax_rates.first.andand.id
|
||||
|
||||
if tr_yielding_matching_tax.nil?
|
||||
@adjustment.errors.add :tax_rate_id, I18n.t(:adjustments_tax_rate_error)
|
||||
end
|
||||
@tax_rate_id = tax_rate_with_matching_tax || tax_rate_valid_for_order
|
||||
|
||||
return unless tax_rate_with_matching_tax.nil?
|
||||
|
||||
@adjustment.errors.add :tax_rate_id, I18n.t(:adjustments_tax_rate_error)
|
||||
end
|
||||
|
||||
def find_tax_rate_with_matching_tax(tax_rates)
|
||||
tax_rates_yielding_matching_tax = tax_rates.select do |tr|
|
||||
tr.compute_tax(@adjustment.amount) == @adjustment.included_tax
|
||||
end
|
||||
tax_rates_yielding_matching_tax.first.andand.id
|
||||
end
|
||||
|
||||
def set_included_tax
|
||||
included_tax = 0
|
||||
if params[:tax_rate_id].present?
|
||||
tax_rate = TaxRate.find params[:tax_rate_id]
|
||||
amount = params[:adjustment][:amount].to_f
|
||||
params[:adjustment][:included_tax] = tax_rate.compute_tax amount
|
||||
|
||||
else
|
||||
params[:adjustment][:included_tax] = 0
|
||||
included_tax = tax_rate.compute_tax amount
|
||||
end
|
||||
params[:adjustment][:included_tax] = included_tax
|
||||
end
|
||||
|
||||
# Spree 2.0 keeps shipping fee adjustments open unless they are manually
|
||||
142
app/controllers/spree/admin/base_controller.rb
Normal file
142
app/controllers/spree/admin/base_controller.rb
Normal file
@@ -0,0 +1,142 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class BaseController < Spree::BaseController
|
||||
ssl_required
|
||||
|
||||
helper 'spree/admin/navigation'
|
||||
layout '/spree/layouts/admin'
|
||||
|
||||
include I18nHelper
|
||||
|
||||
before_filter :authorize_admin
|
||||
before_filter :set_locale
|
||||
before_filter :warn_invalid_order_cycles, if: :html_request?
|
||||
|
||||
# Warn the user when they have an active order cycle with hubs that are not ready
|
||||
# for checkout (ie. does not have valid shipping and payment methods).
|
||||
def warn_invalid_order_cycles
|
||||
distributors = active_distributors_not_ready_for_checkout
|
||||
|
||||
return if distributors.empty? || flash[:notice].present?
|
||||
|
||||
flash[:notice] = active_distributors_not_ready_for_checkout_message(distributors)
|
||||
end
|
||||
|
||||
# This is in Spree::Core::ControllerHelpers::Auth
|
||||
# But you can't easily reopen modules in Ruby
|
||||
def unauthorized
|
||||
if try_spree_current_user
|
||||
flash[:error] = t(:authorization_failure)
|
||||
redirect_to '/unauthorized'
|
||||
else
|
||||
store_location
|
||||
redirect_to root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def model_class
|
||||
const_name = controller_name.classify
|
||||
return "Spree::#{const_name}".constantize if Spree.const_defined?(const_name)
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def action
|
||||
params[:action].to_sym
|
||||
end
|
||||
|
||||
def authorize_admin
|
||||
if respond_to?(:model_class, true) && model_class
|
||||
record = model_class
|
||||
else
|
||||
# This allows specificity for each non-resource controller
|
||||
# (to be consistent with "authorize_resource :class => false", see https://github.com/ryanb/cancan/blob/60cf6a67ef59c0c9b63bc27ea0101125c4193ea6/lib/cancan/controller_resource.rb#L146)
|
||||
record = self.class.to_s.
|
||||
sub("Controller", "").
|
||||
underscore.split('/').last.singularize.to_sym
|
||||
end
|
||||
authorize! :admin, record
|
||||
authorize! resource_authorize_action, record
|
||||
end
|
||||
|
||||
def resource_authorize_action
|
||||
action
|
||||
end
|
||||
|
||||
def flash_message_for(object, event_sym)
|
||||
resource_desc = object.class.model_name.human
|
||||
resource_desc += " \"#{object.name}\"" if object.respond_to?(:name) && object.name.present?
|
||||
Spree.t(event_sym, resource: resource_desc)
|
||||
end
|
||||
|
||||
def render_js_for_destroy
|
||||
render partial: '/spree/admin/shared/destroy'
|
||||
end
|
||||
|
||||
# Index request for JSON needs to pass a CSRF token in order to prevent JSON Hijacking
|
||||
def check_json_authenticity
|
||||
return unless request.format.js? || request.format.json?
|
||||
|
||||
return unless protect_against_forgery?
|
||||
|
||||
auth_token = params[request_forgery_protection_token]
|
||||
return if auth_token && form_authenticity_token == CGI.unescape(auth_token)
|
||||
|
||||
raise(ActionController::InvalidAuthenticityToken)
|
||||
end
|
||||
|
||||
def config_locale
|
||||
Spree::Backend::Config[:locale]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def active_distributors_not_ready_for_checkout
|
||||
ocs = OrderCycle.managed_by(spree_current_user).active
|
||||
distributors = ocs.includes(:distributors).map(&:distributors).flatten.uniq
|
||||
Enterprise.where('enterprises.id IN (?)', distributors).not_ready_for_checkout
|
||||
end
|
||||
|
||||
def active_distributors_not_ready_for_checkout_message(distributors)
|
||||
distributor_names = distributors.map(&:name).join ', '
|
||||
|
||||
if distributors.count > 1
|
||||
I18n.t(:active_distributors_not_ready_for_checkout_message_plural,
|
||||
distributor_names: distributor_names)
|
||||
else
|
||||
I18n.t(:active_distributors_not_ready_for_checkout_message_singular,
|
||||
distributor_names: distributor_names)
|
||||
end
|
||||
end
|
||||
|
||||
def html_request?
|
||||
request.format.html?
|
||||
end
|
||||
|
||||
def json_request?
|
||||
request.format.json?
|
||||
end
|
||||
|
||||
def render_as_json(data, options = {})
|
||||
ams_prefix = options.delete :ams_prefix
|
||||
if [Array, ActiveRecord::Relation].include? data.class
|
||||
render options.merge(json: data, each_serializer: serializer(ams_prefix))
|
||||
else
|
||||
render options.merge(json: data, serializer: serializer(ams_prefix))
|
||||
end
|
||||
end
|
||||
|
||||
def serializer(ams_prefix)
|
||||
unless ams_prefix.nil? || ams_prefix_whitelist.include?(ams_prefix.to_sym)
|
||||
raise "Suffix '#{ams_prefix}' not found in ams_prefix_whitelist for #{self.class.name}."
|
||||
end
|
||||
|
||||
prefix = ams_prefix.andand.classify || ""
|
||||
name = controller_name.classify
|
||||
"::Api::Admin::#{prefix}#{name}Serializer".constantize
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,105 +0,0 @@
|
||||
require 'spree/core/controller_helpers/respond_with_decorator'
|
||||
|
||||
Spree::Admin::BaseController.class_eval do
|
||||
include I18nHelper
|
||||
|
||||
layout 'spree/layouts/admin'
|
||||
|
||||
before_filter :set_locale
|
||||
before_filter :warn_invalid_order_cycles, if: :html_request?
|
||||
|
||||
# Warn the user when they have an active order cycle with hubs that are not ready
|
||||
# for checkout (ie. does not have valid shipping and payment methods).
|
||||
def warn_invalid_order_cycles
|
||||
distributors = active_distributors_not_ready_for_checkout
|
||||
|
||||
if distributors.any? && flash[:notice].nil?
|
||||
flash[:notice] = active_distributors_not_ready_for_checkout_message(distributors)
|
||||
end
|
||||
end
|
||||
|
||||
# Override Spree method
|
||||
# It's a shame Spree doesn't just let CanCan handle this in it's own way
|
||||
def authorize_admin
|
||||
if respond_to?(:model_class, true) && model_class
|
||||
record = model_class
|
||||
else
|
||||
# this line changed to allow specificity for each non-resource controller (to be consistent with "authorize_resource :class => false", see https://github.com/ryanb/cancan/blob/60cf6a67ef59c0c9b63bc27ea0101125c4193ea6/lib/cancan/controller_resource.rb#L146)
|
||||
record = self.class.to_s.sub("Controller", "").underscore.split('/').last.singularize.to_sym
|
||||
end
|
||||
authorize! :admin, record
|
||||
authorize! resource_authorize_action, record
|
||||
end
|
||||
|
||||
def resource_authorize_action
|
||||
action
|
||||
end
|
||||
|
||||
# This is in Spree::Core::ControllerHelpers::Auth
|
||||
# But you can't easily reopen modules in Ruby
|
||||
def unauthorized
|
||||
if try_spree_current_user
|
||||
flash[:error] = t(:authorization_failure)
|
||||
redirect_to '/unauthorized'
|
||||
else
|
||||
store_location
|
||||
redirect_to root_path(anchor: "login?after_login=#{request.env['PATH_INFO']}")
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def model_class
|
||||
const_name = controller_name.classify
|
||||
if Spree.const_defined?(const_name)
|
||||
return "Spree::#{const_name}".constantize
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def active_distributors_not_ready_for_checkout
|
||||
ocs = OrderCycle.managed_by(spree_current_user).active
|
||||
distributors = ocs.includes(:distributors).map(&:distributors).flatten.uniq
|
||||
Enterprise.where('enterprises.id IN (?)', distributors).not_ready_for_checkout
|
||||
end
|
||||
|
||||
def active_distributors_not_ready_for_checkout_message(distributors)
|
||||
distributor_names = distributors.map(&:name).join ', '
|
||||
|
||||
if distributors.count > 1
|
||||
I18n.t(:active_distributors_not_ready_for_checkout_message_plural, distributor_names: distributor_names)
|
||||
else
|
||||
I18n.t(:active_distributors_not_ready_for_checkout_message_singular, distributor_names: distributor_names)
|
||||
end
|
||||
end
|
||||
|
||||
def html_request?
|
||||
request.format.html?
|
||||
end
|
||||
|
||||
def json_request?
|
||||
request.format.json?
|
||||
end
|
||||
|
||||
def render_as_json(data, options = {})
|
||||
ams_prefix = options.delete :ams_prefix
|
||||
if [Array, ActiveRecord::Relation].include? data.class
|
||||
render options.merge(json: data, each_serializer: serializer(ams_prefix))
|
||||
else
|
||||
render options.merge(json: data, serializer: serializer(ams_prefix))
|
||||
end
|
||||
end
|
||||
|
||||
def serializer(ams_prefix)
|
||||
if ams_prefix.nil? || ams_prefix_whitelist.include?(ams_prefix.to_sym)
|
||||
prefix = ams_prefix.andand.classify || ""
|
||||
name = controller_name.classify
|
||||
"Api::Admin::#{prefix}#{name}Serializer".constantize
|
||||
else
|
||||
raise "Suffix '#{ams_prefix}' not found in ams_prefix_whitelist for #{self.class.name}."
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,8 +5,7 @@ module Spree
|
||||
@preferences_general = [:site_name, :default_seo_title, :default_meta_keywords,
|
||||
:default_meta_description, :site_url, :bugherd_api_key]
|
||||
@preferences_security = [:allow_ssl_in_production,
|
||||
:allow_ssl_in_staging, :allow_ssl_in_development_and_test,
|
||||
:check_for_spree_alerts]
|
||||
:allow_ssl_in_staging, :allow_ssl_in_development_and_test]
|
||||
@preferences_currency = [:display_currency, :hide_cents]
|
||||
end
|
||||
|
||||
@@ -20,18 +19,6 @@ module Spree
|
||||
|
||||
redirect_to edit_admin_general_settings_path
|
||||
end
|
||||
|
||||
def dismiss_alert
|
||||
return unless request.xhr? && params[:alert_id]
|
||||
|
||||
dismissed = Spree::Config[:dismissed_spree_alerts] || ''
|
||||
Spree::Config.set(dismissed_spree_alerts: dismissed.
|
||||
split(',').
|
||||
push(params[:alert_id]).
|
||||
join(','))
|
||||
filter_dismissed_alerts
|
||||
render nothing: true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
79
app/controllers/spree/admin/overview_controller.rb
Normal file
79
app/controllers/spree/admin/overview_controller.rb
Normal file
@@ -0,0 +1,79 @@
|
||||
# this clas was inspired (heavily) from the mephisto admin architecture
|
||||
module Spree
|
||||
module Admin
|
||||
class OverviewController < Spree::Admin::BaseController
|
||||
def index
|
||||
@enterprises = Enterprise
|
||||
.managed_by(spree_current_user)
|
||||
.order('is_primary_producer ASC, name')
|
||||
@product_count = Spree::Product.active.managed_by(spree_current_user).count
|
||||
@order_cycle_count = OrderCycle.active.managed_by(spree_current_user).count
|
||||
|
||||
if first_access
|
||||
redirect_to enterprises_path
|
||||
else
|
||||
render dashboard_view
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Checks whether the user is accessing the admin for the first time
|
||||
#
|
||||
# @return [Boolean]
|
||||
def first_access
|
||||
outside_referral && incomplete_enterprise_registration?
|
||||
end
|
||||
|
||||
# Checks whether the request comes from another admin page or not
|
||||
#
|
||||
# @return [Boolean]
|
||||
def outside_referral
|
||||
!URI(request.referer.to_s).path.match(%r{/admin})
|
||||
end
|
||||
|
||||
# Checks that all of the enterprises owned by the current user have a 'sells'
|
||||
# property specified, which indicates that the registration process has been
|
||||
# completed
|
||||
#
|
||||
# @return [Boolean]
|
||||
def incomplete_enterprise_registration?
|
||||
@incomplete_enterprise_registration ||= spree_current_user
|
||||
.owned_enterprises
|
||||
.where(sells: 'unspecified')
|
||||
.exists?
|
||||
end
|
||||
|
||||
# Returns the appropriate enterprise path for the current user
|
||||
#
|
||||
# @return [String]
|
||||
def enterprises_path
|
||||
if managed_enterprises.size == 1
|
||||
@enterprise = @enterprises.first
|
||||
main_app.welcome_admin_enterprise_path(@enterprise)
|
||||
else
|
||||
main_app.admin_enterprises_path
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the appropriate dashboard view for the current user
|
||||
#
|
||||
# @return [String]
|
||||
def dashboard_view
|
||||
if managed_enterprises.size == 1
|
||||
@enterprise = @enterprises.first
|
||||
:single_enterprise_dashboard
|
||||
else
|
||||
:multi_enterprise_dashboard
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the list of enterprises the current user is manager of
|
||||
#
|
||||
# @return [ActiveRecord::Relation<Enterprise>]
|
||||
def managed_enterprises
|
||||
spree_current_user.enterprises
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,74 +0,0 @@
|
||||
Spree::Admin::OverviewController.class_eval do
|
||||
def index
|
||||
@enterprises = Enterprise
|
||||
.managed_by(spree_current_user)
|
||||
.order('is_primary_producer ASC, name')
|
||||
@product_count = Spree::Product.active.managed_by(spree_current_user).count
|
||||
@order_cycle_count = OrderCycle.active.managed_by(spree_current_user).count
|
||||
|
||||
if first_access
|
||||
redirect_to enterprises_path
|
||||
else
|
||||
render dashboard_view
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Checks whether the user is accessing the admin for the first time
|
||||
#
|
||||
# @return [Boolean]
|
||||
def first_access
|
||||
outside_referral && incomplete_enterprise_registration?
|
||||
end
|
||||
|
||||
# Checks whether the request comes from another admin page or not
|
||||
#
|
||||
# @return [Boolean]
|
||||
def outside_referral
|
||||
!URI(request.referer.to_s).path.match(%r{/admin})
|
||||
end
|
||||
|
||||
# Checks that all of the enterprises owned by the current user have a 'sells'
|
||||
# property specified, which indicates that the registration process has been
|
||||
# completed
|
||||
#
|
||||
# @return [Boolean]
|
||||
def incomplete_enterprise_registration?
|
||||
@incomplete_enterprise_registration ||= spree_current_user
|
||||
.owned_enterprises
|
||||
.where(sells: 'unspecified')
|
||||
.exists?
|
||||
end
|
||||
|
||||
# Returns the appropriate enterprise path for the current user
|
||||
#
|
||||
# @return [String]
|
||||
def enterprises_path
|
||||
if managed_enterprises.size == 1
|
||||
@enterprise = @enterprises.first
|
||||
main_app.welcome_admin_enterprise_path(@enterprise)
|
||||
else
|
||||
main_app.admin_enterprises_path
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the appropriate dashboard view for the current user
|
||||
#
|
||||
# @return [String]
|
||||
def dashboard_view
|
||||
if managed_enterprises.size == 1
|
||||
@enterprise = @enterprises.first
|
||||
:single_enterprise_dashboard
|
||||
else
|
||||
:multi_enterprise_dashboard
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the list of enterprises the current user is manager of
|
||||
#
|
||||
# @return [ActiveRecord::Relation<Enterprise>]
|
||||
def managed_enterprises
|
||||
spree_current_user.enterprises
|
||||
end
|
||||
end
|
||||
19
app/controllers/spree/admin/product_properties_controller.rb
Normal file
19
app/controllers/spree/admin/product_properties_controller.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class ProductPropertiesController < ResourceController
|
||||
belongs_to 'spree/product', find_by: :permalink
|
||||
before_filter :find_properties
|
||||
before_filter :setup_property, only: [:index]
|
||||
|
||||
private
|
||||
|
||||
def find_properties
|
||||
@properties = Spree::Property.pluck(:name)
|
||||
end
|
||||
|
||||
def setup_property
|
||||
@product.product_properties.build
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
6
app/controllers/spree/admin/properties_controller.rb
Normal file
6
app/controllers/spree/admin/properties_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class PropertiesController < ResourceController
|
||||
end
|
||||
end
|
||||
end
|
||||
317
app/controllers/spree/admin/reports_controller.rb
Normal file
317
app/controllers/spree/admin/reports_controller.rb
Normal file
@@ -0,0 +1,317 @@
|
||||
require 'csv'
|
||||
|
||||
require 'open_food_network/reports/list'
|
||||
require 'open_food_network/order_and_distributor_report'
|
||||
require 'open_food_network/products_and_inventory_report'
|
||||
require 'open_food_network/lettuce_share_report'
|
||||
require 'open_food_network/group_buy_report'
|
||||
require 'open_food_network/order_grouper'
|
||||
require 'open_food_network/customers_report'
|
||||
require 'open_food_network/users_and_enterprises_report'
|
||||
require 'open_food_network/order_cycle_management_report'
|
||||
require 'open_food_network/packing_report'
|
||||
require 'open_food_network/sales_tax_report'
|
||||
require 'open_food_network/xero_invoices_report'
|
||||
require 'open_food_network/bulk_coop_report'
|
||||
require 'open_food_network/payments_report'
|
||||
require 'open_food_network/orders_and_fulfillments_report'
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
class ReportsController < Spree::Admin::BaseController
|
||||
include Spree::ReportsHelper
|
||||
|
||||
helper_method :render_content?
|
||||
|
||||
before_filter :cache_search_state
|
||||
# Fetches user's distributors, suppliers and order_cycles
|
||||
before_filter :load_data,
|
||||
only: [:customers, :products_and_inventory, :order_cycle_management, :packing]
|
||||
|
||||
respond_to :html
|
||||
|
||||
def report_types
|
||||
OpenFoodNetwork::Reports::List.all
|
||||
end
|
||||
|
||||
def index
|
||||
@reports = authorized_reports
|
||||
respond_with(@reports)
|
||||
end
|
||||
|
||||
def customers
|
||||
@report_types = report_types[:customers]
|
||||
@report_type = params[:report_type]
|
||||
@report = OpenFoodNetwork::CustomersReport.new spree_current_user, params, render_content?
|
||||
render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def order_cycle_management
|
||||
params[:q] ||= {}
|
||||
|
||||
@report_types = report_types[:order_cycle_management]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user,
|
||||
params,
|
||||
render_content?
|
||||
@table = @report.table_items
|
||||
|
||||
render_report(@report.header, @table, params[:csv],
|
||||
"order_cycle_management_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def packing
|
||||
params[:q] ||= {}
|
||||
|
||||
@report_types = report_types[:packing]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::PackingReport.new spree_current_user, params, render_content?
|
||||
@table = order_grouper_table
|
||||
|
||||
render_report(@report.header, @table, params[:csv], "packing_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def orders_and_distributors
|
||||
@report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user,
|
||||
params,
|
||||
render_content?
|
||||
@search = @report.search
|
||||
csv_file_name = "orders_and_distributors_#{timestamp}.csv"
|
||||
render_report(@report.header, @report.table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def sales_tax
|
||||
@distributors = my_distributors
|
||||
@report_type = params[:report_type]
|
||||
@report = OpenFoodNetwork::SalesTaxReport.new spree_current_user, params, render_content?
|
||||
render_report(@report.header, @report.table, params[:csv], "sales_tax.csv")
|
||||
end
|
||||
|
||||
def bulk_coop
|
||||
# -- Prepare form options
|
||||
@distributors = my_distributors
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::BulkCoopReport.new spree_current_user, params, render_content?
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "bulk_coop_#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def payments
|
||||
# -- Prepare Form Options
|
||||
@distributors = my_distributors
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::PaymentsReport.new spree_current_user, params, render_content?
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "payments_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def orders_and_fulfillment
|
||||
params[:q] ||= orders_and_fulfillment_default_filters
|
||||
|
||||
# -- Prepare Form Options
|
||||
permissions = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
# My distributors and any distributors distributing products I supply
|
||||
@distributors = permissions.visible_enterprises_for_order_reports.is_distributor
|
||||
# My suppliers and any suppliers supplying products I distribute
|
||||
@suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer
|
||||
|
||||
@order_cycles = my_order_cycles
|
||||
|
||||
@report_types = report_types[:orders_and_fulfillment]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
@include_blank = I18n.t(:all)
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user,
|
||||
params,
|
||||
render_content?
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def products_and_inventory
|
||||
@report_types = report_types[:products_and_inventory]
|
||||
@report = if params[:report_type] != 'lettuce_share'
|
||||
OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user,
|
||||
params,
|
||||
render_content?
|
||||
else
|
||||
OpenFoodNetwork::LettuceShareReport.new spree_current_user,
|
||||
params,
|
||||
render_content?
|
||||
end
|
||||
|
||||
render_report @report.header,
|
||||
@report.table,
|
||||
params[:csv],
|
||||
"products_and_inventory_#{timestamp}.csv"
|
||||
end
|
||||
|
||||
def users_and_enterprises
|
||||
@report = OpenFoodNetwork::UsersAndEnterprisesReport.new params, render_content?
|
||||
render_report(@report.header, @report.table, params[:csv],
|
||||
"users_and_enterprises_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def xero_invoices
|
||||
params[:q] ||= {}
|
||||
|
||||
@distributors = my_distributors
|
||||
@order_cycles = my_order_cycles
|
||||
|
||||
@report = OpenFoodNetwork::XeroInvoicesReport.new(spree_current_user,
|
||||
params,
|
||||
render_content?)
|
||||
render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def model_class
|
||||
Spree::Admin::ReportsController
|
||||
end
|
||||
|
||||
# Some actions are changing the `params` object. That is unfortunate Spree
|
||||
# behavior and we are building on it. So we have to look at `params` early
|
||||
# to check if we are searching or just displaying a report search form.
|
||||
def cache_search_state
|
||||
search_keys = [
|
||||
# search parameter for ransack
|
||||
:q,
|
||||
# common in all reports, only set for CSV rendering
|
||||
:csv,
|
||||
# `button` is included in all forms. It's not important for searching,
|
||||
# but the Users & Enterprises report doesn't have any other parameter
|
||||
# for an empty search. So we use this one to display data.
|
||||
:button,
|
||||
# Some reports use filtering by enterprise or order cycle
|
||||
:distributor_id,
|
||||
:supplier_id,
|
||||
:order_cycle_id,
|
||||
# Xero Invoices can be filtered by date
|
||||
:invoice_date,
|
||||
:due_date
|
||||
]
|
||||
@searching = search_keys.any? { |key| params.key? key }
|
||||
end
|
||||
|
||||
# We don't want to render data unless search params are supplied.
|
||||
# Compiling data can take a long time.
|
||||
def render_content?
|
||||
@searching
|
||||
end
|
||||
|
||||
def render_report(header, table, create_csv, csv_file_name)
|
||||
send_data csv_report(header, table), filename: csv_file_name if create_csv
|
||||
@header = header
|
||||
@table = table
|
||||
# Rendering HTML is the default.
|
||||
end
|
||||
|
||||
def csv_report(header, table)
|
||||
CSV.generate do |csv|
|
||||
csv << header
|
||||
table.each { |row| csv << row }
|
||||
end
|
||||
end
|
||||
|
||||
def load_data
|
||||
@distributors = my_distributors
|
||||
@suppliers = my_suppliers | suppliers_of_products_distributed_by(@distributors)
|
||||
@order_cycles = my_order_cycles
|
||||
end
|
||||
|
||||
# Load managed distributor enterprises of current user
|
||||
def my_distributors
|
||||
Enterprise.is_distributor.managed_by(spree_current_user)
|
||||
end
|
||||
|
||||
# Load managed producer enterprises of current user
|
||||
def my_suppliers
|
||||
Enterprise.is_primary_producer.managed_by(spree_current_user)
|
||||
end
|
||||
|
||||
def suppliers_of_products_distributed_by(distributors)
|
||||
distributors.map { |d| Spree::Product.in_distributor(d).includes(:supplier).all }.
|
||||
flatten.map(&:supplier).uniq
|
||||
end
|
||||
|
||||
# Load order cycles the current user has access to
|
||||
def my_order_cycles
|
||||
OrderCycle.
|
||||
active_or_complete.
|
||||
accessible_by(spree_current_user).
|
||||
order('orders_close_at DESC')
|
||||
end
|
||||
|
||||
def order_grouper_table
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
|
||||
order_grouper.table(@report.table_items)
|
||||
end
|
||||
|
||||
def authorized_reports
|
||||
all_reports = [
|
||||
:orders_and_distributors,
|
||||
:bulk_coop,
|
||||
:payments,
|
||||
:orders_and_fulfillment,
|
||||
:customers,
|
||||
:products_and_inventory,
|
||||
:users_and_enterprises,
|
||||
:enterprise_fee_summary,
|
||||
:order_cycle_management,
|
||||
:sales_tax,
|
||||
:xero_invoices,
|
||||
:packing
|
||||
]
|
||||
reports = all_reports.select { |action| can? action, Spree::Admin::ReportsController }
|
||||
reports.map { |report| [report, describe_report(report)] }.to_h
|
||||
end
|
||||
|
||||
def describe_report(report)
|
||||
name = I18n.t(:name, scope: [:admin, :reports, report])
|
||||
description = begin
|
||||
I18n.t!(:description, scope: [:admin, :reports, report])
|
||||
rescue I18n::MissingTranslationData
|
||||
render_to_string(
|
||||
partial: "#{report}_description",
|
||||
layout: false,
|
||||
locals: { report_types: report_types[report] }
|
||||
).html_safe
|
||||
end
|
||||
{ name: name, url: url_for_report(report), description: description }
|
||||
end
|
||||
|
||||
def url_for_report(report)
|
||||
public_send("#{report}_admin_reports_url".to_sym)
|
||||
rescue NoMethodError
|
||||
url_for([:new, :admin, :reports, report.to_s.singularize])
|
||||
end
|
||||
|
||||
def timestamp
|
||||
Time.zone.now.strftime("%Y%m%d")
|
||||
end
|
||||
|
||||
def orders_and_fulfillment_default_filters
|
||||
now = Time.zone.now
|
||||
{ completed_at_gt: (now - 1.month).beginning_of_day,
|
||||
completed_at_lt: (now + 1.day).beginning_of_day }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,287 +0,0 @@
|
||||
require 'csv'
|
||||
|
||||
require 'open_food_network/reports/list'
|
||||
require 'open_food_network/order_and_distributor_report'
|
||||
require 'open_food_network/products_and_inventory_report'
|
||||
require 'open_food_network/lettuce_share_report'
|
||||
require 'open_food_network/group_buy_report'
|
||||
require 'open_food_network/order_grouper'
|
||||
require 'open_food_network/customers_report'
|
||||
require 'open_food_network/users_and_enterprises_report'
|
||||
require 'open_food_network/order_cycle_management_report'
|
||||
require 'open_food_network/packing_report'
|
||||
require 'open_food_network/sales_tax_report'
|
||||
require 'open_food_network/xero_invoices_report'
|
||||
require 'open_food_network/bulk_coop_report'
|
||||
require 'open_food_network/payments_report'
|
||||
require 'open_food_network/orders_and_fulfillments_report'
|
||||
|
||||
Spree::Admin::ReportsController.class_eval do
|
||||
include Spree::ReportsHelper
|
||||
|
||||
helper_method :render_content?
|
||||
|
||||
before_filter :cache_search_state
|
||||
# Fetches user's distributors, suppliers and order_cycles
|
||||
before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management, :packing]
|
||||
|
||||
def report_types
|
||||
OpenFoodNetwork::Reports::List.all
|
||||
end
|
||||
|
||||
# Override spree reports list.
|
||||
def index
|
||||
@reports = authorized_reports
|
||||
respond_with(@reports)
|
||||
end
|
||||
|
||||
def customers
|
||||
@report_types = report_types[:customers]
|
||||
@report_type = params[:report_type]
|
||||
@report = OpenFoodNetwork::CustomersReport.new spree_current_user, params, render_content?
|
||||
render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def order_cycle_management
|
||||
params[:q] ||= {}
|
||||
|
||||
@report_types = report_types[:order_cycle_management]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user, params, render_content?
|
||||
@table = @report.table_items
|
||||
|
||||
render_report(@report.header, @table, params[:csv], "order_cycle_management_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def packing
|
||||
params[:q] ||= {}
|
||||
|
||||
@report_types = report_types[:packing]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::PackingReport.new spree_current_user, params, render_content?
|
||||
@table = order_grouper_table
|
||||
|
||||
render_report(@report.header, @table, params[:csv], "packing_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def orders_and_distributors
|
||||
@report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user, params, render_content?
|
||||
@search = @report.search
|
||||
csv_file_name = "orders_and_distributors_#{timestamp}.csv"
|
||||
render_report(@report.header, @report.table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def sales_tax
|
||||
@distributors = my_distributors
|
||||
@report_type = params[:report_type]
|
||||
@report = OpenFoodNetwork::SalesTaxReport.new spree_current_user, params, render_content?
|
||||
render_report(@report.header, @report.table, params[:csv], "sales_tax.csv")
|
||||
end
|
||||
|
||||
def bulk_coop
|
||||
# -- Prepare form options
|
||||
@distributors = my_distributors
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::BulkCoopReport.new spree_current_user, params, render_content?
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "bulk_coop_#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def payments
|
||||
# -- Prepare Form Options
|
||||
@distributors = my_distributors
|
||||
@report_type = params[:report_type]
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::PaymentsReport.new spree_current_user, params, render_content?
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "payments_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def orders_and_fulfillment
|
||||
params[:q] ||= orders_and_fulfillment_default_filters
|
||||
|
||||
# -- Prepare Form Options
|
||||
permissions = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
# My distributors and any distributors distributing products I supply
|
||||
@distributors = permissions.visible_enterprises_for_order_reports.is_distributor
|
||||
# My suppliers and any suppliers supplying products I distribute
|
||||
@suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer
|
||||
|
||||
@order_cycles = my_order_cycles
|
||||
|
||||
@report_types = report_types[:orders_and_fulfillment]
|
||||
@report_type = params[:report_type]
|
||||
|
||||
@include_blank = I18n.t(:all)
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions,
|
||||
params, render_content?)
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
render_report(@report.header, @table, params[:csv], csv_file_name)
|
||||
end
|
||||
|
||||
def products_and_inventory
|
||||
@report_types = report_types[:products_and_inventory]
|
||||
@report = if params[:report_type] != 'lettuce_share'
|
||||
OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user, params, render_content?
|
||||
else
|
||||
OpenFoodNetwork::LettuceShareReport.new spree_current_user, params, render_content?
|
||||
end
|
||||
render_report(@report.header, @report.table, params[:csv], "products_and_inventory_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def users_and_enterprises
|
||||
@report = OpenFoodNetwork::UsersAndEnterprisesReport.new params, render_content?
|
||||
render_report(@report.header, @report.table, params[:csv], "users_and_enterprises_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
def xero_invoices
|
||||
params[:q] ||= {}
|
||||
|
||||
@distributors = my_distributors
|
||||
@order_cycles = my_order_cycles
|
||||
|
||||
@report = OpenFoodNetwork::XeroInvoicesReport.new spree_current_user, params, render_content?
|
||||
render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Some actions are changing the `params` object. That is unfortunate Spree
|
||||
# behavior and we are building on it. So we have to look at `params` early
|
||||
# to check if we are searching or just displaying a report search form.
|
||||
def cache_search_state
|
||||
search_keys = [
|
||||
# search parameter for ransack
|
||||
:q,
|
||||
# common in all reports, only set for CSV rendering
|
||||
:csv,
|
||||
# `button` is included in all forms. It's not important for searching,
|
||||
# but the Users & Enterprises report doesn't have any other parameter
|
||||
# for an empty search. So we use this one to display data.
|
||||
:button,
|
||||
# Some reports use filtering by enterprise or order cycle
|
||||
:distributor_id,
|
||||
:supplier_id,
|
||||
:order_cycle_id,
|
||||
# Xero Invoices can be filtered by date
|
||||
:invoice_date,
|
||||
:due_date
|
||||
]
|
||||
@searching = search_keys.any? { |key| params.key? key }
|
||||
end
|
||||
|
||||
# We don't want to render data unless search params are supplied.
|
||||
# Compiling data can take a long time.
|
||||
def render_content?
|
||||
@searching
|
||||
end
|
||||
|
||||
def render_report(header, table, create_csv, csv_file_name)
|
||||
send_data csv_report(header, table), filename: csv_file_name if create_csv
|
||||
@header = header
|
||||
@table = table
|
||||
# Rendering HTML is the default.
|
||||
end
|
||||
|
||||
def csv_report(header, table)
|
||||
CSV.generate do |csv|
|
||||
csv << header
|
||||
table.each { |row| csv << row }
|
||||
end
|
||||
end
|
||||
|
||||
def load_data
|
||||
@distributors = my_distributors
|
||||
@suppliers = my_suppliers | suppliers_of_products_distributed_by(@distributors)
|
||||
@order_cycles = my_order_cycles
|
||||
end
|
||||
|
||||
# Load managed distributor enterprises of current user
|
||||
def my_distributors
|
||||
Enterprise.is_distributor.managed_by(spree_current_user)
|
||||
end
|
||||
|
||||
# Load managed producer enterprises of current user
|
||||
def my_suppliers
|
||||
Enterprise.is_primary_producer.managed_by(spree_current_user)
|
||||
end
|
||||
|
||||
def suppliers_of_products_distributed_by(distributors)
|
||||
distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq
|
||||
end
|
||||
|
||||
# Load order cycles the current user has access to
|
||||
def my_order_cycles
|
||||
OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC')
|
||||
end
|
||||
|
||||
def order_grouper_table
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
|
||||
order_grouper.table(@report.table_items)
|
||||
end
|
||||
|
||||
def authorized_reports
|
||||
all_reports = [
|
||||
:orders_and_distributors,
|
||||
:bulk_coop,
|
||||
:payments,
|
||||
:orders_and_fulfillment,
|
||||
:customers,
|
||||
:products_and_inventory,
|
||||
:sales_total,
|
||||
:users_and_enterprises,
|
||||
:enterprise_fee_summary,
|
||||
:order_cycle_management,
|
||||
:sales_tax,
|
||||
:xero_invoices,
|
||||
:packing
|
||||
]
|
||||
reports = all_reports.select { |action| can? action, Spree::Admin::ReportsController }
|
||||
reports.map { |report| [report, describe_report(report)] }.to_h
|
||||
end
|
||||
|
||||
def describe_report(report)
|
||||
name = I18n.t(:name, scope: [:admin, :reports, report])
|
||||
description = begin
|
||||
I18n.t!(:description, scope: [:admin, :reports, report])
|
||||
rescue I18n::MissingTranslationData
|
||||
render_to_string(
|
||||
partial: "#{report}_description",
|
||||
layout: false,
|
||||
locals: { report_types: report_types[report] }
|
||||
).html_safe
|
||||
end
|
||||
{ name: name, url: url_for_report(report), description: description }
|
||||
end
|
||||
|
||||
def url_for_report(report)
|
||||
public_send("#{report}_admin_reports_url".to_sym)
|
||||
rescue NoMethodError
|
||||
url_for([:new, :admin, :reports, report.to_s.singularize])
|
||||
end
|
||||
|
||||
def timestamp
|
||||
Time.zone.now.strftime("%Y%m%d")
|
||||
end
|
||||
|
||||
def orders_and_fulfillment_default_filters
|
||||
now = Time.zone.now
|
||||
{ completed_at_gt: (now - 1.month).beginning_of_day,
|
||||
completed_at_lt: (now + 1.day).beginning_of_day }
|
||||
end
|
||||
end
|
||||
275
app/controllers/spree/admin/resource_controller.rb
Normal file
275
app/controllers/spree/admin/resource_controller.rb
Normal file
@@ -0,0 +1,275 @@
|
||||
require 'action_callbacks'
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
class ResourceController < Spree::Admin::BaseController
|
||||
helper_method :new_object_url, :edit_object_url, :object_url, :collection_url
|
||||
before_filter :load_resource, except: [:update_positions]
|
||||
rescue_from ActiveRecord::RecordNotFound, with: :resource_not_found
|
||||
rescue_from CanCan::AccessDenied, with: :unauthorized
|
||||
|
||||
respond_to :html
|
||||
respond_to :js, except: [:show, :index]
|
||||
|
||||
def new
|
||||
invoke_callbacks(:new_action, :before)
|
||||
respond_with(@object) do |format|
|
||||
format.html { render layout: !request.xhr? }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
respond_with(@object) do |format|
|
||||
format.html { render layout: !request.xhr? }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
invoke_callbacks(:update, :before)
|
||||
if @object.update_attributes(params[object_name])
|
||||
invoke_callbacks(:update, :after)
|
||||
flash[:success] = flash_message_for(@object, :successfully_updated)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to location_after_save }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
else
|
||||
invoke_callbacks(:update, :fails)
|
||||
respond_with(@object)
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
invoke_callbacks(:create, :before)
|
||||
@object.attributes = params[object_name]
|
||||
if @object.save
|
||||
invoke_callbacks(:create, :after)
|
||||
flash[:success] = flash_message_for(@object, :successfully_created)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to location_after_save }
|
||||
format.js { render layout: false }
|
||||
end
|
||||
else
|
||||
invoke_callbacks(:create, :fails)
|
||||
respond_with(@object)
|
||||
end
|
||||
end
|
||||
|
||||
def update_positions
|
||||
params[:positions].each do |id, index|
|
||||
model_class.where(id: id).update_all(position: index)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.js { render text: 'Ok' }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
invoke_callbacks(:destroy, :before)
|
||||
if @object.destroy
|
||||
invoke_callbacks(:destroy, :after)
|
||||
flash[:success] = flash_message_for(@object, :successfully_removed)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
format.js { render partial: "spree/admin/shared/destroy" }
|
||||
end
|
||||
else
|
||||
invoke_callbacks(:destroy, :fails)
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def resource_not_found
|
||||
flash[:error] = flash_message_for(model_class.new, :not_found)
|
||||
redirect_to collection_url
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_accessor :parent_data
|
||||
attr_accessor :callbacks
|
||||
|
||||
def belongs_to(model_name, options = {})
|
||||
@parent_data ||= {}
|
||||
@parent_data[:model_name] = model_name
|
||||
@parent_data[:model_class] = model_name.to_s.classify.constantize
|
||||
@parent_data[:find_by] = options[:find_by] || :id
|
||||
end
|
||||
|
||||
def new_action
|
||||
@callbacks ||= {}
|
||||
@callbacks[:new_action] ||= ActionCallbacks.new
|
||||
end
|
||||
|
||||
def create
|
||||
@callbacks ||= {}
|
||||
@callbacks[:create] ||= ActionCallbacks.new
|
||||
end
|
||||
|
||||
def update
|
||||
@callbacks ||= {}
|
||||
@callbacks[:update] ||= ActionCallbacks.new
|
||||
end
|
||||
|
||||
def destroy
|
||||
@callbacks ||= {}
|
||||
@callbacks[:destroy] ||= ActionCallbacks.new
|
||||
end
|
||||
end
|
||||
|
||||
def model_class
|
||||
"Spree::#{controller_name.classify}".constantize
|
||||
end
|
||||
|
||||
def model_name
|
||||
parent_data[:model_name].gsub('spree/', '')
|
||||
end
|
||||
|
||||
def object_name
|
||||
controller_name.singularize
|
||||
end
|
||||
|
||||
def load_resource
|
||||
if member_action?
|
||||
@object ||= load_resource_instance
|
||||
|
||||
# call authorize! a third time (called twice already in Admin::BaseController)
|
||||
# this time we pass the actual instance so fine-grained abilities can control
|
||||
# access to individual records, not just entire models.
|
||||
authorize! action, @object
|
||||
|
||||
instance_variable_set("@#{object_name}", @object)
|
||||
|
||||
# If we don't have access, clear the object
|
||||
unless can? action, @object
|
||||
instance_variable_set("@#{object_name}", nil)
|
||||
end
|
||||
|
||||
authorize! action, @object
|
||||
else
|
||||
@collection ||= collection
|
||||
|
||||
# note: we don't call authorize here as the collection method should use
|
||||
# CanCan's accessible_by method to restrict the actual records returned
|
||||
|
||||
instance_variable_set("@#{controller_name}", @collection)
|
||||
end
|
||||
end
|
||||
|
||||
def load_resource_instance
|
||||
if new_actions.include?(action)
|
||||
build_resource
|
||||
elsif params[:id]
|
||||
find_resource
|
||||
end
|
||||
end
|
||||
|
||||
def parent_data
|
||||
self.class.parent_data
|
||||
end
|
||||
|
||||
def parent
|
||||
return nil if parent_data.blank?
|
||||
|
||||
@parent ||= parent_data[:model_class].
|
||||
public_send("find_by_#{parent_data[:find_by]}", params["#{model_name}_id"])
|
||||
instance_variable_set("@#{model_name}", @parent)
|
||||
end
|
||||
|
||||
def find_resource
|
||||
if parent_data.present?
|
||||
parent.public_send(controller_name).find(params[:id])
|
||||
else
|
||||
model_class.find(params[:id])
|
||||
end
|
||||
end
|
||||
|
||||
def build_resource
|
||||
if parent_data.present?
|
||||
parent.public_send(controller_name).build
|
||||
else
|
||||
model_class.new
|
||||
end
|
||||
end
|
||||
|
||||
def collection
|
||||
return parent.public_send(controller_name) if parent_data.present?
|
||||
|
||||
if model_class.respond_to?(:accessible_by) &&
|
||||
!current_ability.has_block?(params[:action], model_class)
|
||||
model_class.accessible_by(current_ability, action)
|
||||
else
|
||||
model_class.scoped
|
||||
end
|
||||
end
|
||||
|
||||
def location_after_save
|
||||
collection_url
|
||||
end
|
||||
|
||||
def invoke_callbacks(action, callback_type)
|
||||
callbacks = self.class.callbacks || {}
|
||||
return if callbacks[action].nil?
|
||||
|
||||
case callback_type.to_sym
|
||||
when :before then callbacks[action].before_methods.each { |method| __send__ method }
|
||||
when :after then callbacks[action].after_methods.each { |method| __send__ method }
|
||||
when :fails then callbacks[action].fails_methods.each { |method| __send__ method }
|
||||
end
|
||||
end
|
||||
|
||||
# URL helpers
|
||||
|
||||
def new_object_url(options = {})
|
||||
if parent_data.present?
|
||||
spree.new_polymorphic_url([:admin, parent, model_class], options)
|
||||
else
|
||||
spree.new_polymorphic_url([:admin, model_class], options)
|
||||
end
|
||||
end
|
||||
|
||||
def edit_object_url(object, options = {})
|
||||
if parent_data.present?
|
||||
spree.public_send "edit_admin_#{model_name}_#{object_name}_url", parent, object, options
|
||||
else
|
||||
spree.public_send "edit_admin_#{object_name}_url", object, options
|
||||
end
|
||||
end
|
||||
|
||||
def object_url(object = nil, options = {})
|
||||
target = object || @object
|
||||
if parent_data.present?
|
||||
spree.public_send "admin_#{model_name}_#{object_name}_url", parent, target, options
|
||||
else
|
||||
spree.public_send "admin_#{object_name}_url", target, options
|
||||
end
|
||||
end
|
||||
|
||||
def collection_url(options = {})
|
||||
if parent_data.present?
|
||||
spree.polymorphic_url([:admin, parent, model_class], options)
|
||||
else
|
||||
spree.polymorphic_url([:admin, model_class], options)
|
||||
end
|
||||
end
|
||||
|
||||
def collection_actions
|
||||
[:index]
|
||||
end
|
||||
|
||||
def member_action?
|
||||
!collection_actions.include? action
|
||||
end
|
||||
|
||||
def new_actions
|
||||
[:new, :create]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,20 +0,0 @@
|
||||
module AuthorizeOnLoadResource
|
||||
def load_resource
|
||||
super
|
||||
|
||||
if member_action?
|
||||
# If we don't have access, clear the object
|
||||
unless can? action, @object
|
||||
instance_variable_set("@#{object_name}", nil)
|
||||
end
|
||||
|
||||
authorize! action, @object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Spree::Admin::ResourceController.prepend(AuthorizeOnLoadResource)
|
||||
|
||||
Spree::Admin::ResourceController.class_eval do
|
||||
rescue_from CanCan::AccessDenied, with: :unauthorized
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class ReturnAuthorizationsController < ResourceController
|
||||
belongs_to 'spree/order', find_by: :number
|
||||
|
||||
update.after :associate_inventory_units
|
||||
create.after :associate_inventory_units
|
||||
|
||||
def fire
|
||||
@return_authorization.public_send("#{params[:e]}!")
|
||||
flash[:success] = Spree.t(:return_authorization_updated)
|
||||
redirect_to :back
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def associate_inventory_units
|
||||
(params[:return_quantity] || []).each do |variant_id, qty|
|
||||
@return_authorization.add_variant(variant_id.to_i, qty.to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -176,7 +176,7 @@ module Spree
|
||||
previous_states = @order.adjustments.each_with_object({}) do |adjustment, hash|
|
||||
hash[adjustment.id] = adjustment.state
|
||||
end
|
||||
@order.adjustments.each(&:open)
|
||||
@order.adjustments.each { |adjustment| adjustment.fire_events(:open) }
|
||||
|
||||
yield
|
||||
|
||||
|
||||
11
app/helpers/admin/enterprises_helper.rb
Normal file
11
app/helpers/admin/enterprises_helper.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
module Admin
|
||||
module EnterprisesHelper
|
||||
def add_check_if_single(count)
|
||||
if count == 1
|
||||
{ checked: true }
|
||||
else
|
||||
{}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,12 +20,27 @@ module ShopHelper
|
||||
)
|
||||
end
|
||||
|
||||
def shop_tabs
|
||||
def base_shop_tabs(column_sizes)
|
||||
[
|
||||
{ name: 'about', title: t(:shopping_tabs_about, distributor: current_distributor.name), cols: 6 },
|
||||
{ name: 'producers', title: t(:label_producers), cols: 2 },
|
||||
{ name: 'contact', title: t(:shopping_tabs_contact), cols: 2 },
|
||||
{ name: 'groups', title: t(:label_groups), cols: 2 },
|
||||
{ name: 'about', cols: column_sizes[0],
|
||||
title: t(:shopping_tabs_about, distributor: current_distributor.name) },
|
||||
{ name: 'producers', cols: column_sizes[1],
|
||||
title: t(:label_producers) },
|
||||
{ name: 'contact', cols: column_sizes[2],
|
||||
title: t(:shopping_tabs_contact) }
|
||||
]
|
||||
end
|
||||
|
||||
def tabs_with_groups
|
||||
tabs = base_shop_tabs([6, 2, 2])
|
||||
tabs << { name: 'groups', title: t(:label_groups), cols: 2 }
|
||||
end
|
||||
|
||||
def tabs_without_groups
|
||||
base_shop_tabs([4, 4, 4])
|
||||
end
|
||||
|
||||
def shop_tabs
|
||||
current_distributor.groups.present? ? tabs_with_groups : tabs_without_groups
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'spree/admin/base_helper'
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
module BaseHelper
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'spree/admin/navigation_helper'
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
module NavigationHelper
|
||||
|
||||
@@ -35,6 +35,7 @@ class ProducerMailer < Spree::BaseMailer
|
||||
|
||||
def line_items_from(order_cycle, producer)
|
||||
Spree::LineItem.
|
||||
includes(variant: { option_values: :option_type }).
|
||||
from_order_cycle(order_cycle).
|
||||
sorted_by_name_and_unit_value.
|
||||
merge(Spree::Product.in_supplier(producer)).
|
||||
|
||||
@@ -25,7 +25,7 @@ module Calculator
|
||||
end
|
||||
|
||||
def line_item_weight(line_item)
|
||||
if line_item.final_weight_volume.present?
|
||||
if final_weight_volume_present?(line_item)
|
||||
weight_per_final_weight_volume(line_item)
|
||||
else
|
||||
weight_per_variant(line_item) * line_item.quantity
|
||||
@@ -33,13 +33,18 @@ module Calculator
|
||||
end
|
||||
|
||||
def weight_per_variant(line_item)
|
||||
line_item.variant.andand.weight || 0
|
||||
if variant_unit(line_item) == 'weight'
|
||||
# The calculator price is per_kg so we need to convert unit_value to kg
|
||||
convert_g_to_kg(line_item.variant.andand.unit_value)
|
||||
else
|
||||
line_item.variant.andand.weight || 0
|
||||
end
|
||||
end
|
||||
|
||||
def weight_per_final_weight_volume(line_item)
|
||||
if line_item.variant.product.andand.variant_unit == 'weight'
|
||||
# Divided by 1000 because grams is the base weight unit and the calculator price is per_kg
|
||||
line_item.final_weight_volume / 1000.0
|
||||
if variant_unit(line_item) == 'weight'
|
||||
# The calculator price is per_kg so we need to convert final_weight_volume to kg
|
||||
convert_g_to_kg(line_item.final_weight_volume)
|
||||
else
|
||||
weight_per_variant(line_item) * quantity_implied_in_final_weight_volume(line_item)
|
||||
end
|
||||
@@ -51,5 +56,19 @@ module Calculator
|
||||
def quantity_implied_in_final_weight_volume(line_item)
|
||||
(1.0 * line_item.final_weight_volume / line_item.variant.unit_value).round(3)
|
||||
end
|
||||
|
||||
def final_weight_volume_present?(line_item)
|
||||
line_item.respond_to?(:final_weight_volume) && line_item.final_weight_volume.present?
|
||||
end
|
||||
|
||||
def variant_unit(line_item)
|
||||
line_item.variant.product.andand.variant_unit
|
||||
end
|
||||
|
||||
def convert_g_to_kg(value)
|
||||
return 0 unless value
|
||||
|
||||
value / 1000
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
class EnterpriseRelationshipPermission < ActiveRecord::Base
|
||||
default_scope order('name')
|
||||
default_scope { order('name') }
|
||||
end
|
||||
|
||||
@@ -142,9 +142,9 @@ class OrderCycle < ActiveRecord::Base
|
||||
oc.name = I18n.t("models.order_cycle.cloned_order_cycle_name", order_cycle: oc.name)
|
||||
oc.orders_open_at = oc.orders_close_at = nil
|
||||
oc.coordinator_fee_ids = coordinator_fee_ids
|
||||
# rubocop:disable Metrics/LineLength
|
||||
# rubocop:disable Layout/LineLength
|
||||
oc.preferred_product_selection_from_coordinator_inventory_only = preferred_product_selection_from_coordinator_inventory_only
|
||||
# rubocop:enable Metrics/LineLength
|
||||
# rubocop:enable Layout/LineLength
|
||||
oc.save!
|
||||
exchanges.each { |e| e.clone!(oc) }
|
||||
oc.reload
|
||||
|
||||
@@ -2,7 +2,7 @@ class ProducerProperty < ActiveRecord::Base
|
||||
belongs_to :producer, class_name: 'Enterprise', touch: true
|
||||
belongs_to :property, class_name: 'Spree::Property'
|
||||
|
||||
default_scope order("#{table_name}.position")
|
||||
default_scope { order("#{table_name}.position") }
|
||||
|
||||
scope :ever_sold_by, ->(shop) {
|
||||
joins(producer: { supplied_products: { variants: { exchanges: :order_cycle } } }).
|
||||
|
||||
@@ -214,7 +214,9 @@ module ProductImport
|
||||
end
|
||||
|
||||
def accepted_mimetype
|
||||
File.extname(@file.path).in?('.csv', '.xls', '.xlsx', '.ods') ? @file.path.split('.').last.to_sym : false
|
||||
return false unless ['.csv'].include? File.extname(@file.path)
|
||||
|
||||
@file.path.split('.').last.to_sym
|
||||
end
|
||||
|
||||
def headers
|
||||
|
||||
@@ -45,11 +45,11 @@ module ProductImport
|
||||
|
||||
next if @enterprises_index.key? enterprise_name
|
||||
|
||||
enterprise = Enterprise.find_by_name(enterprise_name, select: 'id, is_primary_producer')
|
||||
enterprise = Enterprise.select([:id, :is_primary_producer]).
|
||||
where(name: enterprise_name).first
|
||||
|
||||
@enterprises_index[enterprise_name] =
|
||||
{ id: enterprise.try(:id),
|
||||
is_primary_producer: enterprise.try(:is_primary_producer) }
|
||||
{ id: enterprise.try(:id), is_primary_producer: enterprise.try(:is_primary_producer) }
|
||||
end
|
||||
@enterprises_index
|
||||
end
|
||||
@@ -60,7 +60,8 @@ module ProductImport
|
||||
next unless entry.producer
|
||||
|
||||
producer_name = entry.producer
|
||||
producer_id = @producers_index[producer_name] || Enterprise.find_by_name(producer_name, select: 'id, name').try(:id)
|
||||
producer_id = @producers_index[producer_name] ||
|
||||
Enterprise.select([:id, :name]).where(name: producer_name).first.try(:id)
|
||||
@producers_index[producer_name] = producer_id
|
||||
end
|
||||
@producers_index
|
||||
@@ -70,7 +71,8 @@ module ProductImport
|
||||
@categories_index = {}
|
||||
@entries.each do |entry|
|
||||
category_name = entry.category
|
||||
category_id = @categories_index[category_name] || Spree::Taxon.find_by_name(category_name, select: 'id, name').try(:id)
|
||||
category_id = @categories_index[category_name] ||
|
||||
Spree::Taxon.select([:id, :name]).where(name: category_name).first.try(:id)
|
||||
@categories_index[category_name] = category_id
|
||||
end
|
||||
@categories_index
|
||||
|
||||
@@ -192,7 +192,7 @@ class AbilityDecorator
|
||||
end
|
||||
|
||||
def add_order_cycle_management_abilities(user)
|
||||
can [:admin, :index, :read, :edit, :update], OrderCycle do |order_cycle|
|
||||
can [:admin, :index, :read, :edit, :update, :incoming, :outgoing], OrderCycle do |order_cycle|
|
||||
OrderCycle.accessible_by(user).include? order_cycle
|
||||
end
|
||||
can [:admin, :index, :create], Schedule
|
||||
|
||||
12
app/models/spree/property.rb
Normal file
12
app/models/spree/property.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
module Spree
|
||||
class Property < ActiveRecord::Base
|
||||
has_many :product_properties, dependent: :destroy
|
||||
has_many :products, through: :product_properties
|
||||
|
||||
attr_accessible :name, :presentation
|
||||
|
||||
validates :name, :presentation, presence: true
|
||||
|
||||
scope :sorted, -> { order(:name) }
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,5 @@
|
||||
module Spree
|
||||
class User < ActiveRecord::Base
|
||||
include Core::UserBanners
|
||||
|
||||
devise :database_authenticatable, :token_authenticatable, :registerable, :recoverable,
|
||||
:rememberable, :trackable, :validatable, :encryptable, encryptor: 'authlogic_sha512'
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ class SubscriptionLineItem < ActiveRecord::Base
|
||||
validates :variant, presence: true
|
||||
validates :quantity, presence: true, numericality: { only_integer: true }
|
||||
|
||||
default_scope { order('id ASC') }
|
||||
|
||||
def total_estimate
|
||||
(price_estimate || 0) * (quantity || 0)
|
||||
end
|
||||
@@ -22,6 +24,4 @@ class SubscriptionLineItem < ActiveRecord::Base
|
||||
def price
|
||||
price_estimate
|
||||
end
|
||||
|
||||
default_scope order('id ASC')
|
||||
end
|
||||
|
||||
@@ -12,7 +12,7 @@ class VariantOverride < ActiveRecord::Base
|
||||
# Default stock can be nil, indicating stock should not be reset or zero, meaning reset to zero. Need to ensure this can be set by the user.
|
||||
validates :default_stock, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
||||
|
||||
default_scope where(permission_revoked_at: nil)
|
||||
default_scope { where(permission_revoked_at: nil) }
|
||||
|
||||
scope :for_hubs, lambda { |hubs|
|
||||
where(hub_id: hubs)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'open_food_network/enterprise_issue_validator'
|
||||
|
||||
class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :managed, :supplied_products,
|
||||
attributes :id, :name, :managed,
|
||||
:issues_summary_supplier, :issues_summary_distributor,
|
||||
:is_primary_producer, :is_distributor, :sells
|
||||
|
||||
@@ -25,20 +25,14 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
|
||||
Enterprise.managed_by(options[:spree_current_user]).include? object
|
||||
end
|
||||
|
||||
def supplied_products
|
||||
serializer = Api::Admin::ForOrderCycle::SuppliedProductSerializer
|
||||
ActiveModel::ArraySerializer.new(products, each_serializer: serializer,
|
||||
order_cycle: order_cycle)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def products_scope
|
||||
products_relation = object.supplied_products
|
||||
if order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
object.supplied_products.visible_for(order_cycle.coordinator)
|
||||
else
|
||||
object.supplied_products
|
||||
products_relation = products_relation.visible_for(order_cycle.coordinator)
|
||||
end
|
||||
products_relation.order(:name)
|
||||
end
|
||||
|
||||
def products
|
||||
|
||||
@@ -14,7 +14,8 @@ class Api::Admin::ForOrderCycle::SuppliedProductSerializer < ActiveModel::Serial
|
||||
end
|
||||
|
||||
def variants
|
||||
variants = if order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
variants = if order_cycle.present? &&
|
||||
order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
object.variants.visible_for(order_cycle.coordinator)
|
||||
else
|
||||
object.variants
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
class Api::Admin::OrderSerializer < ActiveModel::Serializer
|
||||
attributes :id, :number, :user_id, :full_name, :email, :phone, :completed_at, :display_total,
|
||||
:edit_path, :state, :payment_state, :shipment_state,
|
||||
:payments_path, :ship_path, :ready_to_ship, :created_at,
|
||||
:distributor_name, :special_instructions, :payment_capture_path,
|
||||
:payments_path, :ready_to_ship, :ready_to_capture, :created_at,
|
||||
:distributor_name, :special_instructions,
|
||||
:item_total, :adjustment_total, :payment_total, :total
|
||||
|
||||
has_one :distributor, serializer: Api::Admin::IdSerializer
|
||||
@@ -28,15 +28,9 @@ class Api::Admin::OrderSerializer < ActiveModel::Serializer
|
||||
spree_routes_helper.admin_order_payments_path(object)
|
||||
end
|
||||
|
||||
def ship_path
|
||||
spree_routes_helper.fire_admin_order_path(object, e: 'ship')
|
||||
end
|
||||
|
||||
def payment_capture_path
|
||||
def ready_to_capture
|
||||
pending_payment = object.pending_payments.first
|
||||
return '' unless object.payment_required? && pending_payment
|
||||
|
||||
spree_routes_helper.fire_admin_order_payment_path(object, pending_payment.id, e: 'capture')
|
||||
object.payment_required? && pending_payment
|
||||
end
|
||||
|
||||
def ready_to_ship
|
||||
|
||||
23
app/services/action_callbacks.rb
Normal file
23
app/services/action_callbacks.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
class ActionCallbacks
|
||||
attr_reader :before_methods
|
||||
attr_reader :after_methods
|
||||
attr_reader :fails_methods
|
||||
|
||||
def initialize
|
||||
@before_methods = []
|
||||
@after_methods = []
|
||||
@fails_methods = []
|
||||
end
|
||||
|
||||
def before(method)
|
||||
@before_methods << method
|
||||
end
|
||||
|
||||
def after(method)
|
||||
@after_methods << method
|
||||
end
|
||||
|
||||
def fails(method)
|
||||
@fails_methods << method
|
||||
end
|
||||
end
|
||||
40
app/services/current_order_locker.rb
Normal file
40
app/services/current_order_locker.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
# Locks a controller's current order including its variants.
|
||||
#
|
||||
# It should be used when making major changes like checking out the order.
|
||||
# It can keep stock checking in sync and prevent overselling of an item.
|
||||
class CurrentOrderLocker
|
||||
# This interface follows the ActionController filters convention:
|
||||
#
|
||||
# https://guides.rubyonrails.org/action_controller_overview.html#filters
|
||||
#
|
||||
def self.around(controller)
|
||||
lock_order_and_variants(controller.current_order) { yield }
|
||||
end
|
||||
|
||||
# Locking will not prevent all access to these rows. Other processes are
|
||||
# only waiting if they try to lock one of these rows as well.
|
||||
#
|
||||
# https://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html
|
||||
#
|
||||
def self.lock_order_and_variants(order)
|
||||
return yield if order.nil?
|
||||
|
||||
order.with_lock do
|
||||
lock_variants_of(order)
|
||||
yield
|
||||
end
|
||||
end
|
||||
private_class_method :lock_order_and_variants
|
||||
|
||||
# There are many places in which stock is stored in the database. Row locking
|
||||
# on variant level ensures that there are no conflicts even when an item is
|
||||
# sold through multiple shops.
|
||||
def self.lock_variants_of(order)
|
||||
variant_ids = order.line_items.select(:variant_id)
|
||||
|
||||
# Ordering the variants by id prevents deadlocks. Plucking the ids sends
|
||||
# the locking query without building Spree::Variant objects.
|
||||
Spree::Variant.where(id: variant_ids).order(:id).lock.pluck(:id)
|
||||
end
|
||||
private_class_method :lock_variants_of
|
||||
end
|
||||
86
app/services/exchange_products_renderer.rb
Normal file
86
app/services/exchange_products_renderer.rb
Normal file
@@ -0,0 +1,86 @@
|
||||
class ExchangeProductsRenderer
|
||||
def initialize(order_cycle, user)
|
||||
@order_cycle = order_cycle
|
||||
@user = user
|
||||
end
|
||||
|
||||
def exchange_products(incoming, enterprise)
|
||||
if incoming
|
||||
products_for_incoming_exchange(enterprise)
|
||||
else
|
||||
products_for_outgoing_exchange
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def products_for_incoming_exchange(enterprise)
|
||||
supplied_products(enterprise.id)
|
||||
end
|
||||
|
||||
def supplied_products(enterprises_query_matcher)
|
||||
products_relation = Spree::Product.where(supplier_id: enterprises_query_matcher)
|
||||
|
||||
if @order_cycle.present? &&
|
||||
@order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
products_relation = products_relation.visible_for(@order_cycle.coordinator)
|
||||
end
|
||||
|
||||
products_relation
|
||||
end
|
||||
|
||||
def products_for_outgoing_exchange
|
||||
supplied_products(enterprises_for_outgoing_exchange.select(:id)).
|
||||
includes(:variants).
|
||||
where("spree_variants.id": incoming_exchanges_variants)
|
||||
end
|
||||
|
||||
def incoming_exchanges_variants
|
||||
return @incoming_exchanges_variants if @incoming_exchanges_variants.present?
|
||||
|
||||
@incoming_exchanges_variants = []
|
||||
visible_incoming_exchanges.each do |incoming_exchange|
|
||||
@incoming_exchanges_variants.push(
|
||||
*incoming_exchange.variants.merge(
|
||||
visible_incoming_variants(incoming_exchange.sender)
|
||||
).map(&:id).to_a
|
||||
)
|
||||
end
|
||||
@incoming_exchanges_variants
|
||||
end
|
||||
|
||||
def visible_incoming_exchanges
|
||||
OpenFoodNetwork::OrderCyclePermissions.
|
||||
new(@user, @order_cycle).
|
||||
visible_exchanges.
|
||||
by_enterprise_name.
|
||||
incoming
|
||||
end
|
||||
|
||||
def visible_incoming_variants(incoming_exchange_sender)
|
||||
variants_relation = permitted_incoming_variants(incoming_exchange_sender)
|
||||
|
||||
if @order_cycle.prefers_product_selection_from_coordinator_inventory_only?
|
||||
variants_relation = variants_relation.visible_for(@order_cycle.coordinator)
|
||||
end
|
||||
|
||||
variants_relation
|
||||
end
|
||||
|
||||
def permitted_incoming_variants(incoming_exchange_sender)
|
||||
OpenFoodNetwork::OrderCyclePermissions.
|
||||
new(@user, @order_cycle).
|
||||
visible_variants_for_incoming_exchanges_from(incoming_exchange_sender)
|
||||
end
|
||||
|
||||
def enterprises_for_outgoing_exchange
|
||||
enterprises = OpenFoodNetwork::OrderCyclePermissions.
|
||||
new(@user, @order_cycle)
|
||||
.visible_enterprises
|
||||
return enterprises if enterprises.empty?
|
||||
|
||||
enterprises.includes(
|
||||
supplied_products: [:supplier, :variants, master: [:images]]
|
||||
)
|
||||
end
|
||||
end
|
||||
98
app/services/permissions/order.rb
Normal file
98
app/services/permissions/order.rb
Normal file
@@ -0,0 +1,98 @@
|
||||
module Permissions
|
||||
class Order
|
||||
def initialize(user)
|
||||
@user = user
|
||||
@permissions = OpenFoodNetwork::Permissions.new(@user)
|
||||
end
|
||||
|
||||
# Find orders that the user can see
|
||||
def visible_orders
|
||||
Spree::Order.
|
||||
with_line_items_variants_and_products_outer.
|
||||
where(visible_orders_where_values)
|
||||
end
|
||||
|
||||
# Any orders that the user can edit
|
||||
def editable_orders
|
||||
Spree::Order.where(
|
||||
managed_orders_where_values.
|
||||
or(coordinated_orders_where_values)
|
||||
)
|
||||
end
|
||||
|
||||
def visible_line_items
|
||||
Spree::LineItem.where(id:
|
||||
editable_line_items.select(:id) |
|
||||
produced_line_items.select("spree_line_items.id"))
|
||||
end
|
||||
|
||||
# Any line items that I can edit
|
||||
def editable_line_items
|
||||
Spree::LineItem.where(order_id: editable_orders.select(:id))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def visible_orders_where_values
|
||||
# Grouping keeps the 2 where clauses from produced_orders_where_values inside parentheses
|
||||
# This way it makes the OR work between the 3 types of orders:
|
||||
# produced, managed and coordinated
|
||||
Spree::Order.arel_table.
|
||||
grouping(produced_orders_where_values).
|
||||
or(managed_orders_where_values).
|
||||
or(coordinated_orders_where_values)
|
||||
end
|
||||
|
||||
# Any orders placed through any hub that I manage
|
||||
def managed_orders_where_values
|
||||
Spree::Order.
|
||||
where(distributor_id: @permissions.managed_enterprises.select("enterprises.id")).
|
||||
where_values.
|
||||
reduce(:and)
|
||||
end
|
||||
|
||||
# Any order that is placed through an order cycle one of my managed enterprises coordinates
|
||||
def coordinated_orders_where_values
|
||||
Spree::Order.
|
||||
where(order_cycle_id: @permissions.coordinated_order_cycles.select(:id)).
|
||||
where_values.
|
||||
reduce(:and)
|
||||
end
|
||||
|
||||
def produced_orders_where_values
|
||||
Spree::Order.with_line_items_variants_and_products_outer.
|
||||
where(
|
||||
distributor_id: granted_distributor_ids,
|
||||
spree_products: { supplier_id: enterprises_with_associated_orders }
|
||||
).
|
||||
where_values.
|
||||
reduce(:and)
|
||||
end
|
||||
|
||||
def enterprises_with_associated_orders
|
||||
# Any orders placed through hubs that my producers have granted P-OC,
|
||||
# and which contain their products. This is pretty complicated but it's looking for order
|
||||
# where at least one of my producers has granted P-OC to the distributor
|
||||
# AND the order contains products of at least one of THE SAME producers
|
||||
@permissions.related_enterprises_granting(:add_to_order_cycle, to: granted_distributor_ids).
|
||||
merge(@permissions.managed_enterprises.is_primary_producer)
|
||||
end
|
||||
|
||||
def granted_distributor_ids
|
||||
@granted_distributor_ids ||= @permissions.related_enterprises_granted(
|
||||
:add_to_order_cycle,
|
||||
by: @permissions.managed_enterprises.is_primary_producer.select("enterprises.id")
|
||||
).select("enterprises.id")
|
||||
end
|
||||
|
||||
# Any from visible orders, where the product is produced by one of my managed producers
|
||||
def produced_line_items
|
||||
Spree::LineItem.where(order_id: visible_orders.select("DISTINCT spree_orders.id")).
|
||||
joins(:product).
|
||||
where(spree_products:
|
||||
{
|
||||
supplier_id: @permissions.managed_enterprises.is_primary_producer.select("enterprises.id")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -66,7 +66,7 @@ class ProductsRenderer
|
||||
.split(",").map { |id| "spree_products.primary_taxon_id=#{id} DESC" }
|
||||
.join(", ") + ", spree_products.name ASC, spree_products.id ASC"
|
||||
else
|
||||
"spree_products.name ASC"
|
||||
"spree_products.name ASC, spree_products.id"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class SearchOrders
|
||||
attr_reader :params, :current_user
|
||||
|
||||
def fetch_orders
|
||||
@search = OpenFoodNetwork::Permissions.new(current_user).editable_orders.ransack(params[:q])
|
||||
@search = ::Permissions::Order.new(current_user).editable_orders.ransack(params[:q])
|
||||
|
||||
return paginated_results if using_pagination?
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
%tr{ ng: { class: "'#{type} #{type}-{{ exchange.enterprise_id }}'" } }
|
||||
%td{:class => "#{type}_name"} {{ enterprises[exchange.enterprise_id].name }}
|
||||
%td.products.panel-toggle.text-center{ name: "products" }
|
||||
{{ exchangeSelectedVariants(exchange) }} /
|
||||
- if type == 'supplier'
|
||||
{{ enterpriseTotalVariants(enterprises[exchange.enterprise_id]) }}
|
||||
- else
|
||||
{{ (incomingExchangeVariantsFor(exchange.enterprise_id)).length }}
|
||||
{{ exchangeSelectedVariants(exchange) }} / {{ exchangeTotalVariants(exchange) }}
|
||||
= t('.selected')
|
||||
- if type == 'supplier'
|
||||
%td.receival-details
|
||||
@@ -14,7 +10,7 @@
|
||||
%td.tags.panel-toggle.text-center{ name: "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', '', '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
|
||||
= text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'ng-init' => 'setPickupTimeFieldDirty($index)', '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
|
||||
%span.icon-question-sign{'ofn-with-tip' => t('.pickup_time_tip')}
|
||||
%br/
|
||||
= text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_instructions', 'placeholder' => t('.pickup_instructions_placeholder'), 'ng-model' => 'exchange.pickup_instructions', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator'
|
||||
@@ -34,11 +30,11 @@
|
||||
|
||||
- if type == 'supplier'
|
||||
%tr.panel-row{ object: "exchange",
|
||||
panels: "{products: 'exchange_supplied_products'}",
|
||||
locals: "$index,order_cycle,exchange,enterprises,setExchangeVariants,suppliedVariants,removeDistributionOfVariant",
|
||||
panels: "{products: 'exchange_products_supplied'}",
|
||||
locals: "$index,order_cycle,exchange,enterprises,setExchangeVariants,selectAllVariants,suppliedVariants,removeDistributionOfVariant,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
|
||||
colspan: 4 }
|
||||
- if type == 'distributor'
|
||||
%tr.panel-row{ object: "exchange",
|
||||
panels: "{products: 'exchange_distributed_products', tags: 'exchange_tags'}",
|
||||
locals: "$index,order_cycle,exchange,supplied_products,setExchangeVariants,incomingExchangeVariantsFor,productSuppliedToOrderCycle,variantSuppliedToOrderCycle",
|
||||
panels: "{products: 'exchange_products_distributed', tags: 'exchange_tags'}",
|
||||
locals: "$index,order_cycle,exchange,enterprises,setExchangeVariants,incomingExchangeVariantsFor,variantSuppliedToOrderCycle,initializeExchangeProductsPanel,loadMoreExchangeProducts,loadAllExchangeProducts,productsLoading",
|
||||
colspan: 5 }
|
||||
|
||||
@@ -1,49 +1,9 @@
|
||||
= render 'wizard_progress'
|
||||
|
||||
%fieldset.no-border-bottom
|
||||
%legend{ align: 'center'}= t('.general_settings')
|
||||
|
||||
= render 'name_and_timing_form', f: f
|
||||
|
||||
-if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator
|
||||
= render 'coordinator_fees', f: f
|
||||
|
||||
%h2= t('.incoming')
|
||||
%table.exchanges
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.supplier')
|
||||
%th
|
||||
=t('.products')
|
||||
= surround '(', ')' do
|
||||
%a{href: '#', 'ng-click' => "OrderCycle.toggleAllProducts('incoming')"}
|
||||
%span{'ng-show' => "OrderCycle.showProducts['incoming']"}= t(:collapse_all)
|
||||
%span{'ng-hide' => "OrderCycle.showProducts['incoming']"}= t(:expand_all)
|
||||
%th= t('.receival_details')
|
||||
%th= t('.fees')
|
||||
%th.actions
|
||||
%tbody.panel-ctrl{ object: 'exchange', 'ng-repeat' => 'exchange in order_cycle.incoming_exchanges'}
|
||||
= render 'exchange_form', f: f, type: 'supplier'
|
||||
|
||||
- if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator
|
||||
= render 'add_exchange_form', f: f, type: 'supplier'
|
||||
|
||||
%h2= t('.outgoing')
|
||||
%table.exchanges
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.distributor')
|
||||
%th
|
||||
= t('.products')
|
||||
= surround '(', ')' do
|
||||
%a{href: '#', 'ng-click' => "OrderCycle.toggleAllProducts('outgoing')"}
|
||||
%span{'ng-show' => "OrderCycle.showProducts['outgoing']"}= t(:collapse_all)
|
||||
%span{'ng-hide' => "OrderCycle.showProducts['outgoing']"}= t(:expand_all)
|
||||
%th{ ng: { if: 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } }
|
||||
= t('.tags')
|
||||
%th= t('.delivery_details')
|
||||
%th= t('.fees')
|
||||
%th.actions
|
||||
%tbody.panel-ctrl{ object: 'exchange', 'ng-repeat' => 'exchange in order_cycle.outgoing_exchanges'}
|
||||
= render 'exchange_form', f: f, type: 'distributor'
|
||||
|
||||
- if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator
|
||||
= render 'add_exchange_form', f: f, type: 'distributor'
|
||||
|
||||
.actions
|
||||
%span{'ng-hide' => 'loaded()'}= t(:loading)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
%table.exchanges
|
||||
%tbody{ng: {repeat: "exchange in order_cycle.incoming_exchanges"}}
|
||||
%tr.products
|
||||
%td{ ng: { include: "'admin/panels/exchange_supplied_products.html'" } }
|
||||
%td{ ng: { include: "'admin/panels/exchange_products_simple.html'" } }
|
||||
|
||||
%br/
|
||||
= label_tag t('.fees')
|
||||
|
||||
18
app/views/admin/order_cycles/_wizard_progress.html.haml
Normal file
18
app/views/admin/order_cycles/_wizard_progress.html.haml
Normal file
@@ -0,0 +1,18 @@
|
||||
%ul.wizard-progress
|
||||
- if action_name == 'new'
|
||||
%li{ class: "current" }
|
||||
= t("admin.order_cycles.wizard_progress.edit")
|
||||
%li
|
||||
= t("admin.order_cycles.wizard_progress.incoming")
|
||||
%li
|
||||
= t("admin.order_cycles.wizard_progress.outgoing")
|
||||
- else
|
||||
%li{ class: "#{'current' if action_name == 'edit'}" }
|
||||
%a{ href: main_app.edit_admin_order_cycle_path(@order_cycle) }
|
||||
= t("admin.order_cycles.wizard_progress.edit")
|
||||
%li{ class: "#{'current' if action_name == 'incoming'}" }
|
||||
%a{ href: main_app.admin_order_cycle_incoming_path(@order_cycle) }
|
||||
= t("admin.order_cycles.wizard_progress.incoming")
|
||||
%li{ class: "#{'current' if action_name == 'outgoing'}" }
|
||||
%a{ href: main_app.admin_order_cycle_outgoing_path(@order_cycle) }
|
||||
= t("admin.order_cycles.wizard_progress.outgoing")
|
||||
@@ -21,7 +21,7 @@
|
||||
#advanced_settings{ hidden: true }
|
||||
= render partial: "/admin/order_cycles/advanced_settings"
|
||||
|
||||
%h1
|
||||
- content_for :page_title do
|
||||
= t :edit_order_cycle
|
||||
|
||||
- ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl'
|
||||
@@ -29,9 +29,13 @@
|
||||
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f|
|
||||
|
||||
%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }
|
||||
%input.red{ type: "button", value: t(:update), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input.red{ type: "button", value: t('.update_and_close'), ng: { click: "submit($event, '#{main_app.admin_order_cycles_path}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t(:cancel)}' : '#{t(:close)}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } }
|
||||
%input.red{ type: "button", value: t('.save'), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
- if order_cycles_simple_form
|
||||
%input.red{ type: "button", value: t('.save_and_back_to_list'), ng: { click: "submit($event, '#{main_app.admin_order_cycles_path}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
- else
|
||||
%input.red{ type: "button", value: t('.save_and_next'), ng: { click: "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input{ type: "button", value: t('.next'), ng: { click: "cancel('#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", disabled: "order_cycle_form.$dirty" } }
|
||||
%input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } }
|
||||
|
||||
- if order_cycles_simple_form
|
||||
= render 'simple_form', f: f
|
||||
|
||||
38
app/views/admin/order_cycles/incoming.html.haml
Normal file
38
app/views/admin/order_cycles/incoming.html.haml
Normal file
@@ -0,0 +1,38 @@
|
||||
- content_for :page_title do
|
||||
= t :edit_order_cycle
|
||||
|
||||
= admin_inject_order_cycle_instance
|
||||
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => 'AdminOrderCycleIncomingCtrl', name: 'order_cycle_form'} do |f|
|
||||
|
||||
= render 'wizard_progress'
|
||||
|
||||
%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }
|
||||
%input.red{ type: "button", value: t('.save'), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input.red{ type: "button", value: t('.save_and_next'), ng: { click: "submit($event, '#{main_app.admin_order_cycle_outgoing_path(@order_cycle)}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input{ type: "button", value: t('.next'), ng: { click: "cancel('#{main_app.admin_order_cycle_outgoing_path(@order_cycle)}')", disabled: "order_cycle_form.$dirty" } }
|
||||
%input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } }
|
||||
|
||||
%fieldset.no-border-bottom
|
||||
%legend{ align: 'center'}= t('.incoming')
|
||||
|
||||
%table.exchanges
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.supplier')
|
||||
%th
|
||||
= t('.products')
|
||||
= surround '(', ')' do
|
||||
%a{href: '#', 'ng-click' => "OrderCycle.toggleAllProducts('incoming')"}
|
||||
%span{'ng-show' => "OrderCycle.showProducts['incoming']"}= t(:collapse_all)
|
||||
%span{'ng-hide' => "OrderCycle.showProducts['incoming']"}= t(:expand_all)
|
||||
%th= t('.receival_details')
|
||||
%th= t('.fees')
|
||||
%th.actions
|
||||
%tbody.panel-ctrl{ object: 'exchange', 'ng-repeat' => 'exchange in order_cycle.incoming_exchanges'}
|
||||
= render 'exchange_form', f: f, type: 'supplier'
|
||||
|
||||
- if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator
|
||||
= render 'add_exchange_form', f: f, type: 'supplier'
|
||||
|
||||
.actions
|
||||
%span{'ng-hide' => 'loaded()'}= t(:loading)
|
||||
@@ -7,8 +7,10 @@
|
||||
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f|
|
||||
|
||||
%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }
|
||||
%input.red{ type: "button", value: t(:create), ng: { click: "submit($event, '#{main_app.admin_order_cycles_path}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input{ type: "button", ng: { value: "order_cycle_form.$dirty ? 'Cancel' : 'Close'", click: "cancel('#{main_app.admin_order_cycles_path}')" } }
|
||||
- if order_cycles_simple_form
|
||||
- custom_redirect_path = main_app.admin_order_cycles_path
|
||||
%input.red{ type: "button", value: t('.create'), ng: { click: "submit($event, '#{custom_redirect_path}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } }
|
||||
|
||||
- if order_cycles_simple_form
|
||||
= render 'simple_form', f: f
|
||||
|
||||
39
app/views/admin/order_cycles/outgoing.html.haml
Normal file
39
app/views/admin/order_cycles/outgoing.html.haml
Normal file
@@ -0,0 +1,39 @@
|
||||
- content_for :page_title do
|
||||
= t :edit_order_cycle
|
||||
|
||||
= admin_inject_order_cycle_instance
|
||||
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => 'AdminOrderCycleOutgoingCtrl', name: 'order_cycle_form'} do |f|
|
||||
|
||||
= render 'wizard_progress'
|
||||
|
||||
%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }
|
||||
%input.red{ type: "button", value: t('.save'), ng: { click: "submit($event, null)", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input.red{ type: "button", value: t('.save_and_back_to_list'), ng: { click: "submit($event, '#{main_app.admin_order_cycles_path}')", disabled: "!order_cycle_form.$dirty || order_cycle_form.$invalid" } }
|
||||
%input{ type: "button", ng: { value: "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", click: "cancel('#{main_app.admin_order_cycles_path}')" } }
|
||||
|
||||
%fieldset.no-border-bottom
|
||||
%legend{ align: 'center'}= t('.outgoing')
|
||||
|
||||
%table.exchanges
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.distributor')
|
||||
%th
|
||||
= t('.products')
|
||||
= surround '(', ')' do
|
||||
%a{href: '#', 'ng-click' => "OrderCycle.toggleAllProducts('outgoing')"}
|
||||
%span{'ng-show' => "OrderCycle.showProducts['outgoing']"}= t(:collapse_all)
|
||||
%span{'ng-hide' => "OrderCycle.showProducts['outgoing']"}= t(:expand_all)
|
||||
%th{ ng: { if: 'enterprises[exchange.enterprise_id].managed || order_cycle.viewing_as_coordinator' } }
|
||||
= t('.tags')
|
||||
%th= t('.delivery_details')
|
||||
%th= t('.fees')
|
||||
%th.actions
|
||||
%tbody.panel-ctrl{ object: 'exchange', 'ng-repeat' => 'exchange in order_cycle.outgoing_exchanges'}
|
||||
= render 'exchange_form', f: f, type: 'distributor'
|
||||
|
||||
- if Enterprise.managed_by(spree_current_user).include? @order_cycle.coordinator
|
||||
= render 'add_exchange_form', f: f, type: 'distributor'
|
||||
|
||||
.actions
|
||||
%span{'ng-hide' => 'loaded()'}= t(:loading)
|
||||
@@ -8,7 +8,7 @@
|
||||
%p
|
||||
= t(".email_userguide_html", link: link_to(t(".userguide"), ContentConfig.user_guide_link))
|
||||
%p
|
||||
= t(".email_admin_html", link: link_to(t(".admin_panel"), spree.admin_url))
|
||||
= t(".email_admin_html", link: link_to(t(".admin_panel"), spree.admin_dashboard_url))
|
||||
|
||||
%p
|
||||
= t(".email_community_html", link: link_to(t(".join_community"), ContentConfig.community_forum_url))
|
||||
|
||||
@@ -8,38 +8,6 @@
|
||||
.small-12.medium-8.medium-offset-2.columns.text-center
|
||||
.alert-box
|
||||
= render 'shared/register_call'
|
||||
.row
|
||||
.small-12.medium-4.medium-offset-2.columns.text-center
|
||||
%h6
|
||||
= t '.footer_global_headline'
|
||||
%p
|
||||
%a{href: "http://www.openfoodnetwork.org", target: "_blank"}
|
||||
= t '.footer_global_home'
|
||||
%span |
|
||||
%a{href: "http://www.openfoodnetwork.org/news/", target: "_blank"}
|
||||
= t '.footer_global_news'
|
||||
%span |
|
||||
%a{href: "http://www.openfoodnetwork.org/about/history-team/", target: "_blank"}
|
||||
= t '.footer_global_about'
|
||||
%span |
|
||||
%a{href: "http://www.openfoodnetwork.org/contact/", target: "_blank"}
|
||||
= t '.footer_global_contact'
|
||||
|
||||
.small-12.medium-4.columns.text-center
|
||||
%h6
|
||||
= t '.footer_sites_headline'
|
||||
%p
|
||||
%a{href: "http://dev.openfoodnetwork.org", target: "_blank"}
|
||||
= t '.footer_sites_developer'
|
||||
%span |
|
||||
%a{href: "http://community.openfoodnetwork.org", target: "_blank"}
|
||||
= t '.footer_sites_community'
|
||||
%span |
|
||||
%a{href: "http://www.openfoodnetwork.org/platform/user-guide/", target: "_blank"}
|
||||
= t '.footer_sites_userguide'
|
||||
|
||||
.medium-2.columns.text-center
|
||||
/ Placeholder
|
||||
|
||||
.footer-local
|
||||
.row
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
%section.right{"ng-cloak" => true}
|
||||
%span.cart-span{"ng-controller" => "CartCtrl", "ng-class" => "{ dirty: Cart.dirty || Cart.empty(), 'pure-dirty': Cart.dirty }"}
|
||||
%a.icon{href: main_app.checkout_path}
|
||||
%a.icon{href: main_app.cart_path}
|
||||
%span
|
||||
= t '.cart'
|
||||
%span.count
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
- if admin_user? or enterprise_user?
|
||||
%li
|
||||
%a{href: spree.admin_path, target:'_blank'}
|
||||
%a{href: spree.admin_dashboard_path, target:'_blank'}
|
||||
%i.ofn-i_021-tools
|
||||
= t 'label_administration'
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
- if admin_user? or enterprise_user?
|
||||
%li
|
||||
%a{href: spree.admin_path, target:'_blank'}
|
||||
%a{href: spree.admin_dashboard_path, target:'_blank'}
|
||||
%i.ofn-i_021-tools
|
||||
= t 'label_admin'
|
||||
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
|
||||
= render :partial => 'adjustments_table'
|
||||
|
||||
= button_link_to t(:continue), @order.cart? ? new_admin_order_payment_url(@order) : admin_orders_url, :icon => 'icon-arrow-right'
|
||||
= button_link_to t(:continue), admin_order_payments_url(@order), :icon => 'icon-arrow-right'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user