mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-19 19:46:51 +00:00
Compare commits
303 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a756cbc6b | ||
|
|
00e869f40c | ||
|
|
e849c4867b | ||
|
|
ada34d27ca | ||
|
|
0288dfc992 | ||
|
|
02c0b89fa0 | ||
|
|
c9e23154d8 | ||
|
|
279b633513 | ||
|
|
9d0ac79983 | ||
|
|
7b0c55e15a | ||
|
|
cbe2477d04 | ||
|
|
c730958fe4 | ||
|
|
37e5e1923c | ||
|
|
542c1bf684 | ||
|
|
06c896b93b | ||
|
|
6433d69d02 | ||
|
|
bf8c632fce | ||
|
|
b3c89a9d6c | ||
|
|
d45403f1d4 | ||
|
|
f2affe80cd | ||
|
|
573a69477f | ||
|
|
c6ce516129 | ||
|
|
2539b84b33 | ||
|
|
dd6d0d25da | ||
|
|
c54cff10d4 | ||
|
|
ab330e882e | ||
|
|
20cabe6f12 | ||
|
|
fe849b8dfd | ||
|
|
167b44c30f | ||
|
|
ad247e2116 | ||
|
|
e46875376e | ||
|
|
d7223c508a | ||
|
|
a91a23aa39 | ||
|
|
370f66e36b | ||
|
|
f7cdcf02e0 | ||
|
|
1ee43fb495 | ||
|
|
e139d24639 | ||
|
|
0dfe749496 | ||
|
|
d6eade6fa3 | ||
|
|
109aed684e | ||
|
|
35d76ac991 | ||
|
|
1ab9e9d7a5 | ||
|
|
e5e716e150 | ||
|
|
b6510c3ab6 | ||
|
|
a158a986c6 | ||
|
|
c7643db66f | ||
|
|
eb8c22aa06 | ||
|
|
d0656485d7 | ||
|
|
640cc1b6d3 | ||
|
|
d65d17a9f3 | ||
|
|
bbea00e431 | ||
|
|
3cecba70e8 | ||
|
|
8bbff09066 | ||
|
|
a7a89d7ccb | ||
|
|
3ccf76ff5f | ||
|
|
6004208496 | ||
|
|
3ce9c712cf | ||
|
|
bfb0032fd2 | ||
|
|
fe37516ead | ||
|
|
289b75e143 | ||
|
|
09f0f8c33f | ||
|
|
8bc1718978 | ||
|
|
cbfce69a6d | ||
|
|
4e0ecdd44c | ||
|
|
23ab9a4bed | ||
|
|
f623446e3e | ||
|
|
6944fe1e46 | ||
|
|
7c9e3d7f06 | ||
|
|
da7456e6e0 | ||
|
|
f134cd9473 | ||
|
|
bdcadf9fc6 | ||
|
|
9b7139fd45 | ||
|
|
543e275d2e | ||
|
|
560fa6b949 | ||
|
|
dbf34da87b | ||
|
|
d6022062e1 | ||
|
|
b082d3301b | ||
|
|
48cd542138 | ||
|
|
e2d341c9c2 | ||
|
|
89873a2640 | ||
|
|
b4be2cc2d4 | ||
|
|
35f89a9750 | ||
|
|
592a53b6f5 | ||
|
|
eab9d42eb0 | ||
|
|
cf05369ca9 | ||
|
|
e96252f2ed | ||
|
|
06e1f56ae9 | ||
|
|
fe0de98821 | ||
|
|
5077b0e625 | ||
|
|
1299c0b5eb | ||
|
|
a2bc61cb4d | ||
|
|
057572a445 | ||
|
|
3403ab4162 | ||
|
|
d997b8f5ee | ||
|
|
9a036d18b7 | ||
|
|
f2b57057cd | ||
|
|
95bc8fa984 | ||
|
|
54c44c33eb | ||
|
|
8a732568e7 | ||
|
|
797a3ad091 | ||
|
|
f4523f0305 | ||
|
|
56a7a49ea2 | ||
|
|
7d1ae3bf67 | ||
|
|
b9aa4e11da | ||
|
|
01d1e8243c | ||
|
|
a1a5c3b7fe | ||
|
|
bc826f73a1 | ||
|
|
96428c11c6 | ||
|
|
c15660da0e | ||
|
|
9ea05b0794 | ||
|
|
c5b939c8ee | ||
|
|
c5a17bcde0 | ||
|
|
b712ec7f13 | ||
|
|
32a7f13dd2 | ||
|
|
713769b497 | ||
|
|
495de37620 | ||
|
|
05d24cf11a | ||
|
|
edd84530af | ||
|
|
6677543de0 | ||
|
|
30aa31252b | ||
|
|
b83d74a609 | ||
|
|
d31b50be3d | ||
|
|
42e3f2f2f4 | ||
|
|
96737da128 | ||
|
|
7f9f0d840c | ||
|
|
686840e262 | ||
|
|
2377b833ee | ||
|
|
3f3c33bce6 | ||
|
|
e35eff95bb | ||
|
|
e6408161db | ||
|
|
4540b32d3a | ||
|
|
9f8d2fddbc | ||
|
|
14047c620a | ||
|
|
2be1c231f7 | ||
|
|
c34646724c | ||
|
|
b731635270 | ||
|
|
c9a62fad4c | ||
|
|
70169d477c | ||
|
|
d95646ea98 | ||
|
|
d50bf928e1 | ||
|
|
3f7aff4b8b | ||
|
|
de9476a8a5 | ||
|
|
5fded022d4 | ||
|
|
bce41a2247 | ||
|
|
1f644f4020 | ||
|
|
cc11d4e5f0 | ||
|
|
5ecac77aa5 | ||
|
|
a5d2579c69 | ||
|
|
112adb11db | ||
|
|
5fdc11bdc7 | ||
|
|
c038b485b1 | ||
|
|
19eb93012c | ||
|
|
d998ec8453 | ||
|
|
0c93665030 | ||
|
|
c4d298d732 | ||
|
|
36883bc051 | ||
|
|
8c252fc160 | ||
|
|
c2f8803d72 | ||
|
|
07967275d8 | ||
|
|
c7db1b7fc0 | ||
|
|
fa57d71a23 | ||
|
|
be5e7bd18d | ||
|
|
5cfc2613ac | ||
|
|
903eb23122 | ||
|
|
d162e8c087 | ||
|
|
75a56c2be5 | ||
|
|
07a5963554 | ||
|
|
a6888cf0be | ||
|
|
ef35db6db2 | ||
|
|
375d4fbffd | ||
|
|
6153789055 | ||
|
|
032741c54f | ||
|
|
e9acf6e0de | ||
|
|
d5e90c3c6c | ||
|
|
7e7429446d | ||
|
|
535e389fb4 | ||
|
|
cecebb82f4 | ||
|
|
49665ae360 | ||
|
|
7e5c194515 | ||
|
|
fe0b3172c7 | ||
|
|
5b27ed6b9f | ||
|
|
bef4741e31 | ||
|
|
d604328bfb | ||
|
|
2af3de51c7 | ||
|
|
d0c77a8dc4 | ||
|
|
8fc4ca6f65 | ||
|
|
721a0d3a98 | ||
|
|
c46fe7b675 | ||
|
|
bb372984e1 | ||
|
|
2489a2f5f5 | ||
|
|
a6698230fe | ||
|
|
50731e929e | ||
|
|
baa09b88f7 | ||
|
|
2f3509bf9b | ||
|
|
e8f9ace8e6 | ||
|
|
04270b6cec | ||
|
|
1155931ea9 | ||
|
|
efd450606e | ||
|
|
2f60a85593 | ||
|
|
78cf35807a | ||
|
|
f3bc038c05 | ||
|
|
7817a40a35 | ||
|
|
a18f6f5b8f | ||
|
|
403653381c | ||
|
|
eb85dccac1 | ||
|
|
f32454b404 | ||
|
|
591efecde6 | ||
|
|
24afa21885 | ||
|
|
cd6d34663e | ||
|
|
c66e5fe1e9 | ||
|
|
4ec8fcd3a6 | ||
|
|
88893a2968 | ||
|
|
62599e8dfc | ||
|
|
b97c428f72 | ||
|
|
42b074f02e | ||
|
|
07e0951490 | ||
|
|
fc433ff8f0 | ||
|
|
8e33437fbb | ||
|
|
a5103c737d | ||
|
|
37c8f42244 | ||
|
|
0e8765636b | ||
|
|
6855e5c8f4 | ||
|
|
a96b56239c | ||
|
|
4f9eee2682 | ||
|
|
44761315c2 | ||
|
|
0d18b1a032 | ||
|
|
cc0dabcd1e | ||
|
|
e79c672263 | ||
|
|
bc6f7934e0 | ||
|
|
89029f46f9 | ||
|
|
144369e55f | ||
|
|
3833cbbf73 | ||
|
|
5b703a02cd | ||
|
|
1d4aebd3f9 | ||
|
|
845a764320 | ||
|
|
c66579a22f | ||
|
|
9291bf5c82 | ||
|
|
4781ab93ef | ||
|
|
def9ab7c47 | ||
|
|
2a68d34fb0 | ||
|
|
2ce56aef2c | ||
|
|
20f965731d | ||
|
|
8378dce752 | ||
|
|
d7a4e3a896 | ||
|
|
7d9de0ca70 | ||
|
|
f9c6f09cd4 | ||
|
|
4a83eca832 | ||
|
|
4954db64b3 | ||
|
|
b2c4b97d94 | ||
|
|
32c1eecece | ||
|
|
6d8c7a4bee | ||
|
|
bd2045cad7 | ||
|
|
e1c3d2442a | ||
|
|
3df65d0463 | ||
|
|
822a17f732 | ||
|
|
ffde0307e2 | ||
|
|
9e57807dc2 | ||
|
|
052bac196b | ||
|
|
00e789b95b | ||
|
|
a004822019 | ||
|
|
49f7b4c8e2 | ||
|
|
8b503b51ca | ||
|
|
4e431ecf18 | ||
|
|
81f8b127db | ||
|
|
f9f81086fd | ||
|
|
9807b432f9 | ||
|
|
6b0f3cd4f3 | ||
|
|
3c412b7b7d | ||
|
|
c4e6c0af2b | ||
|
|
e6387197ec | ||
|
|
746c4f03f6 | ||
|
|
37abdb7727 | ||
|
|
bdb3dd5aaf | ||
|
|
25fbab2e37 | ||
|
|
4d37aaac64 | ||
|
|
19e28cb14a | ||
|
|
a44a251d96 | ||
|
|
2921ee19e1 | ||
|
|
6796d91a07 | ||
|
|
69afcf7510 | ||
|
|
47056e3cc1 | ||
|
|
91a52ead58 | ||
|
|
caebcdf184 | ||
|
|
c66a659e8e | ||
|
|
b8b107dd2d | ||
|
|
3d32987227 | ||
|
|
902e51e580 | ||
|
|
743cb3b33a | ||
|
|
74e49787f4 | ||
|
|
afa8f97627 | ||
|
|
6e677fecce | ||
|
|
889a3e6d9d | ||
|
|
664be47ac3 | ||
|
|
0e4b5ad9ec | ||
|
|
5634f4af18 | ||
|
|
0dede39617 | ||
|
|
377b4a3755 | ||
|
|
0c809b2911 | ||
|
|
33472014f1 | ||
|
|
d038c27f1f | ||
|
|
3d718d9738 | ||
|
|
a2e6b84db2 | ||
|
|
d319f97733 |
66
.github/ISSUE_TEMPLATE.md
vendored
66
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,66 +0,0 @@
|
||||
<!-- Provide a general summary of the issue in the Title above.
|
||||
|
||||
If your issue is not a bug, please use the Feature template instead:
|
||||
https://github.com/openfoodfoundation/openfoodnetwork/wiki/Feature-template
|
||||
|
||||
-->
|
||||
## Description
|
||||
<!-- Provide a more detailed introduction to the issue itself, and why you consider it to be a bug -->
|
||||
|
||||
|
||||
|
||||
## Expected Behavior
|
||||
<!-- Tell us what should happen -->
|
||||
|
||||
|
||||
|
||||
## Actual Behavior
|
||||
<!-- Tell us what happens instead -->
|
||||
|
||||
|
||||
|
||||
## Steps to Reproduce
|
||||
<!-- Provide an unambiguous set of steps to reproduce this bug -->
|
||||
<!-- Include code to reproduce if relevant -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Animated Gif/Screenshot
|
||||
<!-- Provide a screenshot or brief animated gif reproducing the bug. Linux users can use
|
||||
[Peek](https://github.com/phw/peek#ubuntu) while Mac users can use [Recordit](http://recordit.co/) -->
|
||||
|
||||
|
||||
|
||||
## Context
|
||||
<!-- How has this bug affected you? What were you trying to accomplish? -->
|
||||
|
||||
|
||||
|
||||
## Severity
|
||||
<!-- Assign a label and explain the impact.
|
||||
|
||||
bug-s1: a critical feature is broken: checkout, payments, signup, login
|
||||
bug-s2: a non-critical feature is broken, no workaround
|
||||
bug-s3: a feature is broken but there is a workaround
|
||||
bug-s4: it's annoying, but you can use it
|
||||
bug-s5: we can live with it, only a few users impacted
|
||||
|
||||
https://github.com/openfoodfoundation/openfoodnetwork/wiki/Bug-severity
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## Your Environment
|
||||
<!-- Include relevant details about the environment you experienced the bug in -->
|
||||
|
||||
* Version used:
|
||||
* Browser name and version:
|
||||
* Operating System and version (desktop or mobile):
|
||||
|
||||
## Possible Fix
|
||||
<!-- Not obligatory, but suggest a fix or reason for the bug -->
|
||||
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/bug_report.md
vendored
14
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -9,6 +9,7 @@ assignees: ''
|
||||
|
||||
## Description
|
||||
<!-- Provide a more detailed introduction to the issue itself, and why you consider it to be a bug -->
|
||||
<!-- How has this bug affected you? What were you trying to accomplish? -->
|
||||
|
||||
|
||||
## Expected Behavior
|
||||
@@ -22,6 +23,8 @@ assignees: ''
|
||||
## Steps to Reproduce
|
||||
<!-- Provide an unambiguous set of steps to reproduce this bug -->
|
||||
<!-- Include code to reproduce if relevant -->
|
||||
<!-- Include links -->
|
||||
<!-- Include user ID -->
|
||||
|
||||
1.
|
||||
2.
|
||||
@@ -29,13 +32,11 @@ assignees: ''
|
||||
4.
|
||||
|
||||
## Animated Gif/Screenshot
|
||||
<!-- Provide a screenshot or brief animated gif reproducing the bug. Linux users can use
|
||||
[Peek](https://github.com/phw/peek#ubuntu) while Mac users can use [Recordit](http://recordit.co/) -->
|
||||
|
||||
|
||||
## Context
|
||||
<!-- How has this bug affected you? What were you trying to accomplish? -->
|
||||
<!-- Provide a screenshot or brief video reproducing the bug. -->
|
||||
<!-- Please try to have the dev tools opened on the network tab (press F12 to open the devtools of your browser -->
|
||||
|
||||
## Workaround
|
||||
<!-- Include a workaround for this bug (if relevant) -->
|
||||
|
||||
## Severity
|
||||
<!-- Assign a label and explain the impact.
|
||||
@@ -55,7 +56,6 @@ https://github.com/openfoodfoundation/openfoodnetwork/wiki/Bug-severity
|
||||
* Version used:
|
||||
* Browser name and version:
|
||||
* Operating System and version (desktop or mobile):
|
||||
* OFN Platform instance where you discovered the bug, and which version of the software they are using.
|
||||
|
||||
## Possible Fix
|
||||
<!-- Not obligatory, but suggest a fix or reason for the bug -->
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/story-template.md
vendored
11
.github/ISSUE_TEMPLATE/story-template.md
vendored
@@ -14,5 +14,12 @@ assignees: ''
|
||||
**- I want to be able to do:** (specify the desired behavior)
|
||||
(Link to others issues or resources to provide context > only if really necessary). -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- Document the outcomes that need to be achieved before this component can be considered complete. -->
|
||||
## Acceptance Criteria & Tests
|
||||
<!-- Document the outcomes that need to be achieved before this component can be considered complete.
|
||||
-->
|
||||
<!-- Provide an unambiguous set of steps a tester should do to validate the PR that will solve this issue -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
@@ -110,18 +110,6 @@ Metrics/LineLength:
|
||||
- app/models/variant_override.rb
|
||||
- app/models/variant_override_set.rb
|
||||
- app/overrides/add_enterprise_fees_to_admin_configurations_menu.rb
|
||||
- app/serializers/api/admin/basic_enterprise_serializer.rb
|
||||
- app/serializers/api/admin/enterprise_fee_serializer.rb
|
||||
- app/serializers/api/admin/enterprise_serializer.rb
|
||||
- app/serializers/api/admin/exchange_serializer.rb
|
||||
- app/serializers/api/admin/for_order_cycle/enterprise_serializer.rb
|
||||
- app/serializers/api/admin/index_enterprise_serializer.rb
|
||||
- app/serializers/api/admin/index_order_cycle_serializer.rb
|
||||
- app/serializers/api/admin/line_item_serializer.rb
|
||||
- app/serializers/api/admin/order_cycle_serializer.rb
|
||||
- app/serializers/api/admin/subscription_serializer.rb
|
||||
- app/serializers/api/admin/tag_rule_serializer.rb
|
||||
- app/serializers/api/admin/variant_override_serializer.rb
|
||||
- app/services/cart_service.rb
|
||||
- app/services/default_stock_location.rb
|
||||
- app/services/embedded_page_service.rb
|
||||
@@ -148,11 +136,9 @@ Metrics/LineLength:
|
||||
- lib/open_food_network/order_cycle_form_applicator.rb
|
||||
- lib/open_food_network/order_cycle_management_report.rb
|
||||
- lib/open_food_network/order_grouper.rb
|
||||
- lib/open_food_network/orders_and_fulfillments_report.rb
|
||||
- lib/open_food_network/payments_report.rb
|
||||
- lib/open_food_network/permalink_generator.rb
|
||||
- lib/open_food_network/products_cache.rb
|
||||
- lib/open_food_network/products_renderer.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
|
||||
@@ -262,13 +248,10 @@ Metrics/LineLength:
|
||||
- spec/helpers/order_cycles_helper_spec.rb
|
||||
- spec/helpers/spree/admin/base_helper_spec.rb
|
||||
- spec/jobs/confirm_order_job_spec.rb
|
||||
- spec/jobs/products_cache_integrity_checker_job_spec.rb
|
||||
- spec/jobs/refresh_products_cache_job_spec.rb
|
||||
- spec/jobs/subscription_confirm_job_spec.rb
|
||||
- spec/jobs/subscription_placement_job_spec.rb
|
||||
- spec/lib/open_food_network/address_finder_spec.rb
|
||||
- spec/lib/open_food_network/bulk_coop_report_spec.rb
|
||||
- spec/lib/open_food_network/cached_products_renderer_spec.rb
|
||||
- spec/lib/open_food_network/customers_report_spec.rb
|
||||
- spec/lib/open_food_network/enterprise_fee_applicator_spec.rb
|
||||
- spec/lib/open_food_network/enterprise_fee_calculator_spec.rb
|
||||
@@ -285,7 +268,6 @@ Metrics/LineLength:
|
||||
- spec/lib/open_food_network/permissions_spec.rb
|
||||
- spec/lib/open_food_network/products_and_inventory_report_spec.rb
|
||||
- spec/lib/open_food_network/products_cache_spec.rb
|
||||
- spec/lib/open_food_network/products_renderer_spec.rb
|
||||
- spec/lib/open_food_network/proxy_order_syncer_spec.rb
|
||||
- spec/lib/open_food_network/scope_variant_to_hub_spec.rb
|
||||
- spec/lib/open_food_network/subscription_payment_updater_spec.rb
|
||||
@@ -454,7 +436,6 @@ Metrics/AbcSize:
|
||||
- lib/open_food_network/order_cycle_form_applicator.rb
|
||||
- lib/open_food_network/order_cycle_management_report.rb
|
||||
- lib/open_food_network/order_cycle_permissions.rb
|
||||
- lib/open_food_network/orders_and_fulfillments_report.rb
|
||||
- lib/open_food_network/packing_report.rb
|
||||
- lib/open_food_network/payments_report.rb
|
||||
- lib/open_food_network/permissions.rb
|
||||
@@ -532,7 +513,6 @@ Metrics/CyclomaticComplexity:
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/enterprise_issue_validator.rb
|
||||
- lib/open_food_network/orders_and_fulfillments_report.rb
|
||||
- lib/spree/core/controller_helpers/order_decorator.rb
|
||||
- lib/spree/core/controller_helpers/respond_with_decorator.rb
|
||||
- lib/spree/localized_number.rb
|
||||
@@ -557,7 +537,6 @@ Metrics/PerceivedComplexity:
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/enterprise_issue_validator.rb
|
||||
- lib/open_food_network/orders_and_fulfillments_report.rb
|
||||
- lib/spree/core/controller_helpers/order_decorator.rb
|
||||
- lib/spree/core/controller_helpers/respond_with_decorator.rb
|
||||
- lib/spree/localized_number.rb
|
||||
@@ -627,12 +606,10 @@ Metrics/MethodLength:
|
||||
- lib/open_food_network/order_cycle_management_report.rb
|
||||
- lib/open_food_network/order_cycle_permissions.rb
|
||||
- lib/open_food_network/order_grouper.rb
|
||||
- lib/open_food_network/orders_and_fulfillments_report.rb
|
||||
- lib/open_food_network/packing_report.rb
|
||||
- lib/open_food_network/payments_report.rb
|
||||
- lib/open_food_network/permissions.rb
|
||||
- lib/open_food_network/products_and_inventory_report.rb
|
||||
- lib/open_food_network/products_renderer.rb
|
||||
- lib/open_food_network/rack_request_blocker.rb
|
||||
- lib/open_food_network/reports/bulk_coop_allocation_report.rb
|
||||
- lib/open_food_network/reports/bulk_coop_supplier_report.rb
|
||||
@@ -671,7 +648,6 @@ Metrics/ClassLength:
|
||||
- lib/open_food_network/order_cycle_form_applicator.rb
|
||||
- lib/open_food_network/order_cycle_management_report.rb
|
||||
- lib/open_food_network/order_cycle_permissions.rb
|
||||
- lib/open_food_network/orders_and_fulfillments_report.rb
|
||||
- lib/open_food_network/packing_report.rb
|
||||
- lib/open_food_network/payments_report.rb
|
||||
- lib/open_food_network/permissions.rb
|
||||
@@ -688,7 +664,6 @@ Metrics/ModuleLength:
|
||||
- spec/controllers/api/orders_controller_spec.rb
|
||||
- spec/controllers/spree/api/products_controller_spec.rb
|
||||
- spec/lib/open_food_network/address_finder_spec.rb
|
||||
- spec/lib/open_food_network/cached_products_renderer_spec.rb
|
||||
- spec/lib/open_food_network/customers_report_spec.rb
|
||||
- spec/lib/open_food_network/enterprise_fee_calculator_spec.rb
|
||||
- spec/lib/open_food_network/option_value_namer_spec.rb
|
||||
|
||||
@@ -19,6 +19,10 @@ If you want to run the whole test suite, we recommend using a free CI service to
|
||||
|
||||
bundle exec rspec spec
|
||||
|
||||
## Which issue to pick first?
|
||||
|
||||
We have curated all issues interesting for new members of the community within the [Welcome New Developers project board][welcome-dev]. Have a look and pick the one you would prefer working on!
|
||||
|
||||
## Internationalisation (i18n)
|
||||
|
||||
The locale `en` is maintained in the source code, but other locales are managed at [Transifex][ofn-transifex]. Read more about [internationalisation][i18n] in the developer wiki.
|
||||
@@ -62,3 +66,4 @@ From here, your pull request will progress through the [Review, Test, Merge & De
|
||||
[slack-dev]: https://openfoodnetwork.slack.com/messages/C2GQ45KNU
|
||||
[ofn-transifex]: https://www.transifex.com/open-food-foundation/open-food-network/
|
||||
[i18n]: https://github.com/openfoodfoundation/openfoodnetwork/wiki/i18n
|
||||
[welcome-dev]: https://github.com/openfoodfoundation/openfoodnetwork/projects/27
|
||||
|
||||
@@ -6,7 +6,7 @@ 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.5
|
||||
ENV RUBY_VERSION 2.1.9
|
||||
ENV CONFIGURE_OPTS --disable-install-doc
|
||||
|
||||
# Rbenv & Ruby part
|
||||
|
||||
@@ -11,7 +11,7 @@ The following guides are located in the wiki and provide more OS-specific step-b
|
||||
### Dependencies
|
||||
|
||||
* Rails 3.2.x
|
||||
* Ruby 2.1.5
|
||||
* Ruby 2.1.9
|
||||
* PostgreSQL database
|
||||
* PhantomJS (for testing)
|
||||
* See Gemfile for a list of gems required
|
||||
|
||||
7
Gemfile
7
Gemfile
@@ -39,7 +39,7 @@ gem 'activemerchant', '~> 1.78'
|
||||
gem 'devise', '~> 2.2.5'
|
||||
gem 'devise-encryptable', '0.2.0'
|
||||
gem 'jwt', '~> 2.2'
|
||||
gem 'oauth2', '~> 1.4.1' # Used for Stripe Connect
|
||||
gem 'oauth2', '~> 1.4.2' # Used for Stripe Connect
|
||||
|
||||
gem 'daemons'
|
||||
gem 'delayed_job_active_record'
|
||||
@@ -104,7 +104,10 @@ group :assets do
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
gem 'compass-rails'
|
||||
|
||||
gem 'therubyracer', '=0.12.0'
|
||||
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 'uglifier', '>= 1.0.3'
|
||||
|
||||
|
||||
57
Gemfile.lock
57
Gemfile.lock
@@ -223,7 +223,7 @@ GEM
|
||||
activerecord (>= 3.2.0, < 5.0)
|
||||
fog (~> 1.0)
|
||||
rails (>= 3.2.0, < 5.0)
|
||||
ddtrace (0.27.0)
|
||||
ddtrace (0.28.0)
|
||||
msgpack
|
||||
debugger-linecache (1.2.0)
|
||||
deface (1.0.2)
|
||||
@@ -465,7 +465,7 @@ GEM
|
||||
addressable (~> 2.3)
|
||||
letter_opener (1.7.0)
|
||||
launchy (~> 2.2)
|
||||
libv8 (3.16.14.19)
|
||||
libv8 (6.3.292.48.1)
|
||||
listen (3.0.8)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
@@ -476,6 +476,8 @@ GEM
|
||||
mime-types (1.25.1)
|
||||
mini_mime (1.0.1)
|
||||
mini_portile2 (2.1.0)
|
||||
mini_racer (0.1.15)
|
||||
libv8 (~> 6.3)
|
||||
momentjs-rails (2.20.1)
|
||||
railties (>= 3.1)
|
||||
money (5.1.1)
|
||||
@@ -489,8 +491,8 @@ GEM
|
||||
newrelic_rpm (3.18.1.330)
|
||||
nokogiri (1.6.8.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
oauth2 (1.4.1)
|
||||
faraday (>= 0.8, < 0.16.0)
|
||||
oauth2 (1.4.2)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
@@ -579,7 +581,6 @@ GEM
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
redcarpet (3.5.0)
|
||||
ref (2.0.0)
|
||||
request_store (1.4.1)
|
||||
rack (>= 1.4)
|
||||
roadie (3.4.0)
|
||||
@@ -595,29 +596,29 @@ GEM
|
||||
nokogiri
|
||||
roo (>= 2.0.0beta1, < 3)
|
||||
spreadsheet (> 0.9.0)
|
||||
rspec (3.8.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-core (3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-expectations (3.8.2)
|
||||
rspec (3.9.0)
|
||||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
rspec-mocks (~> 3.9.0)
|
||||
rspec-core (3.9.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-expectations (3.9.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-mocks (3.8.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-mocks (3.9.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-rails (3.8.2)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-rails (3.9.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
rspec-mocks (~> 3.9.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-retry (0.6.1)
|
||||
rspec-core (> 3.3)
|
||||
rspec-support (3.8.0)
|
||||
rspec-support (3.9.0)
|
||||
rubocop (0.57.2)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
@@ -629,7 +630,7 @@ GEM
|
||||
ruby-ole (1.2.12.1)
|
||||
ruby-progressbar (1.10.1)
|
||||
ruby-rc4 (0.1.5)
|
||||
rubyzip (1.2.2)
|
||||
rubyzip (1.3.0)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.3.14)
|
||||
sass-rails (3.2.6)
|
||||
@@ -670,9 +671,6 @@ GEM
|
||||
stripe (4.24.0)
|
||||
faraday (~> 0.13)
|
||||
net-http-persistent (~> 3.0)
|
||||
therubyracer (0.12.0)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
thor (0.20.3)
|
||||
tilt (1.4.1)
|
||||
timecop (0.9.1)
|
||||
@@ -685,7 +683,7 @@ GEM
|
||||
railties (> 3.2.8, < 4.0.0)
|
||||
sprockets (>= 2.2.0)
|
||||
tzinfo (0.3.55)
|
||||
uglifier (4.1.20)
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (1.3.2)
|
||||
unicorn (5.5.1)
|
||||
@@ -705,7 +703,7 @@ GEM
|
||||
nokogiri (~> 1.6)
|
||||
rubyzip (~> 1.0)
|
||||
selenium-webdriver (~> 3.0)
|
||||
webmock (3.7.4)
|
||||
webmock (3.7.6)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
@@ -773,11 +771,13 @@ DEPENDENCIES
|
||||
kaminari (~> 0.14.1)
|
||||
knapsack
|
||||
letter_opener (>= 1.4.1)
|
||||
libv8 (= 6.3.292.48.1)
|
||||
listen (= 3.0.8)
|
||||
mini_racer (= 0.1.15)
|
||||
momentjs-rails
|
||||
newrelic_rpm (~> 3.0)
|
||||
nokogiri (>= 1.6.7.1)
|
||||
oauth2 (~> 1.4.1)
|
||||
oauth2 (~> 1.4.2)
|
||||
ofn-qz!
|
||||
oj
|
||||
order_management!
|
||||
@@ -814,7 +814,6 @@ DEPENDENCIES
|
||||
spring (= 1.7.2)
|
||||
spring-commands-rspec
|
||||
stripe
|
||||
therubyracer (= 0.12.0)
|
||||
timecop
|
||||
truncate_html
|
||||
turbo-sprockets-rails3
|
||||
|
||||
@@ -35,7 +35,7 @@ We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. B
|
||||
Copyright (c) 2012 - 2019 Open Food Foundation, released under the AGPL licence.
|
||||
|
||||
[survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit#
|
||||
[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/enQtMzU2Mjk5MDc2MjA5LTM4ZTAzZjIwNzIxMmU5ODFiNWY1MTU2ZWUyNzQwNjdjNTY0N2VhY2UwOGU4ZmVjNzYyZDU2NjY3NzZkZmQwYjk
|
||||
[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/enQtNzY3NDEwNzM2MDM0LWFmNGRhNDUwYzNmNWNkYmFkMzgxNDg1OTg1ODNjNWY4Y2FhNDIwNmE4ZWI0OThiMGNmZjFkODczNGZiYTJmNWI
|
||||
[contributor-guide]: https://ofn-user-guide.gitbook.io/ofn-contributor-guide/who-are-we
|
||||
[ofn-install]: https://github.com/openfoodfoundation/ofn-install
|
||||
[super-admin-guide]: https://ofn-user-guide.gitbook.io/ofn-super-admin-guide
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Darkswarm.controller "OrderCycleCtrl", ($scope, $timeout, OrderCycle) ->
|
||||
Darkswarm.controller "OrderCycleCtrl", ($scope, $rootScope, $timeout, OrderCycle) ->
|
||||
$scope.order_cycle = OrderCycle.order_cycle
|
||||
$scope.OrderCycle = OrderCycle
|
||||
|
||||
@@ -6,11 +6,12 @@ Darkswarm.controller "OrderCycleCtrl", ($scope, $timeout, OrderCycle) ->
|
||||
# This is a hack. We should probably write our own "popover" directive
|
||||
# That takes an expression instead of a trigger, and binds to that
|
||||
$timeout =>
|
||||
$rootScope.$broadcast 'orderCycleSelected'
|
||||
if !$scope.OrderCycle.selected()
|
||||
$("#order_cycle_id").trigger("openTrigger")
|
||||
|
||||
|
||||
Darkswarm.controller "OrderCycleChangeCtrl", ($scope, $timeout, OrderCycle, Products, Variants, Cart, ChangeableOrdersAlert) ->
|
||||
Darkswarm.controller "OrderCycleChangeCtrl", ($scope, $rootScope, $timeout, OrderCycle, Products, Variants, Cart, ChangeableOrdersAlert) ->
|
||||
# Track previous order cycle id for use with revertOrderCycle()
|
||||
$scope.previous_order_cycle_id = OrderCycle.order_cycle.order_cycle_id
|
||||
$scope.$watch 'order_cycle.order_cycle_id', (newValue, oldValue)->
|
||||
@@ -32,3 +33,4 @@ Darkswarm.controller "OrderCycleChangeCtrl", ($scope, $timeout, OrderCycle, Prod
|
||||
Products.update()
|
||||
Cart.reloadFinalisedLineItems()
|
||||
ChangeableOrdersAlert.reload()
|
||||
$rootScope.$broadcast 'orderCycleSelected'
|
||||
|
||||
@@ -1,41 +1,65 @@
|
||||
Darkswarm.controller "ProductsCtrl", ($scope, $filter, $rootScope, Products, OrderCycle, FilterSelectorsService, Cart, Taxons, Properties) ->
|
||||
Darkswarm.controller "ProductsCtrl", ($scope, $filter, $rootScope, Products, OrderCycle, OrderCycleResource, FilterSelectorsService, Cart, Dereferencer, Taxons, Properties, currentHub, $timeout) ->
|
||||
$scope.Products = Products
|
||||
$scope.Cart = Cart
|
||||
$scope.query = ""
|
||||
$scope.taxonSelectors = FilterSelectorsService.createSelectors()
|
||||
$scope.propertySelectors = FilterSelectorsService.createSelectors()
|
||||
$scope.filtersActive = true
|
||||
$scope.limit = 10
|
||||
$scope.page = 1
|
||||
$scope.per_page = 10
|
||||
$scope.order_cycle = OrderCycle.order_cycle
|
||||
# $scope.infiniteDisabled = true
|
||||
$scope.supplied_taxons = null
|
||||
$scope.supplied_properties = null
|
||||
|
||||
# All of this logic basically just replicates the functionality filtering an ng-repeat
|
||||
# except that it allows us to filter a separate list before rendering, meaning that
|
||||
# we can get much better performance when applying filters by resetting the limit on the
|
||||
# number of products being rendered each time a filter is changed.
|
||||
$rootScope.$on "orderCycleSelected", ->
|
||||
$scope.update_filters()
|
||||
$scope.clearAll()
|
||||
|
||||
$scope.$watch "Products.loading", (newValue, oldValue) ->
|
||||
$scope.updateFilteredProducts()
|
||||
$scope.$broadcast("loadFilterSelectors") if !newValue
|
||||
$scope.update_filters = ->
|
||||
order_cycle_id = OrderCycle.order_cycle.order_cycle_id
|
||||
|
||||
$scope.incrementLimit = ->
|
||||
if $scope.limit < Products.products.length
|
||||
$scope.limit += 10
|
||||
$scope.updateVisibleProducts()
|
||||
return unless order_cycle_id
|
||||
|
||||
$scope.$watch 'query', -> $scope.updateFilteredProducts()
|
||||
$scope.$watchCollection 'activeTaxons', -> $scope.updateFilteredProducts()
|
||||
$scope.$watchCollection 'activeProperties', -> $scope.updateFilteredProducts()
|
||||
params = {
|
||||
id: order_cycle_id,
|
||||
distributor: currentHub.id
|
||||
}
|
||||
OrderCycleResource.taxons params, (data)=>
|
||||
$scope.supplied_taxons = {}
|
||||
data.map( (taxon) ->
|
||||
$scope.supplied_taxons[taxon.id] = Taxons.taxons_by_id[taxon.id]
|
||||
)
|
||||
OrderCycleResource.properties params, (data)=>
|
||||
$scope.supplied_properties = {}
|
||||
data.map( (property) ->
|
||||
$scope.supplied_properties[property.id] = Properties.properties_by_id[property.id]
|
||||
)
|
||||
|
||||
$scope.updateFilteredProducts = ->
|
||||
$scope.limit = 10
|
||||
f1 = $filter('products')(Products.products, $scope.query)
|
||||
f2 = $filter('taxons')(f1, $scope.activeTaxons)
|
||||
$scope.filteredProducts = $filter('properties')(f2, $scope.activeProperties)
|
||||
$scope.updateVisibleProducts()
|
||||
$scope.loadMore = ->
|
||||
if ($scope.page * $scope.per_page) <= Products.products.length
|
||||
$scope.loadMoreProducts()
|
||||
|
||||
$scope.updateVisibleProducts = ->
|
||||
$scope.visibleProducts = $filter('limitTo')($scope.filteredProducts, $scope.limit)
|
||||
$scope.$watch 'query', (newValue, oldValue) -> $scope.loadProducts() if newValue != oldValue
|
||||
$scope.$watchCollection 'activeTaxons', (newValue, oldValue) -> $scope.loadProducts() if newValue != oldValue
|
||||
$scope.$watchCollection 'activeProperties', (newValue, oldValue) -> $scope.loadProducts() if newValue != oldValue
|
||||
|
||||
$scope.loadProducts = ->
|
||||
$scope.page = 1
|
||||
Products.update($scope.queryParams())
|
||||
|
||||
$scope.loadMoreProducts = ->
|
||||
Products.update($scope.queryParams($scope.page + 1), true)
|
||||
$scope.page += 1
|
||||
|
||||
$scope.queryParams = (page = null) ->
|
||||
{
|
||||
id: $scope.order_cycle.order_cycle_id,
|
||||
page: page || $scope.page,
|
||||
per_page: $scope.per_page,
|
||||
'q[name_or_meta_keywords_or_supplier_name_cont]': $scope.query,
|
||||
'q[properties_id_or_supplier_properties_id_in_any][]': $scope.activeProperties,
|
||||
'q[primary_taxon_id_in_any][]': $scope.activeTaxons
|
||||
}
|
||||
|
||||
$scope.searchKeypress = (e)->
|
||||
code = e.keyCode || e.which
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
Darkswarm.factory 'OrderCycleResource', ($resource) ->
|
||||
$resource('/api/order_cycles/:id', {}, {
|
||||
'products':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/order_cycles/:id/products'
|
||||
params:
|
||||
id: '@id'
|
||||
'taxons':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/order_cycles/:id/taxons'
|
||||
params:
|
||||
id: '@id'
|
||||
'properties':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/order_cycles/:id/properties'
|
||||
params:
|
||||
id: '@id'
|
||||
})
|
||||
@@ -1,26 +1,34 @@
|
||||
Darkswarm.factory 'Products', ($resource, Shopfront, Dereferencer, Taxons, Properties, Cart, Variants) ->
|
||||
Darkswarm.factory 'Products', (OrderCycleResource, OrderCycle, Shopfront, currentHub, Dereferencer, Taxons, Properties, Cart, Variants) ->
|
||||
new class Products
|
||||
constructor: ->
|
||||
@update()
|
||||
|
||||
# TODO: don't need to scope this into object
|
||||
# Already on object as far as controller scope is concerned
|
||||
products: null
|
||||
products: []
|
||||
fetched_products: []
|
||||
loading: true
|
||||
|
||||
update: =>
|
||||
update: (params = {}, load_more = false) =>
|
||||
@loading = true
|
||||
@products = []
|
||||
$resource("/shop/products").query (products)=>
|
||||
@products = products
|
||||
order_cycle_id = OrderCycle.order_cycle.order_cycle_id
|
||||
|
||||
if order_cycle_id == undefined
|
||||
@loading = false
|
||||
return
|
||||
|
||||
params['id'] = order_cycle_id
|
||||
params['distributor'] = currentHub.id
|
||||
|
||||
OrderCycleResource.products params, (data)=>
|
||||
@products = [] unless load_more
|
||||
@fetched_products = data
|
||||
@extend()
|
||||
@dereference()
|
||||
@registerVariants()
|
||||
@products = @products.concat(@fetched_products)
|
||||
@loading = false
|
||||
|
||||
extend: ->
|
||||
for product in @products
|
||||
for product in @fetched_products
|
||||
if product.variants?.length > 0
|
||||
prices = (v.price for v in product.variants)
|
||||
product.price = Math.min.apply(null, prices)
|
||||
@@ -30,7 +38,7 @@ Darkswarm.factory 'Products', ($resource, Shopfront, Dereferencer, Taxons, Prope
|
||||
product.largeImage = product.images[0]?.large_url if product.images
|
||||
|
||||
dereference: ->
|
||||
for product in @products
|
||||
for product in @fetched_products
|
||||
product.supplier = Shopfront.producers_by_id[product.supplier.id]
|
||||
Dereferencer.dereference product.taxons, Taxons.taxons_by_id
|
||||
|
||||
@@ -40,7 +48,7 @@ Darkswarm.factory 'Products', ($resource, Shopfront, Dereferencer, Taxons, Prope
|
||||
# May return different objects! If the variant has already been registered
|
||||
# by another service, we fetch those
|
||||
registerVariants: ->
|
||||
for product in @products
|
||||
for product in @fetched_products
|
||||
if product.variants
|
||||
product.variant_names = ""
|
||||
product.variants = for variant in product.variants
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
.small-4.medium-2.large-2.columns.variant-price
|
||||
.table-cell.price
|
||||
%i.ofn-i_009-close
|
||||
{{ ::variant.price_with_fees | localizeCurrency }}
|
||||
{{ variant.price_with_fees | localizeCurrency }}
|
||||
|
||||
-# Now in a template in app/assets/javascripts/templates !
|
||||
%price-breakdown{"price-breakdown" => "_", variant: "variant",
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
require 'open_food_network/products_cache_integrity_checker'
|
||||
|
||||
module Admin
|
||||
class CacheSettingsController < Spree::Admin::BaseController
|
||||
def edit
|
||||
@results = Exchange.cachable.map do |exchange|
|
||||
checker = OpenFoodNetwork::ProductsCacheIntegrityChecker
|
||||
.new(exchange.receiver, exchange.order_cycle)
|
||||
|
||||
{
|
||||
distributor: exchange.receiver,
|
||||
order_cycle: exchange.order_cycle,
|
||||
status: checker.ok?,
|
||||
diff: checker.diff
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
Spree::Config.set(params[:preferences])
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to main_app.edit_admin_cache_settings_path }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
88
app/controllers/api/order_cycles_controller.rb
Normal file
88
app/controllers/api/order_cycles_controller.rb
Normal file
@@ -0,0 +1,88 @@
|
||||
module Api
|
||||
class OrderCyclesController < BaseController
|
||||
include EnterprisesHelper
|
||||
respond_to :json
|
||||
|
||||
skip_authorization_check
|
||||
|
||||
def products
|
||||
products = ProductsRenderer.new(
|
||||
distributor,
|
||||
order_cycle,
|
||||
customer,
|
||||
search_params
|
||||
).products_json
|
||||
|
||||
render json: products
|
||||
rescue ProductsRenderer::NoProducts
|
||||
render status: :not_found, json: ''
|
||||
end
|
||||
|
||||
def taxons
|
||||
taxons = Spree::Taxon.
|
||||
joins(:products).
|
||||
where(spree_products: { id: distributed_products }).
|
||||
select('DISTINCT spree_taxons.*')
|
||||
|
||||
render json: ActiveModel::ArraySerializer.new(taxons, each_serializer: Api::TaxonSerializer)
|
||||
end
|
||||
|
||||
def properties
|
||||
render json: ActiveModel::ArraySerializer.new(
|
||||
product_properties | producer_properties, each_serializer: Api::PropertySerializer
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def product_properties
|
||||
Spree::Property.
|
||||
joins(:products).
|
||||
where(spree_products: { id: distributed_products }).
|
||||
select('DISTINCT spree_properties.*')
|
||||
end
|
||||
|
||||
def producer_properties
|
||||
producers = Enterprise.
|
||||
joins(:supplied_products).
|
||||
where(spree_products: { id: distributed_products })
|
||||
|
||||
Spree::Property.
|
||||
joins(:producer_properties).
|
||||
where(producer_properties: { producer_id: producers }).
|
||||
select('DISTINCT spree_properties.*')
|
||||
end
|
||||
|
||||
def search_params
|
||||
permitted_search_params = params.slice :q, :page, :per_page
|
||||
|
||||
if permitted_search_params.key? :q
|
||||
permitted_search_params[:q].slice!(*permitted_ransack_params)
|
||||
end
|
||||
|
||||
permitted_search_params
|
||||
end
|
||||
|
||||
def permitted_ransack_params
|
||||
[:name_or_meta_keywords_or_supplier_name_cont,
|
||||
:properties_id_or_supplier_properties_id_in_any,
|
||||
:primary_taxon_id_in_any]
|
||||
end
|
||||
|
||||
def distributor
|
||||
Enterprise.find_by_id(params[:distributor])
|
||||
end
|
||||
|
||||
def order_cycle
|
||||
OrderCycle.find_by_id(params[:id])
|
||||
end
|
||||
|
||||
def customer
|
||||
@current_api_user.andand.customer_of(distributor) || nil
|
||||
end
|
||||
|
||||
def distributed_products
|
||||
OrderCycleDistributedProducts.new(distributor, order_cycle, customer).products_relation
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,10 @@
|
||||
module Api
|
||||
class OrdersController < BaseController
|
||||
def show
|
||||
authorize! :read, order
|
||||
render json: order, serializer: Api::OrderDetailedSerializer, current_order: order
|
||||
end
|
||||
|
||||
def index
|
||||
authorize! :admin, Spree::Order
|
||||
|
||||
@@ -19,5 +24,12 @@ module Api
|
||||
each_serializer: Api::Admin::OrderSerializer
|
||||
)
|
||||
end
|
||||
|
||||
def order
|
||||
@order ||= Spree::Order.
|
||||
where(number: params[:id]).
|
||||
includes(line_items: { variant: [:product, :stock_items, :default_price] }).
|
||||
first!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'open_food_network/cached_products_renderer'
|
||||
|
||||
class ShopController < BaseController
|
||||
layout "darkswarm"
|
||||
before_filter :require_distributor_chosen, :set_order_cycles, except: :changeable_orders_alert
|
||||
@@ -9,19 +7,6 @@ class ShopController < BaseController
|
||||
redirect_to main_app.enterprise_shop_path(current_distributor)
|
||||
end
|
||||
|
||||
def products
|
||||
renderer = OpenFoodNetwork::CachedProductsRenderer.new(current_distributor,
|
||||
current_order_cycle)
|
||||
|
||||
# If we add any more filtering logic, we should probably
|
||||
# move it all to a lib class like 'CachedProductsFilterer'
|
||||
products_json = filter(renderer.products_json)
|
||||
|
||||
render json: products_json
|
||||
rescue OpenFoodNetwork::CachedProductsRenderer::NoProducts
|
||||
render status: :not_found, json: ''
|
||||
end
|
||||
|
||||
def order_cycle
|
||||
if request.post?
|
||||
if oc = OrderCycle.with_distributor(@distributor).active.find_by_id(params[:order_cycle_id])
|
||||
@@ -39,27 +24,4 @@ class ShopController < BaseController
|
||||
def changeable_orders_alert
|
||||
render layout: false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filtered_json(products_json)
|
||||
if applicator.rules.any?
|
||||
filter(products_json)
|
||||
else
|
||||
products_json
|
||||
end
|
||||
end
|
||||
|
||||
def filter(products_json)
|
||||
products_hash = JSON.parse(products_json)
|
||||
applicator.filter!(products_hash)
|
||||
JSON.unparse(products_hash)
|
||||
end
|
||||
|
||||
def applicator
|
||||
return @applicator unless @applicator.nil?
|
||||
@applicator = OpenFoodNetwork::TagRuleApplicator.new(current_distributor,
|
||||
"FilterProducts",
|
||||
current_customer.andand.tag_list)
|
||||
end
|
||||
end
|
||||
|
||||
9
app/controllers/spree/admin/countries_controller.rb
Normal file
9
app/controllers/spree/admin/countries_controller.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class CountriesController < ResourceController
|
||||
def collection
|
||||
super.order(:name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
35
app/controllers/spree/admin/general_settings_controller.rb
Normal file
35
app/controllers/spree/admin/general_settings_controller.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class GeneralSettingsController < Spree::Admin::BaseController
|
||||
def edit
|
||||
@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]
|
||||
@preferences_currency = [:display_currency, :hide_cents]
|
||||
end
|
||||
|
||||
def update
|
||||
params.each do |name, value|
|
||||
next unless Spree::Config.has_preference? name
|
||||
Spree::Config[name] = value
|
||||
end
|
||||
flash[:success] = Spree.t(:successfully_updated, resource: Spree.t(:general_settings))
|
||||
|
||||
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
|
||||
@@ -1,14 +0,0 @@
|
||||
module Spree
|
||||
module Admin
|
||||
GeneralSettingsController.class_eval do
|
||||
end
|
||||
|
||||
module GeneralSettingsEditPreferences
|
||||
def edit
|
||||
super
|
||||
@preferences_general << :bugherd_api_key
|
||||
end
|
||||
end
|
||||
GeneralSettingsController.prepend(GeneralSettingsEditPreferences)
|
||||
end
|
||||
end
|
||||
79
app/controllers/spree/admin/image_settings_controller.rb
Normal file
79
app/controllers/spree/admin/image_settings_controller.rb
Normal file
@@ -0,0 +1,79 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class ImageSettingsController < Spree::Admin::BaseController
|
||||
def edit
|
||||
@styles = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles])
|
||||
@headers = ActiveSupport::JSON.decode(Spree::Config[:s3_headers])
|
||||
end
|
||||
|
||||
def update
|
||||
update_styles(params)
|
||||
update_headers(params) if Spree::Config[:use_s3]
|
||||
|
||||
Spree::Config.set(params[:preferences])
|
||||
update_paperclip_settings
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
flash[:success] = Spree.t(:image_settings_updated)
|
||||
redirect_to spree.edit_admin_image_settings_path
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_styles(params)
|
||||
if params[:new_attachment_styles].present?
|
||||
params[:new_attachment_styles].each do |_index, style|
|
||||
params[:attachment_styles][style[:name]] = style[:value] unless style[:value].empty?
|
||||
end
|
||||
end
|
||||
|
||||
styles = params[:attachment_styles]
|
||||
|
||||
Spree::Config[:attachment_styles] = ActiveSupport::JSON.encode(styles) unless styles.nil?
|
||||
end
|
||||
|
||||
def update_headers(params)
|
||||
if params[:new_s3_headers].present?
|
||||
params[:new_s3_headers].each do |_index, header|
|
||||
params[:s3_headers][header[:name]] = header[:value] unless header[:value].empty?
|
||||
end
|
||||
end
|
||||
|
||||
headers = params[:s3_headers]
|
||||
|
||||
Spree::Config[:s3_headers] = ActiveSupport::JSON.encode(headers) unless headers.nil?
|
||||
end
|
||||
|
||||
def update_paperclip_settings
|
||||
if Spree::Config[:use_s3]
|
||||
s3_creds = { access_key_id: Spree::Config[:s3_access_key],
|
||||
secret_access_key: Spree::Config[:s3_secret],
|
||||
bucket: Spree::Config[:s3_bucket] }
|
||||
Spree::Image.attachment_definitions[:attachment][:storage] = :s3
|
||||
Spree::Image.attachment_definitions[:attachment][:s3_credentials] = s3_creds
|
||||
Spree::Image.attachment_definitions[:attachment][:s3_headers] =
|
||||
ActiveSupport::JSON.decode(Spree::Config[:s3_headers])
|
||||
Spree::Image.attachment_definitions[:attachment][:bucket] = Spree::Config[:s3_bucket]
|
||||
else
|
||||
Spree::Image.attachment_definitions[:attachment].delete :storage
|
||||
end
|
||||
|
||||
Spree::Image.attachment_definitions[:attachment][:styles] =
|
||||
ActiveSupport::JSON.decode(Spree::Config[:attachment_styles]).symbolize_keys!
|
||||
Spree::Image.attachment_definitions[:attachment][:path] = Spree::Config[:attachment_path]
|
||||
Spree::Image.attachment_definitions[:attachment][:default_url] =
|
||||
Spree::Config[:attachment_default_url]
|
||||
Spree::Image.attachment_definitions[:attachment][:default_style] =
|
||||
Spree::Config[:attachment_default_style]
|
||||
|
||||
# Spree stores attachent definitions in JSON. This converts the style name and format to
|
||||
# strings. However, when paperclip encounters these, it doesn't recognise the format.
|
||||
# Here we solve that problem by converting format and style name to symbols.
|
||||
Spree::Image.reformat_styles
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,11 +0,0 @@
|
||||
Spree::Admin::ImageSettingsController.class_eval do
|
||||
# Spree stores attachent definitions in JSON. This converts the style name and format to
|
||||
# strings. However, when paperclip encounters these, it doesn't recognise the format.
|
||||
# Here we solve that problem by converting format and style name to symbols.
|
||||
def update_paperclip_settings_with_format_styles
|
||||
update_paperclip_settings_without_format_styles
|
||||
Spree::Image.reformat_styles
|
||||
end
|
||||
|
||||
alias_method_chain :update_paperclip_settings, :format_styles
|
||||
end
|
||||
39
app/controllers/spree/admin/mail_methods_controller.rb
Normal file
39
app/controllers/spree/admin/mail_methods_controller.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class MailMethodsController < Spree::Admin::BaseController
|
||||
after_filter :initialize_mail_settings
|
||||
|
||||
def update
|
||||
if params[:smtp_password].blank?
|
||||
params.delete(:smtp_password)
|
||||
end
|
||||
|
||||
params.each do |name, value|
|
||||
next unless Spree::Config.has_preference? name
|
||||
Spree::Config[name] = value
|
||||
end
|
||||
|
||||
flash[:success] = Spree.t(:successfully_updated, resource: Spree.t(:mail_methods))
|
||||
render :edit
|
||||
end
|
||||
|
||||
def testmail
|
||||
if TestMailer.test_email(try_spree_current_user).deliver
|
||||
flash[:success] = Spree.t('admin.mail_methods.testmail.delivery_success')
|
||||
else
|
||||
flash[:error] = Spree.t('admin.mail_methods.testmail.delivery_error')
|
||||
end
|
||||
rescue StandardError => e
|
||||
flash[:error] = Spree.t('admin.mail_methods.testmail.error') % { e: e }
|
||||
ensure
|
||||
redirect_to edit_admin_mail_method_url
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_mail_settings
|
||||
Spree::Core::MailSettings.init
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -30,6 +30,11 @@ Spree::Admin::ProductsController.class_eval do
|
||||
@show_latest_import = params[:latest_import] || false
|
||||
end
|
||||
|
||||
def new
|
||||
@object.shipping_category = DefaultShippingCategory.find_or_create
|
||||
super
|
||||
end
|
||||
|
||||
def create
|
||||
delete_stock_params_and_set_after do
|
||||
super
|
||||
@@ -44,8 +49,6 @@ Spree::Admin::ProductsController.class_eval do
|
||||
delete_stock_params_and_set_after do
|
||||
super
|
||||
end
|
||||
|
||||
clear_variants_unit_description if @object.variant_unit == 'items'
|
||||
end
|
||||
|
||||
def bulk_update
|
||||
@@ -190,10 +193,4 @@ Spree::Admin::ProductsController.class_eval do
|
||||
def set_product_master_variant_price_to_zero
|
||||
@product.price = 0 if @product.price.nil?
|
||||
end
|
||||
|
||||
def clear_variants_unit_description
|
||||
@object.variants.each do |variant|
|
||||
variant.update_attribute :unit_description, ''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -126,7 +126,8 @@ Spree::Admin::ReportsController.class_eval do
|
||||
@include_blank = I18n.t(:all)
|
||||
|
||||
# -- Build Report with Order Grouper
|
||||
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user, params, render_content?
|
||||
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new(permissions,
|
||||
params, render_content?)
|
||||
@table = order_grouper_table
|
||||
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class ShippingCategoriesController < ResourceController
|
||||
end
|
||||
end
|
||||
end
|
||||
84
app/controllers/spree/admin/shipping_methods_controller.rb
Normal file
84
app/controllers/spree/admin/shipping_methods_controller.rb
Normal file
@@ -0,0 +1,84 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class ShippingMethodsController < ResourceController
|
||||
before_filter :load_data, except: [:index]
|
||||
before_filter :set_shipping_category, only: [:create, :update]
|
||||
before_filter :set_zones, only: [:create, :update]
|
||||
before_filter :load_hubs, only: [:new, :edit, :create, :update]
|
||||
|
||||
# Sort shipping methods by distributor name
|
||||
def collection
|
||||
collection = super
|
||||
collection = collection.managed_by(spree_current_user).by_name
|
||||
|
||||
if params.key? :enterprise_id
|
||||
distributor = Enterprise.find params[:enterprise_id]
|
||||
collection = collection.for_distributor(distributor)
|
||||
end
|
||||
|
||||
collection
|
||||
end
|
||||
|
||||
def new
|
||||
@object.shipping_categories = [DefaultShippingCategory.find_or_create]
|
||||
super
|
||||
end
|
||||
|
||||
def destroy
|
||||
# Our reports are not adapted to soft deleted shipping_methods so here we prevent
|
||||
# the deletion (even soft) of shipping_methods that are referenced in orders
|
||||
if order = order_referenced_by_shipping_method
|
||||
flash[:error] = I18n.t(:shipping_method_destroy_error, number: order.number)
|
||||
redirect_to(collection_url) && return
|
||||
end
|
||||
|
||||
@object.touch :deleted_at
|
||||
flash[:success] = flash_message_for(@object, :successfully_removed)
|
||||
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def order_referenced_by_shipping_method
|
||||
Order.joins(shipments: :shipping_rates)
|
||||
.where( spree_shipping_rates: { shipping_method_id: @object } )
|
||||
.first
|
||||
end
|
||||
|
||||
def load_hubs
|
||||
# rubocop:disable Style/TernaryParentheses
|
||||
@hubs = Enterprise.managed_by(spree_current_user).is_distributor.sort_by! do |d|
|
||||
[(@shipping_method.has_distributor? d) ? 0 : 1, d.name]
|
||||
end
|
||||
# rubocop:enable Style/TernaryParentheses
|
||||
end
|
||||
|
||||
def set_shipping_category
|
||||
return true if params["shipping_method"][:shipping_categories] == ""
|
||||
@shipping_method.shipping_categories =
|
||||
Spree::ShippingCategory.where(id: params["shipping_method"][:shipping_categories])
|
||||
@shipping_method.save
|
||||
params[:shipping_method].delete(:shipping_categories)
|
||||
end
|
||||
|
||||
def set_zones
|
||||
return true if params["shipping_method"][:zones] == ""
|
||||
@shipping_method.zones = Spree::Zone.where(id: params["shipping_method"][:zones])
|
||||
@shipping_method.save
|
||||
params[:shipping_method].delete(:zones)
|
||||
end
|
||||
|
||||
def location_after_save
|
||||
edit_admin_shipping_method_path(@shipping_method)
|
||||
end
|
||||
|
||||
def load_data
|
||||
@available_zones = Zone.order(:name)
|
||||
@calculators = ShippingMethod.calculators.sort_by(&:name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,42 +0,0 @@
|
||||
module Spree
|
||||
module Admin
|
||||
ShippingMethodsController.class_eval do
|
||||
before_filter :do_not_destroy_referenced_shipping_methods, only: :destroy
|
||||
before_filter :load_hubs, only: [:new, :edit, :create, :update]
|
||||
|
||||
# Sort shipping methods by distributor name
|
||||
def collection
|
||||
collection = super
|
||||
collection = collection.managed_by(spree_current_user).by_name
|
||||
|
||||
if params.key? :enterprise_id
|
||||
distributor = Enterprise.find params[:enterprise_id]
|
||||
collection = collection.for_distributor(distributor)
|
||||
end
|
||||
|
||||
collection
|
||||
end
|
||||
|
||||
# Spree allows soft deletes of shipping_methods but our reports are not adapted to that
|
||||
# Here we prevent the deletion (even soft) of shipping_methods that are referenced in orders
|
||||
def do_not_destroy_referenced_shipping_methods
|
||||
order = Order.joins(shipments: :shipping_rates)
|
||||
.where( spree_shipping_rates: { shipping_method_id: @object } )
|
||||
.first
|
||||
return unless order
|
||||
flash[:error] = I18n.t(:shipping_method_destroy_error, number: order.number)
|
||||
redirect_to(collection_url) && return
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_hubs
|
||||
# rubocop:disable Style/TernaryParentheses
|
||||
@hubs = Enterprise.managed_by(spree_current_user).is_distributor.sort_by! do |d|
|
||||
[(@shipping_method.has_distributor? d) ? 0 : 1, d.name]
|
||||
end
|
||||
# rubocop:enable Style/TernaryParentheses
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
29
app/controllers/spree/admin/states_controller.rb
Normal file
29
app/controllers/spree/admin/states_controller.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class StatesController < ResourceController
|
||||
belongs_to 'spree/country'
|
||||
before_filter :load_data
|
||||
|
||||
def index
|
||||
respond_with(@collection) do |format|
|
||||
format.html
|
||||
format.js { render partial: 'state_list' }
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def location_after_save
|
||||
admin_country_states_url(@country)
|
||||
end
|
||||
|
||||
def collection
|
||||
super.order(:name)
|
||||
end
|
||||
|
||||
def load_data
|
||||
@countries = Country.order(:name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
19
app/controllers/spree/admin/tax_categories_controller.rb
Normal file
19
app/controllers/spree/admin/tax_categories_controller.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class TaxCategoriesController < ResourceController
|
||||
def destroy
|
||||
if @object.destroy
|
||||
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
|
||||
respond_with(@object) do |format|
|
||||
format.html { redirect_to collection_url }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
26
app/controllers/spree/admin/tax_rates_controller.rb
Normal file
26
app/controllers/spree/admin/tax_rates_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class TaxRatesController < ResourceController
|
||||
before_filter :load_data
|
||||
|
||||
update.after :update_after
|
||||
create.after :create_after
|
||||
|
||||
private
|
||||
|
||||
def load_data
|
||||
@available_zones = Zone.order(:name)
|
||||
@available_categories = TaxCategory.order(:name)
|
||||
@calculators = TaxRate.calculators.sort_by(&:name)
|
||||
end
|
||||
|
||||
def update_after
|
||||
Rails.cache.delete('vat_rates')
|
||||
end
|
||||
|
||||
def create_after
|
||||
Rails.cache.delete('vat_rates')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
15
app/controllers/spree/admin/tax_settings_controller.rb
Normal file
15
app/controllers/spree/admin/tax_settings_controller.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class TaxSettingsController < Spree::Admin::BaseController
|
||||
def update
|
||||
Spree::Config.set(params[:preferences])
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
redirect_to edit_admin_tax_settings_path
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
21
app/controllers/spree/admin/taxonomies_controller.rb
Normal file
21
app/controllers/spree/admin/taxonomies_controller.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class TaxonomiesController < ResourceController
|
||||
respond_to :json, :only => [:get_children]
|
||||
|
||||
def get_children
|
||||
@taxons = Taxon.find(params[:parent_id]).children
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def location_after_save
|
||||
if @taxonomy.created_at == @taxonomy.updated_at
|
||||
edit_admin_taxonomy_url(@taxonomy)
|
||||
else
|
||||
admin_taxonomies_url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
118
app/controllers/spree/admin/taxons_controller.rb
Normal file
118
app/controllers/spree/admin/taxons_controller.rb
Normal file
@@ -0,0 +1,118 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class TaxonsController < Spree::Admin::BaseController
|
||||
respond_to :html, :json, :js
|
||||
|
||||
def search
|
||||
if params[:ids]
|
||||
@taxons = Spree::Taxon.where(id: params[:ids].split(','))
|
||||
else
|
||||
@taxons = Spree::Taxon.limit(20).search(name_cont: params[:q]).result
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@taxonomy = Taxonomy.find(params[:taxonomy_id])
|
||||
@taxon = @taxonomy.taxons.build(params[:taxon])
|
||||
if @taxon.save
|
||||
respond_with(@taxon) do |format|
|
||||
format.json { render json: @taxon.to_json }
|
||||
end
|
||||
else
|
||||
flash[:error] = Spree.t('errors.messages.could_not_create_taxon')
|
||||
respond_with(@taxon) do |format|
|
||||
format.html do
|
||||
if redirect_to @taxonomy
|
||||
edit_admin_taxonomy_url(@taxonomy)
|
||||
else
|
||||
admin_taxonomies_url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@taxonomy = Taxonomy.find(params[:taxonomy_id])
|
||||
@taxon = @taxonomy.taxons.find(params[:id])
|
||||
@permalink_part = @taxon.permalink.split("/").last
|
||||
end
|
||||
|
||||
def update
|
||||
@taxonomy = Taxonomy.find(params[:taxonomy_id])
|
||||
@taxon = @taxonomy.taxons.find(params[:id])
|
||||
parent_id = params[:taxon][:parent_id]
|
||||
new_position = params[:taxon][:position]
|
||||
|
||||
if parent_id || new_position # taxon is being moved
|
||||
new_parent = parent_id.nil? ? @taxon.parent : Taxon.find(parent_id.to_i)
|
||||
new_position = new_position.nil? ? -1 : new_position.to_i
|
||||
|
||||
# Bellow is a very complicated way of finding where in nested set we
|
||||
# should actually move the taxon to achieve sane results,
|
||||
# JS is giving us the desired position, which was awesome for previous setup,
|
||||
# but now it's quite complicated to find where we should put it as we have
|
||||
# to differenciate between moving to the same branch, up down and into
|
||||
# first position.
|
||||
new_siblings = new_parent.children
|
||||
if new_position <= 0 && new_siblings.empty?
|
||||
@taxon.move_to_child_of(new_parent)
|
||||
elsif new_parent.id != @taxon.parent_id
|
||||
if new_position.zero?
|
||||
@taxon.move_to_left_of(new_siblings.first)
|
||||
else
|
||||
@taxon.move_to_right_of(new_siblings[new_position - 1])
|
||||
end
|
||||
elsif new_position < new_siblings.index(@taxon)
|
||||
@taxon.move_to_left_of(new_siblings[new_position]) # we move up
|
||||
else
|
||||
@taxon.move_to_right_of(new_siblings[new_position - 1]) # we move down
|
||||
end
|
||||
# Reset legacy position, if any extensions still rely on it
|
||||
new_parent.children.reload.each{ |t| t.update_column(:position, t.position) }
|
||||
|
||||
if parent_id
|
||||
@taxon.reload
|
||||
@taxon.set_permalink
|
||||
@taxon.save!
|
||||
@update_children = true
|
||||
end
|
||||
end
|
||||
|
||||
if params.key? "permalink_part"
|
||||
parent_permalink = @taxon.permalink.split("/")[0...-1].join("/")
|
||||
parent_permalink += "/" if parent_permalink.present?
|
||||
params[:taxon][:permalink] = parent_permalink + params[:permalink_part]
|
||||
end
|
||||
# check if we need to rename child taxons if parent name or permalink changes
|
||||
if params[:taxon][:name] != @taxon.name || params[:taxon][:permalink] != @taxon.permalink
|
||||
@update_children = true
|
||||
end
|
||||
|
||||
if @taxon.update_attributes(params[:taxon])
|
||||
flash[:success] = flash_message_for(@taxon, :successfully_updated)
|
||||
end
|
||||
|
||||
# rename child taxons
|
||||
if @update_children
|
||||
@taxon.descendants.each do |taxon|
|
||||
taxon.reload
|
||||
taxon.set_permalink
|
||||
taxon.save!
|
||||
end
|
||||
end
|
||||
|
||||
respond_with(@taxon) do |format|
|
||||
format.html { redirect_to edit_admin_taxonomy_url(@taxonomy) }
|
||||
format.json { render json: @taxon.to_json }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@taxon = Taxon.find(params[:id])
|
||||
@taxon.destroy
|
||||
respond_with(@taxon) { |format| format.json { render json: '' } }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -46,7 +46,12 @@ module Spree
|
||||
@user.spree_roles = roles.reject(&:blank?).collect{ |r| Spree::Role.find(r) }
|
||||
end
|
||||
|
||||
flash.now[:success] = Spree.t(:account_updated)
|
||||
message = if new_email_unconfirmed?
|
||||
Spree.t(:email_updated)
|
||||
else
|
||||
Spree.t(:account_updated)
|
||||
end
|
||||
flash.now[:success] = message
|
||||
end
|
||||
render :edit
|
||||
end
|
||||
@@ -126,6 +131,10 @@ module Spree
|
||||
def load_roles
|
||||
@roles = Spree::Role.scoped
|
||||
end
|
||||
|
||||
def new_email_unconfirmed?
|
||||
params[:user][:email] != @user.email
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
26
app/controllers/spree/admin/zones_controller.rb
Normal file
26
app/controllers/spree/admin/zones_controller.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
module Spree
|
||||
module Admin
|
||||
class ZonesController < ResourceController
|
||||
before_filter :load_data, except: [:index]
|
||||
|
||||
def new
|
||||
@zone.zone_members.build
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def collection
|
||||
params[:q] ||= {}
|
||||
params[:q][:s] ||= "ascend_by_name"
|
||||
@search = super.ransack(params[:q])
|
||||
@zones = @search.result.page(params[:page]).per(Spree::Config[:orders_per_page])
|
||||
end
|
||||
|
||||
def load_data
|
||||
@countries = Country.order(:name)
|
||||
@states = State.order(:name)
|
||||
@zones = Zone.order(:name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/helpers/spree/admin/general_settings_helper.rb
Normal file
13
app/helpers/spree/admin/general_settings_helper.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
module Spree
|
||||
module Admin
|
||||
module GeneralSettingsHelper
|
||||
def currency_options
|
||||
currencies = ::Money::Currency.table.map do |_code, details|
|
||||
iso = details[:iso_code]
|
||||
[iso, "#{details[:name]} (#{iso})"]
|
||||
end
|
||||
options_from_collection_for_select(currencies, :first, :last, Spree::Config[:currency])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
9
app/helpers/spree/admin/taxons_helper.rb
Normal file
9
app/helpers/spree/admin/taxons_helper.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module Spree
|
||||
module Admin
|
||||
module TaxonsHelper
|
||||
def taxon_path(taxon)
|
||||
taxon.ancestors.reverse.collect(&:name).join( " >> ")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,30 +0,0 @@
|
||||
require 'open_food_network/products_cache_integrity_checker'
|
||||
|
||||
ProductsCacheIntegrityCheckerJob = Struct.new(:distributor_id, :order_cycle_id) do
|
||||
def perform
|
||||
unless checker.ok?
|
||||
exception = RuntimeError.new(
|
||||
"Products JSON differs from cached version for distributor: #{distributor_id}, " \
|
||||
"order cycle: #{order_cycle_id}"
|
||||
)
|
||||
|
||||
Bugsnag.notify(exception) do |report|
|
||||
report.add_tab(:products_cache, diff: checker.diff.to_s(:text))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def checker
|
||||
OpenFoodNetwork::ProductsCacheIntegrityChecker.new(distributor, order_cycle)
|
||||
end
|
||||
|
||||
def distributor
|
||||
Enterprise.find distributor_id
|
||||
end
|
||||
|
||||
def order_cycle
|
||||
OrderCycle.find order_cycle_id
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
require 'open_food_network/products_renderer'
|
||||
|
||||
RefreshProductsCacheJob = Struct.new(:distributor_id, :order_cycle_id) do
|
||||
def perform
|
||||
Rails.cache.write(key, products_json)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def key
|
||||
"products-json-#{distributor_id}-#{order_cycle_id}"
|
||||
end
|
||||
|
||||
def products_json
|
||||
distributor = Enterprise.find distributor_id
|
||||
order_cycle = OrderCycle.find order_cycle_id
|
||||
OpenFoodNetwork::ProductsRenderer.new(distributor, order_cycle).products_json
|
||||
end
|
||||
end
|
||||
@@ -19,7 +19,7 @@ module OrderShipment
|
||||
#
|
||||
# @return [ShippingMethod]
|
||||
def shipping_method
|
||||
return if shipments.empty?
|
||||
return if shipments.blank?
|
||||
shipments.first.shipping_method
|
||||
end
|
||||
|
||||
|
||||
@@ -25,36 +25,21 @@ module VariantStock
|
||||
#
|
||||
# @return [Float|Integer]
|
||||
def on_hand
|
||||
warn_deprecation(__method__, '#total_on_hand')
|
||||
|
||||
total_on_hand
|
||||
end
|
||||
|
||||
# Sets the stock level of the variant.
|
||||
# This will only work if `track_inventory_levels` config is set
|
||||
# and if there is a stock item for the variant.
|
||||
# This will only work if there is a stock item for the variant.
|
||||
#
|
||||
# @raise [StandardError] when the track_inventory_levels config key is not set
|
||||
# and when the variant has no stock item
|
||||
# @raise [StandardError] when the variant has no stock item
|
||||
def on_hand=(new_level)
|
||||
warn_deprecation(__method__, '#total_on_hand')
|
||||
|
||||
error = 'Cannot set on_hand value when Spree::Config[:track_inventory_levels] is false'
|
||||
raise error unless Spree::Config.track_inventory_levels
|
||||
|
||||
raise_error_if_no_stock_item_available
|
||||
|
||||
overwrite_stock_levels(new_level)
|
||||
end
|
||||
|
||||
# Checks whether this variant is produced on demand.
|
||||
#
|
||||
# In Spree 2.0 this attribute is removed in favour of
|
||||
# track_inventory_levels only. It was initially introduced in
|
||||
# https://github.com/openfoodfoundation/spree/commit/20b5ad9835dca7f41a40ad16c7b45f987eea6dcc
|
||||
def on_demand
|
||||
warn_deprecation(__method__, 'StockItem#backorderable?')
|
||||
|
||||
# A variant that has not been saved yet, doesn't have a stock item
|
||||
# This provides a default value for variant.on_demand using Spree::StockLocation.backorderable_default
|
||||
return Spree::StockLocation.first.backorderable_default if stock_items.empty?
|
||||
@@ -69,8 +54,6 @@ module VariantStock
|
||||
#
|
||||
# @raise [StandardError] when the variant has no stock item yet
|
||||
def on_demand=(new_value)
|
||||
warn_deprecation(__method__, 'StockItem#backorderable=')
|
||||
|
||||
raise_error_if_no_stock_item_available
|
||||
|
||||
# There should be only one at the default stock location.
|
||||
@@ -89,8 +72,6 @@ module VariantStock
|
||||
# Here we depend only on variant.total_on_hand and variant.on_demand.
|
||||
# This way, variant_overrides only need to override variant.total_on_hand and variant.on_demand.
|
||||
def can_supply?(quantity)
|
||||
return true unless Spree::Config[:track_inventory_levels]
|
||||
|
||||
on_demand || total_on_hand >= quantity
|
||||
end
|
||||
|
||||
@@ -158,11 +139,4 @@ module VariantStock
|
||||
def stock_item
|
||||
stock_items.first
|
||||
end
|
||||
|
||||
def warn_deprecation(method_name, new_method_name)
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"`##{method_name}` is deprecated and will be removed. " \
|
||||
"Please use `#{new_method_name}` instead."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
class CoordinatorFee < ActiveRecord::Base
|
||||
belongs_to :order_cycle
|
||||
belongs_to :enterprise_fee
|
||||
|
||||
after_save :refresh_products_cache
|
||||
after_destroy :refresh_products_cache
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
order_cycle.refresh_products_cache
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,10 +10,6 @@ class EnterpriseFee < ActiveRecord::Base
|
||||
has_many :exchange_fees, dependent: :destroy
|
||||
has_many :exchanges, through: :exchange_fees
|
||||
|
||||
after_save :refresh_products_cache
|
||||
# After destroy, the products cache is refreshed via the after_destroy hook for
|
||||
# coordinator_fees and exchange_fees
|
||||
|
||||
attr_accessible :enterprise_id, :fee_type, :name, :tax_category_id, :calculator_type, :inherits_tax_category
|
||||
|
||||
FEE_TYPES = %w(packing transport admin sales fundraising).freeze
|
||||
@@ -59,8 +55,4 @@ class EnterpriseFee < ActiveRecord::Base
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def refresh_products_cache
|
||||
OpenFoodNetwork::ProductsCache.enterprise_fee_changed self
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,9 +23,6 @@ class Exchange < ActiveRecord::Base
|
||||
validates :order_cycle, :sender, :receiver, presence: true
|
||||
validates :sender_id, uniqueness: { scope: [:order_cycle_id, :receiver_id, :incoming] }
|
||||
|
||||
after_save :refresh_products_cache
|
||||
after_destroy :refresh_products_cache_from_destroy
|
||||
|
||||
accepts_nested_attributes_for :variants
|
||||
|
||||
scope :in_order_cycle, lambda { |order_cycle| where(order_cycle_id: order_cycle) }
|
||||
@@ -98,12 +95,4 @@ class Exchange < ActiveRecord::Base
|
||||
def participant
|
||||
incoming? ? sender : receiver
|
||||
end
|
||||
|
||||
def refresh_products_cache
|
||||
OpenFoodNetwork::ProductsCache.exchange_changed self
|
||||
end
|
||||
|
||||
def refresh_products_cache_from_destroy
|
||||
OpenFoodNetwork::ProductsCache.exchange_destroyed self
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
class ExchangeFee < ActiveRecord::Base
|
||||
belongs_to :exchange
|
||||
belongs_to :enterprise_fee
|
||||
|
||||
after_save :refresh_products_cache
|
||||
after_destroy :refresh_products_cache
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
exchange.refresh_products_cache
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'open_food_network/products_cache'
|
||||
|
||||
class InventoryItem < ActiveRecord::Base
|
||||
attr_accessible :enterprise, :enterprise_id, :variant, :variant_id, :visible
|
||||
|
||||
@@ -13,12 +11,4 @@ class InventoryItem < ActiveRecord::Base
|
||||
|
||||
scope :visible, -> { where(visible: true) }
|
||||
scope :hidden, -> { where(visible: false) }
|
||||
|
||||
after_save :refresh_products_cache
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
OpenFoodNetwork::ProductsCache.inventory_item_changed self
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,8 +23,6 @@ class OrderCycle < ActiveRecord::Base
|
||||
validates :name, :coordinator_id, presence: true
|
||||
validate :orders_close_at_after_orders_open_at?
|
||||
|
||||
after_save :refresh_products_cache
|
||||
|
||||
preference :product_selection_from_coordinator_inventory_only, :boolean, default: false
|
||||
|
||||
scope :active, lambda {
|
||||
@@ -247,10 +245,6 @@ class OrderCycle < ActiveRecord::Base
|
||||
coordinator.users.include? user
|
||||
end
|
||||
|
||||
def refresh_products_cache
|
||||
OpenFoodNetwork::ProductsCache.order_cycle_changed self
|
||||
end
|
||||
|
||||
def items_bought_by_user(user, distributor)
|
||||
# The Spree::Order.complete scope only checks for completed_at date
|
||||
# it does not ensure state is "complete"
|
||||
|
||||
@@ -4,9 +4,6 @@ class ProducerProperty < ActiveRecord::Base
|
||||
|
||||
default_scope order("#{table_name}.position")
|
||||
|
||||
after_save :refresh_products_cache
|
||||
after_destroy :refresh_products_cache_from_destroy
|
||||
|
||||
scope :ever_sold_by, ->(shop) {
|
||||
joins(producer: { supplied_products: { variants: { exchanges: :order_cycle } } }).
|
||||
merge(Exchange.outgoing).
|
||||
@@ -29,14 +26,4 @@ class ProducerProperty < ActiveRecord::Base
|
||||
Spree::Property.create(name: name, presentation: name)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
OpenFoodNetwork::ProductsCache.producer_property_changed self
|
||||
end
|
||||
|
||||
def refresh_products_cache_from_destroy
|
||||
OpenFoodNetwork::ProductsCache.producer_property_destroyed self
|
||||
end
|
||||
end
|
||||
|
||||
@@ -207,12 +207,15 @@ class AbilityDecorator
|
||||
end
|
||||
|
||||
def add_order_management_abilities(user)
|
||||
# Enterprise User can only access orders that they are a distributor for
|
||||
can [:index, :create], Spree::Order
|
||||
can [:read, :update, :fire, :resend, :invoice, :print, :print_ticket], Spree::Order do |order|
|
||||
# We allow editing orders with a nil distributor as this state occurs
|
||||
# during the order creation process from the admin backend
|
||||
order.distributor.nil? || user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user)
|
||||
order.distributor.nil? ||
|
||||
# Enterprise User can access orders that they are a distributor for
|
||||
user.enterprises.include?(order.distributor) ||
|
||||
# Enterprise User can access orders that are placed inside a OC they coordinate
|
||||
order.order_cycle.andand.coordinated_by?(user)
|
||||
end
|
||||
can [:admin, :bulk_management, :managed], Spree::Order do
|
||||
user.admin? || user.enterprises.any?(&:is_distributor)
|
||||
|
||||
@@ -2,16 +2,9 @@ Spree::Classification.class_eval do
|
||||
belongs_to :product, class_name: "Spree::Product", touch: true
|
||||
|
||||
before_destroy :dont_destroy_if_primary_taxon
|
||||
after_destroy :refresh_products_cache
|
||||
after_save :refresh_products_cache
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
product = Spree::Product.with_deleted.find(product_id) if product.blank?
|
||||
product.refresh_products_cache
|
||||
end
|
||||
|
||||
def dont_destroy_if_primary_taxon
|
||||
if product.primary_taxon == taxon
|
||||
errors.add :base, I18n.t(:spree_classification_primary_taxon_error, taxon: taxon.name, product: product.name)
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
Spree::Image.class_eval do
|
||||
after_save :refresh_products_cache
|
||||
after_destroy :refresh_products_cache
|
||||
|
||||
# Spree stores attachent definitions in JSON. This converts the style name and format to
|
||||
# strings. However, when paperclip encounters these, it doesn't recognise the format.
|
||||
# Here we solve that problem by converting format and style name to symbols.
|
||||
@@ -23,10 +20,4 @@ Spree::Image.class_eval do
|
||||
end
|
||||
|
||||
reformat_styles
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
viewable.try :refresh_products_cache
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
module Spree
|
||||
OptionType.class_eval do
|
||||
has_many :products, through: :product_option_types
|
||||
after_save :refresh_products_cache
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
products(:reload).each(&:refresh_products_cache)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
module Spree
|
||||
OptionValue.class_eval do
|
||||
after_save :refresh_products_cache
|
||||
around_destroy :refresh_products_cache_from_destroy
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
variants(:reload).each(&:refresh_products_cache)
|
||||
end
|
||||
|
||||
def refresh_products_cache_from_destroy
|
||||
vs = variants(:reload).to_a
|
||||
yield
|
||||
vs.each(&:refresh_products_cache)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,30 +0,0 @@
|
||||
require 'open_food_network/products_cache'
|
||||
|
||||
module Spree
|
||||
Preference.class_eval do
|
||||
after_save :refresh_products_cache
|
||||
|
||||
# When the setting preferred_product_selection_from_inventory_only has changed, we want to
|
||||
# refresh all active exchanges for this enterprise.
|
||||
def refresh_products_cache
|
||||
if product_selection_from_inventory_only_changed?
|
||||
OpenFoodNetwork::ProductsCache.distributor_changed(enterprise)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def product_selection_from_inventory_only_changed?
|
||||
!!(key =~ product_selection_from_inventory_only_regex)
|
||||
end
|
||||
|
||||
def enterprise
|
||||
enterprise_id = key.match(product_selection_from_inventory_only_regex)[1]
|
||||
Enterprise.find(enterprise_id)
|
||||
end
|
||||
|
||||
def product_selection_from_inventory_only_regex
|
||||
/^enterprise\/product_selection_from_inventory_only\/(\d+)$/
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,8 +2,6 @@ module Spree
|
||||
Price.class_eval do
|
||||
acts_as_paranoid without_default_scope: true
|
||||
|
||||
after_save :refresh_products_cache
|
||||
|
||||
# Allow prices to access associated soft-deleted variants.
|
||||
def variant
|
||||
Spree::Variant.unscoped { super }
|
||||
@@ -16,9 +14,5 @@ module Spree
|
||||
self.currency = Spree::Config[:currency]
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_products_cache
|
||||
variant.andand.refresh_products_cache
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,7 +38,6 @@ Spree::Product.class_eval do
|
||||
after_save :remove_previous_primary_taxon_from_taxons
|
||||
after_save :ensure_standard_variant
|
||||
after_save :update_units
|
||||
after_save :refresh_products_cache
|
||||
|
||||
# -- Joins
|
||||
scope :with_order_cycles_outer, -> {
|
||||
@@ -192,23 +191,17 @@ Spree::Product.class_eval do
|
||||
|
||||
def destroy_with_delete_from_order_cycles
|
||||
transaction do
|
||||
OpenFoodNetwork::ProductsCache.product_deleted(self) do
|
||||
touch_distributors
|
||||
touch_distributors
|
||||
|
||||
ExchangeVariant.
|
||||
where('exchange_variants.variant_id IN (?)', variants_including_master.with_deleted.
|
||||
select(:id)).destroy_all
|
||||
ExchangeVariant.
|
||||
where('exchange_variants.variant_id IN (?)', variants_including_master.with_deleted.
|
||||
select(:id)).destroy_all
|
||||
|
||||
destroy_without_delete_from_order_cycles
|
||||
end
|
||||
destroy_without_delete_from_order_cycles
|
||||
end
|
||||
end
|
||||
alias_method_chain :destroy, :delete_from_order_cycles
|
||||
|
||||
def refresh_products_cache
|
||||
OpenFoodNetwork::ProductsCache.product_changed self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_available_on_to_now
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
module Spree
|
||||
ProductProperty.class_eval do
|
||||
belongs_to :product, class_name: "Spree::Product", touch: true
|
||||
|
||||
after_save :refresh_products_cache
|
||||
after_destroy :refresh_products_cache
|
||||
|
||||
delegate :refresh_products_cache, to: :product
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,19 +20,8 @@ module Spree
|
||||
merge(OrderCycle.active)
|
||||
}
|
||||
|
||||
after_save :refresh_products_cache
|
||||
|
||||
# When a Property is destroyed, dependent-destroy will destroy all ProductProperties,
|
||||
# which will take care of refreshing the products cache
|
||||
|
||||
def property
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
product_properties(:reload).each(&:refresh_products_cache)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ module Spree
|
||||
# NOTE: This is an override of spree's method, needed to allow orders
|
||||
# without line items (ie. user invoices) to not have inventory units
|
||||
def require_inventory
|
||||
return false unless Spree::Config[:track_inventory_levels] && line_items.count > 0 # This line altered
|
||||
return false unless line_items.count > 0 # This line altered
|
||||
order.completed? && !order.canceled?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
Spree::StockMovement.class_eval do
|
||||
after_save :refresh_products_cache
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
return if stock_item.variant.blank?
|
||||
OpenFoodNetwork::ProductsCache.variant_changed stock_item.variant
|
||||
end
|
||||
end
|
||||
@@ -4,8 +4,6 @@ Spree::Taxon.class_eval do
|
||||
attachment_definitions[:icon][:path] = 'public/images/spree/taxons/:id/:style/:basename.:extension'
|
||||
attachment_definitions[:icon][:url] = '/images/spree/taxons/:id/:style/:basename.:extension'
|
||||
|
||||
after_save :refresh_products_cache
|
||||
|
||||
# Indicate which filters should be used for this taxon
|
||||
def applicable_filters
|
||||
fs = []
|
||||
@@ -49,10 +47,4 @@ Spree::Taxon.class_eval do
|
||||
ts[t.enterprise_id.to_i] << t.id
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache
|
||||
products(:reload).each(&:refresh_products_cache)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
require 'open_food_network/enterprise_fee_calculator'
|
||||
require 'open_food_network/variant_and_line_item_naming'
|
||||
require 'concerns/variant_stock'
|
||||
require 'open_food_network/products_cache'
|
||||
|
||||
Spree::Variant.class_eval do
|
||||
extend Spree::LocalizedNumber
|
||||
@@ -30,7 +29,6 @@ Spree::Variant.class_eval do
|
||||
|
||||
before_validation :update_weight_from_unit_value, if: ->(v) { v.product.present? }
|
||||
after_save :update_units
|
||||
after_save :refresh_products_cache
|
||||
around_destroy :destruction
|
||||
|
||||
scope :with_order_cycles_inner, -> { joins(exchanges: :order_cycle) }
|
||||
@@ -108,14 +106,6 @@ Spree::Variant.class_eval do
|
||||
OpenFoodNetwork::EnterpriseFeeCalculator.new(distributor, order_cycle).fees_by_type_for self
|
||||
end
|
||||
|
||||
def refresh_products_cache
|
||||
if is_master?
|
||||
product.refresh_products_cache
|
||||
else
|
||||
OpenFoodNetwork::ProductsCache.variant_changed self
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_weight_from_unit_value
|
||||
@@ -123,21 +113,7 @@ Spree::Variant.class_eval do
|
||||
end
|
||||
|
||||
def destruction
|
||||
if is_master?
|
||||
exchange_variants(:reload).destroy_all
|
||||
yield
|
||||
product.refresh_products_cache
|
||||
|
||||
else
|
||||
OpenFoodNetwork::ProductsCache.variant_destroyed(self) do
|
||||
# Remove this association here instead of using dependent: :destroy because
|
||||
# dependent-destroy acts before this around_filter is called, so ProductsCache
|
||||
# has no way of knowing which exchanges the variant was a member of.
|
||||
exchange_variants(:reload).destroy_all
|
||||
|
||||
# Destroy the variant
|
||||
yield
|
||||
end
|
||||
end
|
||||
exchange_variants(:reload).destroy_all
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,6 +10,11 @@ class SubscriptionLineItem < ActiveRecord::Base
|
||||
(price_estimate || 0) * (quantity || 0)
|
||||
end
|
||||
|
||||
# Ensure SubscriptionLineItem always has access to soft-deleted Variant attribute
|
||||
def variant
|
||||
Spree::Variant.unscoped { super }
|
||||
end
|
||||
|
||||
# Used to calculators to estimate fees
|
||||
alias_method :amount, :total_estimate
|
||||
|
||||
|
||||
@@ -12,9 +12,6 @@ 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
|
||||
|
||||
after_save :refresh_products_cache_from_save
|
||||
after_destroy :refresh_products_cache_from_destroy
|
||||
|
||||
default_scope where(permission_revoked_at: nil)
|
||||
|
||||
scope :for_hubs, lambda { |hubs|
|
||||
@@ -73,14 +70,4 @@ class VariantOverride < ActiveRecord::Base
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def refresh_products_cache_from_save
|
||||
OpenFoodNetwork::ProductsCache.variant_override_changed self
|
||||
end
|
||||
|
||||
def refresh_products_cache_from_destroy
|
||||
OpenFoodNetwork::ProductsCache.variant_override_destroyed self
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<!-- surround_contents 'body' -->
|
||||
|
||||
<div <%= yield(:app_wrapper_attrs).strip.html_safe %>>
|
||||
<%= render_original %>
|
||||
</div>
|
||||
@@ -4,7 +4,11 @@ class Api::AddressSerializer < ActiveModel::Serializer
|
||||
|
||||
attributes :id, :zipcode, :city, :state_name, :state_id,
|
||||
:phone, :firstname, :lastname, :address1, :address2, :city, :country_id,
|
||||
:zipcode
|
||||
:zipcode, :country_name
|
||||
|
||||
def country_name
|
||||
object.country.andand.name
|
||||
end
|
||||
|
||||
def state_name
|
||||
object.state.andand.abbr
|
||||
|
||||
8
app/serializers/api/adjustment_serializer.rb
Normal file
8
app/serializers/api/adjustment_serializer.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
module Api
|
||||
class AdjustmentSerializer < ActiveModel::Serializer
|
||||
attributes :id, :amount, :label, :eligible,
|
||||
:source_type, :source_id,
|
||||
:adjustable_type, :adjustable_id,
|
||||
:originator_type, :originator_id
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
class Api::Admin::BasicEnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :payment_method_ids, :shipping_method_ids
|
||||
attributes :producer_profile_only, :permalink
|
||||
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category,
|
||||
:payment_method_ids, :shipping_method_ids, :producer_profile_only, :permalink
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Api::Admin::CustomerSerializer < ActiveModel::Serializer
|
||||
attributes :id, :email, :enterprise_id, :user_id, :code, :tags, :tag_list, :name
|
||||
attributes :allow_charges, :default_card_present?
|
||||
attributes :id, :email, :enterprise_id, :user_id, :code, :tags, :tag_list, :name,
|
||||
:allow_charges, :default_card_present?
|
||||
|
||||
has_one :ship_address, serializer: Api::AddressSerializer
|
||||
has_one :bill_address, serializer: Api::AddressSerializer
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer
|
||||
attributes :id, :enterprise_id, :fee_type, :name, :tax_category_id, :inherits_tax_category, :calculator_type
|
||||
attributes :enterprise_name, :calculator_description, :calculator_settings
|
||||
attributes :id, :enterprise_id, :fee_type, :name, :tax_category_id, :inherits_tax_category,
|
||||
:calculator_type, :enterprise_name, :calculator_description, :calculator_settings
|
||||
|
||||
def enterprise_name
|
||||
object.enterprise.andand.name
|
||||
@@ -16,7 +16,9 @@ class Api::Admin::EnterpriseFeeSerializer < ActiveModel::Serializer
|
||||
result = nil
|
||||
|
||||
options[:controller].__send__(:with_format, :html) do
|
||||
result = options[:controller].render_to_string partial: 'admin/enterprise_fees/calculator_settings', locals: { enterprise_fee: object }
|
||||
result = options[:controller].
|
||||
render_to_string(partial: 'admin/enterprise_fees/calculator_settings',
|
||||
locals: { enterprise_fee: object })
|
||||
end
|
||||
|
||||
result.gsub('[0]', '[{{ $index }}]').gsub('_0_', '_{{ $index }}_')
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :payment_method_ids, :shipping_method_ids
|
||||
attributes :producer_profile_only, :long_description, :permalink
|
||||
attributes :preferred_shopfront_message, :preferred_shopfront_closed_message, :preferred_shopfront_taxon_order, :preferred_shopfront_order_cycle_order
|
||||
attributes :preferred_product_selection_from_inventory_only
|
||||
attributes :owner, :contact, :users, :tag_groups, :default_tag_group
|
||||
attributes :require_login, :allow_guest_orders, :allow_order_changes
|
||||
attributes :logo, :promo_image
|
||||
attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :permalink,
|
||||
:payment_method_ids, :shipping_method_ids, :producer_profile_only, :long_description,
|
||||
:preferred_shopfront_message, :preferred_shopfront_closed_message,
|
||||
:preferred_shopfront_taxon_order, :preferred_shopfront_order_cycle_order,
|
||||
:preferred_product_selection_from_inventory_only,
|
||||
:owner, :contact, :users, :tag_groups, :default_tag_group,
|
||||
:require_login, :allow_guest_orders, :allow_order_changes,
|
||||
:logo, :promo_image
|
||||
|
||||
has_one :owner, serializer: Api::Admin::UserSerializer
|
||||
has_many :users, serializer: Api::Admin::UserSerializer
|
||||
@@ -21,7 +22,9 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
|
||||
|
||||
def tag_groups
|
||||
object.tag_rules.prioritised.reject(&:is_default).each_with_object([]) do |tag_rule, tag_groups|
|
||||
tag_group = find_match(tag_groups, tag_rule.preferred_customer_tags.split(",").map{ |t| { text: t } })
|
||||
tag_group = find_match(tag_groups, tag_rule.preferred_customer_tags.
|
||||
split(",").
|
||||
map{ |t| { text: t } })
|
||||
if tag_group[:rules].empty?
|
||||
tag_groups << tag_group
|
||||
tag_group[:position] = tag_groups.count
|
||||
@@ -32,13 +35,16 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
|
||||
|
||||
def default_tag_group
|
||||
default_rules = object.tag_rules.select(&:is_default)
|
||||
serialized_rules = ActiveModel::ArraySerializer.new(default_rules, each_serializer: Api::Admin::TagRuleSerializer)
|
||||
serialized_rules =
|
||||
ActiveModel::ArraySerializer.new(default_rules,
|
||||
each_serializer: Api::Admin::TagRuleSerializer)
|
||||
{ tags: [], rules: serialized_rules }
|
||||
end
|
||||
|
||||
def find_match(tag_groups, tags)
|
||||
tag_groups.each do |tag_group|
|
||||
return tag_group if tag_group[:tags].length == tags.length && (tag_group[:tags] & tags) == tag_group[:tags]
|
||||
return tag_group if tag_group[:tags].length == tags.length &&
|
||||
(tag_group[:tags] & tags) == tag_group[:tags]
|
||||
end
|
||||
{ tags: tags, rules: [] }
|
||||
end
|
||||
@@ -56,7 +62,7 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer
|
||||
# # medium: LOGO_MEDIUM_URL
|
||||
# # }
|
||||
def attachment_urls(attachment, versions)
|
||||
return unless attachment.exists?
|
||||
return unless attachment.file?
|
||||
|
||||
versions.each_with_object({}) do |version, urls|
|
||||
urls[version] = attachment.url(version)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class Api::Admin::ExchangeSerializer < ActiveModel::Serializer
|
||||
attributes :id, :sender_id, :receiver_id, :incoming, :variants, :receival_instructions, :pickup_time, :pickup_instructions
|
||||
attributes :tags, :tag_list
|
||||
attributes :id, :sender_id, :receiver_id, :incoming, :variants,
|
||||
:receival_instructions, :pickup_time, :pickup_instructions,
|
||||
:tags, :tag_list
|
||||
|
||||
has_many :enterprise_fees, serializer: Api::Admin::BasicEnterpriseFeeSerializer
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
require 'open_food_network/enterprise_issue_validator'
|
||||
|
||||
class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :managed, :supplied_products
|
||||
attributes :issues_summary_supplier, :issues_summary_distributor
|
||||
attributes :is_primary_producer, :is_distributor, :sells
|
||||
attributes :id, :name, :managed, :supplied_products,
|
||||
:issues_summary_supplier, :issues_summary_distributor,
|
||||
:is_primary_producer, :is_distributor, :sells
|
||||
|
||||
def issues_summary_supplier
|
||||
issues = OpenFoodNetwork::EnterpriseIssueValidator.new(object).issues_summary confirmation_only: true
|
||||
issues =
|
||||
OpenFoodNetwork::EnterpriseIssueValidator.
|
||||
new(object).
|
||||
issues_summary(confirmation_only: true)
|
||||
|
||||
if issues.nil? && products.empty?
|
||||
issues = "no products in inventory"
|
||||
end
|
||||
@@ -23,7 +27,8 @@ class Api::Admin::ForOrderCycle::EnterpriseSerializer < ActiveModel::Serializer
|
||||
|
||||
def supplied_products
|
||||
serializer = Api::Admin::ForOrderCycle::SuppliedProductSerializer
|
||||
ActiveModel::ArraySerializer.new(products, each_serializer: serializer, order_cycle: order_cycle)
|
||||
ActiveModel::ArraySerializer.new(products, each_serializer: serializer,
|
||||
order_cycle: order_cycle)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
require 'open_food_network/enterprise_issue_validator'
|
||||
|
||||
class Api::Admin::IndexEnterpriseSerializer < ActiveModel::Serializer
|
||||
attributes :name, :id, :permalink, :is_primary_producer, :sells, :producer_profile_only, :owned, :edit_path
|
||||
|
||||
attributes :issues, :warnings
|
||||
attributes :name, :id, :permalink, :is_primary_producer, :sells, :producer_profile_only, :owned,
|
||||
:edit_path, :issues, :warnings
|
||||
|
||||
def owned
|
||||
return true if options[:spree_current_user].admin?
|
||||
|
||||
@@ -5,9 +5,9 @@ module Api
|
||||
class IndexOrderCycleSerializer < ActiveModel::Serializer
|
||||
include OrderCyclesHelper
|
||||
|
||||
attributes :id, :name, :orders_open_at, :orders_close_at, :status, :variant_count, :deletable
|
||||
attributes :coordinator, :producers, :shops, :viewing_as_coordinator
|
||||
attributes :edit_path, :clone_path, :delete_path, :subscriptions_count
|
||||
attributes :id, :name, :orders_open_at, :orders_close_at, :status, :variant_count, :deletable,
|
||||
:coordinator, :producers, :shops, :viewing_as_coordinator,
|
||||
:edit_path, :clone_path, :delete_path, :subscriptions_count
|
||||
|
||||
has_many :schedules, serializer: Api::Admin::IdNameSerializer
|
||||
|
||||
@@ -68,7 +68,10 @@ module Api
|
||||
private
|
||||
|
||||
def visible_enterprises
|
||||
@visible_enterprises ||= OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).visible_enterprises
|
||||
@visible_enterprises ||=
|
||||
OpenFoodNetwork::OrderCyclePermissions.
|
||||
new(options[:current_user], object).
|
||||
visible_enterprises
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class Api::Admin::LineItemSerializer < ActiveModel::Serializer
|
||||
attributes :id, :quantity, :max_quantity, :price, :supplier, :final_weight_volume, :units_product, :units_variant
|
||||
attributes :id, :quantity, :max_quantity, :price, :supplier, :final_weight_volume,
|
||||
:units_product, :units_variant
|
||||
|
||||
has_one :order, serializer: Api::Admin::IdSerializer
|
||||
|
||||
@@ -20,7 +21,8 @@ class Api::Admin::LineItemSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def max_quantity
|
||||
return object.quantity unless object.max_quantity.present? && object.max_quantity > object.quantity
|
||||
return object.quantity unless object.max_quantity.present? &&
|
||||
object.max_quantity > object.quantity
|
||||
object.max_quantity
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
require 'open_food_network/order_cycle_permissions'
|
||||
|
||||
class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges
|
||||
attributes :editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges
|
||||
attributes :visible_variants_for_outgoing_exchanges
|
||||
attributes :viewing_as_coordinator, :schedule_ids, :subscriptions_count
|
||||
attributes :id, :name, :orders_open_at, :orders_close_at, :coordinator_id, :exchanges,
|
||||
:editable_variants_for_incoming_exchanges, :editable_variants_for_outgoing_exchanges,
|
||||
:visible_variants_for_outgoing_exchanges,
|
||||
:viewing_as_coordinator, :schedule_ids, :subscriptions_count
|
||||
|
||||
has_many :coordinator_fees, serializer: Api::IdSerializer
|
||||
|
||||
@@ -25,8 +25,14 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def exchanges
|
||||
scoped_exchanges = OpenFoodNetwork::OrderCyclePermissions.new(options[:current_user], object).visible_exchanges.by_enterprise_name
|
||||
ActiveModel::ArraySerializer.new(scoped_exchanges, each_serializer: Api::Admin::ExchangeSerializer, current_user: options[:current_user])
|
||||
scoped_exchanges =
|
||||
OpenFoodNetwork::OrderCyclePermissions.
|
||||
new(options[:current_user], object).
|
||||
visible_exchanges.by_enterprise_name
|
||||
|
||||
ActiveModel::ArraySerializer.
|
||||
new(scoped_exchanges, each_serializer: Api::Admin::ExchangeSerializer,
|
||||
current_user: options[:current_user])
|
||||
end
|
||||
|
||||
def editable_variants_for_incoming_exchanges
|
||||
@@ -66,9 +72,13 @@ class Api::Admin::OrderCycleSerializer < ActiveModel::Serializer
|
||||
# for shops. We need this here to allow hubs to restrict visible variants to only those in
|
||||
# their inventory if they so choose
|
||||
variants = if enterprise.prefers_product_selection_from_inventory_only?
|
||||
permissions.visible_variants_for_outgoing_exchanges_to(enterprise).visible_for(enterprise)
|
||||
permissions.
|
||||
visible_variants_for_outgoing_exchanges_to(enterprise).
|
||||
visible_for(enterprise)
|
||||
else
|
||||
permissions.visible_variants_for_outgoing_exchanges_to(enterprise).not_hidden_for(enterprise)
|
||||
permissions.
|
||||
visible_variants_for_outgoing_exchanges_to(enterprise).
|
||||
not_hidden_for(enterprise)
|
||||
end.pluck(:id)
|
||||
visible[enterprise.id] = variants if variants.any?
|
||||
end
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
class Api::Admin::OrderSerializer < ActiveModel::Serializer
|
||||
attributes :id, :number, :full_name, :email, :phone, :completed_at, :display_total
|
||||
attributes :edit_path, :state, :payment_state, :shipment_state
|
||||
attributes :payments_path, :ship_path, :ready_to_ship, :created_at
|
||||
attributes :distributor_name, :special_instructions, :payment_capture_path
|
||||
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,
|
||||
:item_total, :adjustment_total, :payment_total, :total
|
||||
|
||||
has_one :distributor, serializer: Api::Admin::IdSerializer
|
||||
has_one :order_cycle, serializer: Api::Admin::IdSerializer
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
module Api
|
||||
module Admin
|
||||
class SubscriptionSerializer < ActiveModel::Serializer
|
||||
attributes :id, :shop_id, :customer_id, :schedule_id, :payment_method_id, :shipping_method_id, :begins_at, :ends_at
|
||||
attributes :customer_email, :schedule_name, :edit_path, :canceled_at, :paused_at, :state
|
||||
attributes :shipping_fee_estimate, :payment_fee_estimate
|
||||
attributes :id, :shop_id, :customer_id, :schedule_id, :payment_method_id, :shipping_method_id,
|
||||
:begins_at, :ends_at,
|
||||
:customer_email, :schedule_name, :edit_path, :canceled_at, :paused_at, :state,
|
||||
:shipping_fee_estimate, :payment_fee_estimate
|
||||
|
||||
has_many :subscription_line_items, serializer: Api::Admin::SubscriptionLineItemSerializer
|
||||
has_many :closed_proxy_orders, serializer: Api::Admin::ProxyOrderSerializer
|
||||
|
||||
@@ -16,7 +16,8 @@ module Api::Admin::TagRule
|
||||
end
|
||||
|
||||
class FilterShippingMethodsSerializer < BaseSerializer
|
||||
attributes :preferred_matched_shipping_methods_visibility, :preferred_shipping_method_tags, :shipping_method_tags
|
||||
attributes :preferred_matched_shipping_methods_visibility, :preferred_shipping_method_tags,
|
||||
:shipping_method_tags
|
||||
|
||||
def shipping_method_tags
|
||||
object.preferred_shipping_method_tags.split(",")
|
||||
@@ -24,7 +25,8 @@ module Api::Admin::TagRule
|
||||
end
|
||||
|
||||
class FilterPaymentMethodsSerializer < BaseSerializer
|
||||
attributes :preferred_matched_payment_methods_visibility, :preferred_payment_method_tags, :payment_method_tags
|
||||
attributes :preferred_matched_payment_methods_visibility, :preferred_payment_method_tags,
|
||||
:payment_method_tags
|
||||
|
||||
def payment_method_tags
|
||||
object.preferred_payment_method_tags.split(",")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Api::Admin::VariantOverrideSerializer < ActiveModel::Serializer
|
||||
attributes :id, :hub_id, :variant_id, :sku, :price, :count_on_hand, :on_demand, :default_stock, :resettable
|
||||
attributes :tag_list, :tags, :import_date
|
||||
attributes :id, :hub_id, :variant_id, :sku, :price, :count_on_hand, :on_demand, :default_stock,
|
||||
:resettable, :tag_list, :tags, :import_date
|
||||
|
||||
def tag_list
|
||||
object.tag_list.join(",")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
class Api::Admin::VariantSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :producer_name, :image, :sku, :import_date
|
||||
attributes :options_text, :unit_value, :unit_description, :unit_to_display
|
||||
attributes :display_as, :display_name, :name_to_display
|
||||
attributes :price, :on_demand, :on_hand, :in_stock, :stock_location_id, :stock_location_name
|
||||
attributes :id, :name, :producer_name, :image, :sku, :import_date,
|
||||
:options_text, :unit_value, :unit_description, :unit_to_display,
|
||||
:display_as, :display_name, :name_to_display,
|
||||
:price, :on_demand, :on_hand, :in_stock, :stock_location_id, :stock_location_name
|
||||
|
||||
has_many :variant_overrides
|
||||
|
||||
|
||||
12
app/serializers/api/order_detailed_serializer.rb
Normal file
12
app/serializers/api/order_detailed_serializer.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
module Api
|
||||
class OrderDetailedSerializer < Api::Admin::OrderSerializer
|
||||
has_one :shipping_method, serializer: Api::ShippingMethodSerializer
|
||||
has_one :ship_address, serializer: Api::AddressSerializer
|
||||
has_one :bill_address, serializer: Api::AddressSerializer
|
||||
|
||||
has_many :line_items, serializer: Api::LineItemSerializer
|
||||
has_many :adjustments, serializer: Api::AdjustmentSerializer
|
||||
|
||||
has_many :payments, serializer: Api::PaymentSerializer
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
module Api
|
||||
class OrderSerializer < ActiveModel::Serializer
|
||||
attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state
|
||||
attributes :outstanding_balance, :payments, :path, :cancel_path
|
||||
attributes :changes_allowed, :changes_allowed_until, :item_count
|
||||
attributes :shop_id
|
||||
attributes :number, :completed_at, :total, :state, :shipment_state, :payment_state,
|
||||
:outstanding_balance, :payments, :path, :cancel_path,
|
||||
:changes_allowed, :changes_allowed_until, :item_count,
|
||||
:shop_id
|
||||
|
||||
has_many :payments, serializer: Api::PaymentSerializer
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module Api
|
||||
class PaymentSerializer < ActiveModel::Serializer
|
||||
attributes :amount, :updated_at, :payment_method, :state
|
||||
|
||||
def payment_method
|
||||
object.payment_method.try(:name)
|
||||
end
|
||||
|
||||
@@ -1,43 +1,11 @@
|
||||
require 'open_food_network/scope_variant_to_hub'
|
||||
|
||||
class Api::ProductSerializer < ActiveModel::Serializer
|
||||
# TODO
|
||||
# Prices can't be cached? How?
|
||||
def serializable_hash
|
||||
cached_serializer_hash.merge(uncached_serializer_hash)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cached_serializer_hash
|
||||
Api::CachedProductSerializer.new(object, @options).serializable_hash
|
||||
end
|
||||
|
||||
def uncached_serializer_hash
|
||||
Api::UncachedProductSerializer.new(object, @options).serializable_hash
|
||||
end
|
||||
end
|
||||
|
||||
class Api::UncachedProductSerializer < ActiveModel::Serializer
|
||||
attributes :price
|
||||
|
||||
def price
|
||||
if options[:enterprise_fee_calculator]
|
||||
object.master.price + options[:enterprise_fee_calculator].indexed_fees_for(object.master)
|
||||
else
|
||||
object.master.price_with_fees(options[:current_distributor], options[:current_order_cycle])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Api::CachedProductSerializer < ActiveModel::Serializer
|
||||
# cached
|
||||
# delegate :cache_key, to: :object
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
attributes :id, :name, :permalink, :meta_keywords
|
||||
attributes :group_buy, :notes, :description, :description_html
|
||||
attributes :properties_with_values
|
||||
attributes :properties_with_values, :price
|
||||
|
||||
has_many :variants, serializer: Api::VariantSerializer
|
||||
has_one :master, serializer: Api::VariantSerializer
|
||||
@@ -70,4 +38,12 @@ class Api::CachedProductSerializer < ActiveModel::Serializer
|
||||
def master
|
||||
options[:master_variants][object.id].andand.first
|
||||
end
|
||||
|
||||
def price
|
||||
if options[:enterprise_fee_calculator]
|
||||
object.master.price + options[:enterprise_fee_calculator].indexed_fees_for(object.master)
|
||||
else
|
||||
object.master.price_with_fees(options[:current_distributor], options[:current_order_cycle])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class Api::VariantSerializer < ActiveModel::Serializer
|
||||
attributes :id, :is_master, :product_name, :sku
|
||||
attributes :options_text, :unit_value, :unit_description, :unit_to_display
|
||||
attributes :display_as, :display_name, :name_to_display
|
||||
attributes :price, :on_demand, :on_hand, :fees, :price_with_fees
|
||||
attributes :tag_list
|
||||
attributes :id, :is_master, :product_name, :sku,
|
||||
:options_text, :unit_value, :unit_description, :unit_to_display,
|
||||
:display_as, :display_name, :name_to_display,
|
||||
:price, :on_demand, :on_hand, :fees, :price_with_fees,
|
||||
:tag_list
|
||||
|
||||
delegate :price, to: :object
|
||||
|
||||
|
||||
13
app/services/default_shipping_category.rb
Normal file
13
app/services/default_shipping_category.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# Encapsulates the concept of default stock location in creation of a product or a shipping method.
|
||||
|
||||
class DefaultShippingCategory
|
||||
NAME = 'Default'.freeze
|
||||
|
||||
def self.create!
|
||||
Spree::ShippingCategory.create!(name: NAME)
|
||||
end
|
||||
|
||||
def self.find_or_create
|
||||
Spree::ShippingCategory.find_or_create_by_name(NAME)
|
||||
end
|
||||
end
|
||||
@@ -73,7 +73,9 @@ class EmbeddedPageService
|
||||
|
||||
def current_referer
|
||||
return if @request.referer.blank?
|
||||
URI(@request.referer).host.downcase
|
||||
uri = URI(@request.referer)
|
||||
return if uri.host.blank?
|
||||
uri.host.downcase
|
||||
end
|
||||
|
||||
def current_referer_without_www
|
||||
|
||||
@@ -1,41 +1,80 @@
|
||||
# Finds valid products distributed by a particular distributor in an order cycle
|
||||
#
|
||||
# If a product without variants is added to an order cycle, and then some
|
||||
# variants are added to that product, but not the order cycle, then the master
|
||||
# variant should not available for customers to purchase. This class filters
|
||||
# out such products so that the customer cannot purchase them.
|
||||
# Returns a (paginatable) AR object for the products or variants in stock for a given shop and OC.
|
||||
# The stock-checking includes on_demand and stock level overrides from variant_overrides.
|
||||
class OrderCycleDistributedProducts
|
||||
def initialize(order_cycle, distributor)
|
||||
@order_cycle = order_cycle
|
||||
def initialize(distributor, order_cycle, customer)
|
||||
@distributor = distributor
|
||||
@order_cycle = order_cycle
|
||||
@customer = customer
|
||||
end
|
||||
|
||||
# Returns an ActiveRecord relation without invalid products. Check
|
||||
# #valid_products_distributed_by for details
|
||||
#
|
||||
# @return [ActiveRecord::Relation<Spree::Product>]
|
||||
def relation
|
||||
variants = order_cycle.variants_distributed_by(distributor)
|
||||
products = variants.map(&:product).uniq
|
||||
def products_relation
|
||||
Spree::Product.where(id: stocked_products)
|
||||
end
|
||||
|
||||
valid_products = products.reject do |product|
|
||||
product_has_only_obsolete_master_in_distribution?(product, variants)
|
||||
end
|
||||
product_ids = valid_products.map(&:id)
|
||||
|
||||
Spree::Product.where(id: product_ids)
|
||||
def variants_relation
|
||||
order_cycle.
|
||||
variants_distributed_by(distributor).
|
||||
merge(stocked_variants_and_overrides)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :order_cycle, :distributor
|
||||
attr_reader :distributor, :order_cycle, :customer
|
||||
|
||||
# If a product without variants is added to an order cycle, and then some variants are added
|
||||
# to that product, but not the order cycle, then the master variant should not available for
|
||||
# customers to purchase.
|
||||
def product_has_only_obsolete_master_in_distribution?(product, distributed_variants)
|
||||
product.has_variants? &&
|
||||
distributed_variants.include?(product.master) &&
|
||||
(product.variants & distributed_variants).empty?
|
||||
def stocked_products
|
||||
order_cycle.
|
||||
variants_distributed_by(distributor).
|
||||
merge(stocked_variants_and_overrides).
|
||||
select("DISTINCT spree_variants.product_id")
|
||||
end
|
||||
|
||||
def stocked_variants_and_overrides
|
||||
stocked_variants = Spree::Variant.
|
||||
joins("LEFT OUTER JOIN variant_overrides ON variant_overrides.variant_id = spree_variants.id
|
||||
AND variant_overrides.hub_id = #{distributor.id}").
|
||||
joins(:stock_items).
|
||||
where(query_stock_with_overrides)
|
||||
|
||||
ProductTagRulesFilterer.new(distributor, customer, stocked_variants).call
|
||||
end
|
||||
|
||||
def query_stock_with_overrides
|
||||
"( #{variant_not_overriden} AND ( #{variant_on_demand} OR #{variant_in_stock} ) )
|
||||
OR ( #{variant_overriden} AND ( #{override_on_demand} OR #{override_in_stock} ) )
|
||||
OR ( #{variant_overriden} AND ( #{override_on_demand_null} AND #{variant_on_demand} ) )
|
||||
OR ( #{variant_overriden} AND ( #{override_on_demand_null}
|
||||
AND #{variant_not_on_demand} AND #{variant_in_stock} ) )"
|
||||
end
|
||||
|
||||
def variant_not_overriden
|
||||
"variant_overrides.id IS NULL"
|
||||
end
|
||||
|
||||
def variant_overriden
|
||||
"variant_overrides.id IS NOT NULL"
|
||||
end
|
||||
|
||||
def variant_in_stock
|
||||
"spree_stock_items.count_on_hand > 0"
|
||||
end
|
||||
|
||||
def variant_on_demand
|
||||
"spree_stock_items.backorderable IS TRUE"
|
||||
end
|
||||
|
||||
def variant_not_on_demand
|
||||
"spree_stock_items.backorderable IS FALSE"
|
||||
end
|
||||
|
||||
def override_on_demand
|
||||
"variant_overrides.on_demand IS TRUE"
|
||||
end
|
||||
|
||||
def override_in_stock
|
||||
"variant_overrides.count_on_hand > 0"
|
||||
end
|
||||
|
||||
def override_on_demand_null
|
||||
"variant_overrides.on_demand IS NULL"
|
||||
end
|
||||
end
|
||||
|
||||
111
app/services/product_tag_rules_filterer.rb
Normal file
111
app/services/product_tag_rules_filterer.rb
Normal file
@@ -0,0 +1,111 @@
|
||||
# Takes a Spree::Variant AR object and filters results based on applicable tag rules.
|
||||
# Tag rules exists in the context of enterprise, customer, and variant_overrides,
|
||||
# and are applied to variant_overrides only. Returns a Spree::Variant AR object.
|
||||
|
||||
class ProductTagRulesFilterer
|
||||
def initialize(distributor, customer, variants_relation)
|
||||
@distributor = distributor
|
||||
@customer = customer
|
||||
@variants_relation = variants_relation
|
||||
end
|
||||
|
||||
def call
|
||||
return variants_relation unless distributor_rules.any?
|
||||
|
||||
filter(variants_relation)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_accessor :distributor, :customer, :variants_relation
|
||||
|
||||
def distributor_rules
|
||||
@distributor_rules ||= TagRule::FilterProducts.prioritised.for(distributor).all
|
||||
end
|
||||
|
||||
def filter(variants_relation)
|
||||
return variants_relation unless overrides_to_hide.any?
|
||||
|
||||
variants_relation.where(query_with_tag_rules)
|
||||
end
|
||||
|
||||
def query_with_tag_rules
|
||||
"#{variant_not_overriden} OR ( #{variant_overriden}
|
||||
AND ( #{override_not_hidden_by_rule}
|
||||
OR #{override_shown_by_rule} ) )"
|
||||
end
|
||||
|
||||
def variant_not_overriden
|
||||
"variant_overrides.id IS NULL"
|
||||
end
|
||||
|
||||
def variant_overriden
|
||||
"variant_overrides.id IS NOT NULL"
|
||||
end
|
||||
|
||||
def override_not_hidden_by_rule
|
||||
return "FALSE" unless overrides_to_hide.any?
|
||||
"variant_overrides.id NOT IN (#{overrides_to_hide.join(',')})"
|
||||
end
|
||||
|
||||
def override_shown_by_rule
|
||||
return "FALSE" unless overrides_to_show.any?
|
||||
"variant_overrides.id IN (#{overrides_to_show.join(',')})"
|
||||
end
|
||||
|
||||
def overrides_to_hide
|
||||
@overrides_to_hide ||= VariantOverride.where(hub_id: distributor.id).
|
||||
tagged_with(default_rule_tags + hide_rule_tags, any: true).
|
||||
pluck(:id)
|
||||
end
|
||||
|
||||
def overrides_to_show
|
||||
@overrides_to_show ||= VariantOverride.where(hub_id: distributor.id).
|
||||
tagged_with(show_rule_tags, any: true).
|
||||
pluck(:id)
|
||||
end
|
||||
|
||||
def default_rule_tags
|
||||
default_rules.map(&:preferred_variant_tags)
|
||||
end
|
||||
|
||||
def hide_rule_tags
|
||||
hide_rules.map(&:preferred_variant_tags)
|
||||
end
|
||||
|
||||
def show_rule_tags
|
||||
show_rules.map(&:preferred_variant_tags)
|
||||
end
|
||||
|
||||
def default_rules
|
||||
# These rules hide a variant_override with tag X and apply to all customers
|
||||
distributor_rules.select(&:is_default?)
|
||||
end
|
||||
|
||||
def non_default_rules
|
||||
# These rules show or hide a variant_override with tag X for customer with tag Y
|
||||
distributor_rules.reject(&:is_default?)
|
||||
end
|
||||
|
||||
def customer_applicable_rules
|
||||
# Rules which apply specifically to the current customer
|
||||
@customer_applicable_rules ||= non_default_rules.select{ |rule| customer_tagged?(rule) }
|
||||
end
|
||||
|
||||
def hide_rules
|
||||
@hide_rules ||= customer_applicable_rules.
|
||||
select{ |rule| rule.preferred_matched_variants_visibility == 'hidden' }
|
||||
end
|
||||
|
||||
def show_rules
|
||||
customer_applicable_rules - hide_rules
|
||||
end
|
||||
|
||||
def customer_tagged?(rule)
|
||||
customer_tag_list.include? rule.preferred_customer_tags
|
||||
end
|
||||
|
||||
def customer_tag_list
|
||||
customer.andand.tag_list || []
|
||||
end
|
||||
end
|
||||
98
app/services/products_renderer.rb
Normal file
98
app/services/products_renderer.rb
Normal file
@@ -0,0 +1,98 @@
|
||||
require 'open_food_network/scope_product_to_hub'
|
||||
|
||||
class ProductsRenderer
|
||||
class NoProducts < RuntimeError; end
|
||||
DEFAULT_PAGE = 1
|
||||
DEFAULT_PER_PAGE = 10
|
||||
|
||||
def initialize(distributor, order_cycle, customer, args = {})
|
||||
@distributor = distributor
|
||||
@order_cycle = order_cycle
|
||||
@customer = customer
|
||||
@args = args
|
||||
end
|
||||
|
||||
def products_json
|
||||
raise NoProducts unless order_cycle && distributor && products
|
||||
|
||||
ActiveModel::ArraySerializer.new(products,
|
||||
each_serializer: Api::ProductSerializer,
|
||||
current_order_cycle: order_cycle,
|
||||
current_distributor: distributor,
|
||||
variants: variants_for_shop_by_id,
|
||||
master_variants: master_variants_for_shop_by_id,
|
||||
enterprise_fee_calculator: enterprise_fee_calculator).to_json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :order_cycle, :distributor, :customer, :args
|
||||
|
||||
def products
|
||||
return unless order_cycle
|
||||
|
||||
@products ||= begin
|
||||
results = distributed_products.products_relation.order(taxon_order)
|
||||
|
||||
filter_and_paginate(results).
|
||||
each { |product| product_scoper.scope(product) } # Scope results with variant_overrides
|
||||
end
|
||||
end
|
||||
|
||||
def product_scoper
|
||||
OpenFoodNetwork::ScopeProductToHub.new(distributor)
|
||||
end
|
||||
|
||||
def enterprise_fee_calculator
|
||||
OpenFoodNetwork::EnterpriseFeeCalculator.new distributor, order_cycle
|
||||
end
|
||||
|
||||
def filter_and_paginate(query)
|
||||
query.
|
||||
ransack(args[:q]).
|
||||
result.
|
||||
page(args[:page] || DEFAULT_PAGE).
|
||||
per(args[:per_page] || DEFAULT_PER_PAGE)
|
||||
end
|
||||
|
||||
def distributed_products
|
||||
OrderCycleDistributedProducts.new(distributor, order_cycle, customer)
|
||||
end
|
||||
|
||||
def taxon_order
|
||||
if distributor.preferred_shopfront_taxon_order.present?
|
||||
distributor
|
||||
.preferred_shopfront_taxon_order
|
||||
.split(",").map { |id| "primary_taxon_id=#{id} DESC" }
|
||||
.join(",") + ", name ASC"
|
||||
else
|
||||
"name ASC"
|
||||
end
|
||||
end
|
||||
|
||||
def variants_for_shop
|
||||
@variants_for_shop ||= begin
|
||||
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
|
||||
|
||||
distributed_products.variants_relation.
|
||||
includes(:default_price, :stock_locations, :product).
|
||||
where(product_id: products).
|
||||
each { |v| scoper.scope(v) } # Scope results with variant_overrides
|
||||
end
|
||||
end
|
||||
|
||||
def variants_for_shop_by_id
|
||||
index_by_product_id variants_for_shop.reject(&:is_master)
|
||||
end
|
||||
|
||||
def master_variants_for_shop_by_id
|
||||
index_by_product_id variants_for_shop.select(&:is_master)
|
||||
end
|
||||
|
||||
def index_by_product_id(variants)
|
||||
variants.each_with_object({}) do |v, vs|
|
||||
vs[v.product_id] ||= []
|
||||
vs[v.product_id] << v
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -9,8 +9,8 @@ class SubscriptionValidator
|
||||
|
||||
attr_reader :subscription
|
||||
|
||||
validates_presence_of :shop, :customer, :schedule, :shipping_method, :payment_method
|
||||
validates_presence_of :bill_address, :ship_address, :begins_at
|
||||
validates :shop, :customer, :schedule, :shipping_method, :payment_method, presence: true
|
||||
validates :bill_address, :ship_address, :begins_at, presence: true
|
||||
validate :shipping_method_allowed?
|
||||
validate :payment_method_allowed?
|
||||
validate :payment_method_type_allowed?
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
- content_for :page_title do
|
||||
= t(:cache_settings)
|
||||
|
||||
= form_tag main_app.admin_cache_settings_path, :method => :put do
|
||||
.field
|
||||
= hidden_field_tag 'preferences[enable_products_cache?]', '0'
|
||||
= check_box_tag 'preferences[enable_products_cache?]', '1', Spree::Config[:enable_products_cache?]
|
||||
= label_tag nil, t('.enable_products_cache')
|
||||
.form-buttons
|
||||
= button t(:update), 'icon-refresh'
|
||||
|
||||
%br
|
||||
%br
|
||||
|
||||
%h4= t(:cache_state)
|
||||
%br
|
||||
%table.index
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.distributor')
|
||||
%th= t('.order_cycle')
|
||||
%th= t('.status')
|
||||
%th= t('.diff')
|
||||
%tbody
|
||||
- @results.each do |result|
|
||||
%tr
|
||||
%td= result[:distributor].name
|
||||
%td= result[:order_cycle].name
|
||||
%td= result[:status] ? t(:ok) : t('.error')
|
||||
%td
|
||||
%pre= result[:diff].to_s(:text)
|
||||
@@ -2,8 +2,8 @@
|
||||
%h1.page-title
|
||||
=t :customers
|
||||
|
||||
- content_for :app_wrapper_attrs do
|
||||
= "ng-app='admin.customers'"
|
||||
- content_for :main_ng_app_name do
|
||||
= "admin.customers"
|
||||
|
||||
- content_for :page_actions do
|
||||
%li
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
= t('.editing')
|
||||
= @enterprise.name
|
||||
|
||||
- content_for :app_wrapper_attrs do
|
||||
= "ng-app='admin.enterprises'"
|
||||
- content_for :main_ng_app_name do
|
||||
= "admin.enterprises"
|
||||
|
||||
- content_for :page_actions do
|
||||
%li= select :enterprise, :id, options_for_select(editable_enterprises.collect {|e| [e.name, e.id, {:'data-url' => "#{main_app.edit_admin_enterprise_path(e.permalink)}", :'ng-selected' => "selected==#{e.id}"}]}, @enterprise.id ), {}, {:'enterprise-switcher' => '', 'data-initial' => "#{@enterprise.id}", :'ng-init' => "selected='#{@enterprise.id}'", :'ng-model' => 'selected', :id => 'enterprise_switcher', :class => 'select2'}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
- content_for :page_title do
|
||||
= t('.title')
|
||||
|
||||
- content_for :app_wrapper_attrs do
|
||||
= "ng-app='admin.enterprises'"
|
||||
- content_for :main_ng_app_name do
|
||||
= "admin.enterprises"
|
||||
|
||||
- content_for :page_actions do
|
||||
= render 'admin/shared/user_guide_link'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user