mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-12 18:36:49 +00:00
Compare commits
508 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eafffa2c23 | ||
|
|
82a4753eec | ||
|
|
b92e858448 | ||
|
|
b5ba2acb21 | ||
|
|
e97a16cb40 | ||
|
|
57d87a8042 | ||
|
|
0ca87580e8 | ||
|
|
59e0f3d9f4 | ||
|
|
fc5aff8c79 | ||
|
|
dd717fe8ac | ||
|
|
6341c5fd80 | ||
|
|
47ac6c1491 | ||
|
|
6afda141a1 | ||
|
|
5bb2614f9d | ||
|
|
b3c968856b | ||
|
|
b0a7497f2a | ||
|
|
f959e632ea | ||
|
|
f9cf826f1c | ||
|
|
ececbce596 | ||
|
|
1b7ac1a252 | ||
|
|
d31b24786a | ||
|
|
374bf04118 | ||
|
|
3aff7f62e3 | ||
|
|
fc5e346a06 | ||
|
|
29bbf2fa74 | ||
|
|
64c66ddedc | ||
|
|
e0e2c32d9f | ||
|
|
003341ef7a | ||
|
|
94f8ea2f93 | ||
|
|
eb64112b22 | ||
|
|
3e14b62b46 | ||
|
|
3244650932 | ||
|
|
b6753a2593 | ||
|
|
1b119805b4 | ||
|
|
edde7689a9 | ||
|
|
8060977786 | ||
|
|
837a345958 | ||
|
|
7c5e511fde | ||
|
|
d18a06a0f7 | ||
|
|
d23b4fd307 | ||
|
|
2cb4c6bec2 | ||
|
|
924e816a5b | ||
|
|
109da43905 | ||
|
|
33ca6a2096 | ||
|
|
e7b780f963 | ||
|
|
13cba3d244 | ||
|
|
ce45e7cf71 | ||
|
|
ba5a56db14 | ||
|
|
276dcf4a3b | ||
|
|
dcfb1aec6d | ||
|
|
fde2aac366 | ||
|
|
63138aef30 | ||
|
|
7612415991 | ||
|
|
53d901b41b | ||
|
|
bc859cf9f7 | ||
|
|
af48cac140 | ||
|
|
59bb956677 | ||
|
|
55b3f4d54f | ||
|
|
452ab3a842 | ||
|
|
a049e7a433 | ||
|
|
97063bf47e | ||
|
|
e64d573337 | ||
|
|
7858a26e5e | ||
|
|
4922c05bcf | ||
|
|
d06b7b8606 | ||
|
|
72b47fbceb | ||
|
|
da7b0966be | ||
|
|
445eb9f287 | ||
|
|
5b4dd57380 | ||
|
|
adb61e48c5 | ||
|
|
838ffdbf00 | ||
|
|
fe8beb5448 | ||
|
|
18ee5254f9 | ||
|
|
40f8cf660c | ||
|
|
2c70db7952 | ||
|
|
1a38a4e1a7 | ||
|
|
15d106bb0a | ||
|
|
c8be2fb89a | ||
|
|
b1f8f91011 | ||
|
|
e08606a310 | ||
|
|
2230d83729 | ||
|
|
d38da02113 | ||
|
|
0e268a171f | ||
|
|
73c4eedd06 | ||
|
|
8a220742f5 | ||
|
|
9fa604db0d | ||
|
|
6083d91d3e | ||
|
|
9bdb396b86 | ||
|
|
ad42b1b485 | ||
|
|
78170bc709 | ||
|
|
957b398a54 | ||
|
|
c3d25bf163 | ||
|
|
ce2a164c66 | ||
|
|
b898ce1ae1 | ||
|
|
409681b7af | ||
|
|
9317549103 | ||
|
|
b22ad244f9 | ||
|
|
9c17a91215 | ||
|
|
6a1c541479 | ||
|
|
6c64261868 | ||
|
|
f437d0f8a0 | ||
|
|
79d6d7cc9e | ||
|
|
e200ece280 | ||
|
|
30bf9257ab | ||
|
|
03e229da08 | ||
|
|
28473c9087 | ||
|
|
09c8819e5a | ||
|
|
f7c047b798 | ||
|
|
b6da0e2092 | ||
|
|
95963c5732 | ||
|
|
aba1b5b67a | ||
|
|
fe58121c7f | ||
|
|
5c4a2c2790 | ||
|
|
a07281910b | ||
|
|
72f9da3ac4 | ||
|
|
375b4648dc | ||
|
|
9af0a39305 | ||
|
|
6817231f29 | ||
|
|
7c7f9551d6 | ||
|
|
d568b45d4a | ||
|
|
b76a6d15a3 | ||
|
|
940423acfc | ||
|
|
6a57aa3b29 | ||
|
|
ca78e9d0e2 | ||
|
|
e705e88007 | ||
|
|
1fbb9fa3df | ||
|
|
71f00f9283 | ||
|
|
7d33a237d0 | ||
|
|
857cacb74b | ||
|
|
fbfe663ebc | ||
|
|
83b90f3167 | ||
|
|
14fd9a121e | ||
|
|
904e89e325 | ||
|
|
d254df7ccc | ||
|
|
8caf10f634 | ||
|
|
2e98b0b5c1 | ||
|
|
2966dd9536 | ||
|
|
21e1c0ed0b | ||
|
|
48b99d02b9 | ||
|
|
f17a2eeaea | ||
|
|
18419d0276 | ||
|
|
bd19d8b0bd | ||
|
|
4bcd665379 | ||
|
|
e63dbcfa89 | ||
|
|
c3283adcf5 | ||
|
|
b2ed69831b | ||
|
|
7daba62f43 | ||
|
|
a08020490d | ||
|
|
eb4d970bc7 | ||
|
|
7a3549209f | ||
|
|
52ebd1b402 | ||
|
|
a3a26f704f | ||
|
|
cff8f6dd96 | ||
|
|
81537d92cf | ||
|
|
91e88bd028 | ||
|
|
f5e254a105 | ||
|
|
b41b5d0395 | ||
|
|
296d2e5edb | ||
|
|
ac0a62e962 | ||
|
|
523faa670d | ||
|
|
2c5db8935b | ||
|
|
e5e9325499 | ||
|
|
2e4f8003b6 | ||
|
|
24c8f38111 | ||
|
|
b801bffcd9 | ||
|
|
60d9edb185 | ||
|
|
434b68b019 | ||
|
|
9af4bb9757 | ||
|
|
d847560d7c | ||
|
|
87fae15434 | ||
|
|
e27f7a4301 | ||
|
|
bddfa95eb5 | ||
|
|
ef0fb18fda | ||
|
|
87ee4bbebc | ||
|
|
f5567e556b | ||
|
|
54c3c73ed2 | ||
|
|
8dfdc9bc15 | ||
|
|
36aa52736a | ||
|
|
ac38b2735c | ||
|
|
8b93c5ab56 | ||
|
|
434f98fb46 | ||
|
|
c82c54873c | ||
|
|
de180d32bf | ||
|
|
5b481c19cc | ||
|
|
7110f9e6ee | ||
|
|
63d748b2a4 | ||
|
|
310906c7da | ||
|
|
b81843921b | ||
|
|
dd0e135a4d | ||
|
|
0f2c5d379a | ||
|
|
a29f263041 | ||
|
|
26cd0f4a9d | ||
|
|
0b878dd0a2 | ||
|
|
45c204017f | ||
|
|
fa98a8ea17 | ||
|
|
17c2f7b138 | ||
|
|
70643a84b2 | ||
|
|
62a00b17a2 | ||
|
|
c4f96a1dcf | ||
|
|
6f4f3d42cc | ||
|
|
7582df2771 | ||
|
|
e8692fec4c | ||
|
|
3b2c10526d | ||
|
|
0e62dc04bd | ||
|
|
e2940eb9ff | ||
|
|
1c1f066884 | ||
|
|
d5cf355a11 | ||
|
|
d2eee1dafd | ||
|
|
540b26105f | ||
|
|
c788f1ae57 | ||
|
|
b1a274ea27 | ||
|
|
00c7b9986b | ||
|
|
25b11f1f4b | ||
|
|
22384cb4da | ||
|
|
9dc18afef0 | ||
|
|
7baa875a91 | ||
|
|
3de887e1d8 | ||
|
|
d2d3d767ac | ||
|
|
a4ff74272b | ||
|
|
1000dc52bf | ||
|
|
54b8d22e0d | ||
|
|
7e00f78a77 | ||
|
|
5a9b5660f1 | ||
|
|
1d42ce885b | ||
|
|
17751c448f | ||
|
|
4b8d9d18d7 | ||
|
|
a21ef19529 | ||
|
|
ea80ae3832 | ||
|
|
bf26a26743 | ||
|
|
e3f840f48c | ||
|
|
42ca7888c0 | ||
|
|
8aa892136e | ||
|
|
2d21341183 | ||
|
|
5cabf59015 | ||
|
|
289dff5b91 | ||
|
|
c4e4beb912 | ||
|
|
27d8951add | ||
|
|
f7720f1b1f | ||
|
|
c46ae4354d | ||
|
|
5f00323cbb | ||
|
|
c52f29706b | ||
|
|
2590745b2e | ||
|
|
6db0421347 | ||
|
|
b63c47cca2 | ||
|
|
2b9f57f4e0 | ||
|
|
8a1de72542 | ||
|
|
2cb3a0cd99 | ||
|
|
b0637a24ff | ||
|
|
d969b68c8c | ||
|
|
a89d4266d5 | ||
|
|
4b8ced5fc0 | ||
|
|
2678342122 | ||
|
|
b33c819863 | ||
|
|
97d7e27786 | ||
|
|
e73e43838c | ||
|
|
cdc40fbc38 | ||
|
|
3430cc617a | ||
|
|
ed6f042446 | ||
|
|
80a8c436d6 | ||
|
|
049a87e8a9 | ||
|
|
deb5b8e74c | ||
|
|
a5dd14d902 | ||
|
|
9a6859edc0 | ||
|
|
c0c53113d3 | ||
|
|
26688409a1 | ||
|
|
0893d14025 | ||
|
|
efd314e3b1 | ||
|
|
bd7549c57f | ||
|
|
45cf54408d | ||
|
|
15360740b1 | ||
|
|
5ce5072f26 | ||
|
|
35133f7ee8 | ||
|
|
5becbc2a11 | ||
|
|
390d80f0eb | ||
|
|
6da43850d1 | ||
|
|
a146bdacc8 | ||
|
|
43cadb00c4 | ||
|
|
2e616a9e31 | ||
|
|
1927e2883e | ||
|
|
a4b94cf39f | ||
|
|
a35f3c130e | ||
|
|
a93243a8b7 | ||
|
|
de4402457a | ||
|
|
bf9f7309f7 | ||
|
|
4b0e1610ec | ||
|
|
0c6fe20e82 | ||
|
|
0f2e23d225 | ||
|
|
45b5e838b7 | ||
|
|
129de8fd57 | ||
|
|
e24d858af9 | ||
|
|
d2cacf5330 | ||
|
|
af231d2ebe | ||
|
|
711a3debe7 | ||
|
|
d09b0849e4 | ||
|
|
c639821dc3 | ||
|
|
d9b643d795 | ||
|
|
6b8b3be524 | ||
|
|
1db8283e98 | ||
|
|
5fce40ee71 | ||
|
|
c64493ca77 | ||
|
|
5b942e6933 | ||
|
|
ffbb5934d7 | ||
|
|
05ccd1ecbf | ||
|
|
bae9df8214 | ||
|
|
f8a4f00d52 | ||
|
|
29377bbff9 | ||
|
|
f68d0c2a0f | ||
|
|
3901c49af9 | ||
|
|
ae0ceb61a1 | ||
|
|
fb1c825fbc | ||
|
|
e36b0249b9 | ||
|
|
34fa2d7ad6 | ||
|
|
3aefea9f04 | ||
|
|
15231a9128 | ||
|
|
25e3f72934 | ||
|
|
523d819575 | ||
|
|
5da47b3f19 | ||
|
|
b14e4237cc | ||
|
|
3e7685193f | ||
|
|
8558000c22 | ||
|
|
6486e5f908 | ||
|
|
9244a95472 | ||
|
|
bd237ef257 | ||
|
|
bc06e10146 | ||
|
|
05eadac935 | ||
|
|
5a83b12c66 | ||
|
|
71576fd7db | ||
|
|
63a1d4145a | ||
|
|
1049525e50 | ||
|
|
bd32510837 | ||
|
|
f0b40eea1e | ||
|
|
ed790ab65d | ||
|
|
46e23b28fd | ||
|
|
bc904a7afa | ||
|
|
8ccc8dfaf6 | ||
|
|
55b32c828c | ||
|
|
25dfd8ad40 | ||
|
|
2a5311493f | ||
|
|
57ac28cfbd | ||
|
|
fb02043e6e | ||
|
|
30d7cc89fa | ||
|
|
d14b5eb46b | ||
|
|
8eb60388fd | ||
|
|
633f1bd7cf | ||
|
|
933b5f1606 | ||
|
|
4c7b8209b9 | ||
|
|
802ac647e3 | ||
|
|
c83bded763 | ||
|
|
03246d425d | ||
|
|
7585e3d1d6 | ||
|
|
bc0a1d9bae | ||
|
|
a53dc3a8c1 | ||
|
|
bc1430c984 | ||
|
|
676d949972 | ||
|
|
e4bd7c4e30 | ||
|
|
50cdda7c63 | ||
|
|
d0af046e59 | ||
|
|
5811f3ead1 | ||
|
|
d7d3c9ea53 | ||
|
|
a02cc1de34 | ||
|
|
94783f44f9 | ||
|
|
2184c7c06b | ||
|
|
928bf0d9c7 | ||
|
|
0d02b2afcf | ||
|
|
1e76f3f744 | ||
|
|
7c3a0a292f | ||
|
|
c5ca0976a5 | ||
|
|
be7b3d5a12 | ||
|
|
d3f498f5b1 | ||
|
|
55941a1206 | ||
|
|
6304a085c0 | ||
|
|
7c708de937 | ||
|
|
5d51e5d393 | ||
|
|
464717dec5 | ||
|
|
7a06018c3e | ||
|
|
b8c76ff633 | ||
|
|
1ecc0bfe07 | ||
|
|
600d2d23c8 | ||
|
|
bf3211fd01 | ||
|
|
5fd0d9406d | ||
|
|
c78a6bea91 | ||
|
|
be9f33312b | ||
|
|
f23575302b | ||
|
|
0042ab2f28 | ||
|
|
53a63775fe | ||
|
|
461b1b26f3 | ||
|
|
f13d7d6845 | ||
|
|
e4d09b5404 | ||
|
|
415415273c | ||
|
|
d969190ca5 | ||
|
|
a180576c0a | ||
|
|
1382bb3c6b | ||
|
|
7fdaa0f0c7 | ||
|
|
ba750547a2 | ||
|
|
af8369ae1b | ||
|
|
829a73c58d | ||
|
|
a2691df64e | ||
|
|
6c8b175344 | ||
|
|
ad10053271 | ||
|
|
a508c55700 | ||
|
|
2712be3fa4 | ||
|
|
484326561f | ||
|
|
7ea96f88e8 | ||
|
|
de752b05a7 | ||
|
|
0b18344572 | ||
|
|
690474c01a | ||
|
|
777754f8a9 | ||
|
|
e4c5893c1e | ||
|
|
67aeae4a6d | ||
|
|
02008769e9 | ||
|
|
65dd9f51cf | ||
|
|
a224c53200 | ||
|
|
66f07c0d1c | ||
|
|
d5287026f8 | ||
|
|
6b80eb2c16 | ||
|
|
b54b981740 | ||
|
|
895032fe6a | ||
|
|
531c385aae | ||
|
|
d0a3ab68f3 | ||
|
|
b5038c5745 | ||
|
|
6877485c90 | ||
|
|
95c1b7f7a6 | ||
|
|
3fcf286516 | ||
|
|
e2cdb01a28 | ||
|
|
08e729673f | ||
|
|
c0bf09131f | ||
|
|
19042e0d37 | ||
|
|
4b3b4e00ff | ||
|
|
e3ffe8fe6b | ||
|
|
d91578ab80 | ||
|
|
873dcc373f | ||
|
|
1289c3f1a2 | ||
|
|
c37218fdc5 | ||
|
|
dad21a52b2 | ||
|
|
97a65d760f | ||
|
|
63e92197f5 | ||
|
|
fd534bf629 | ||
|
|
64d83bfc4d | ||
|
|
83065a798e | ||
|
|
b661b3ee40 | ||
|
|
b528903aa8 | ||
|
|
38215c2a88 | ||
|
|
4c3916a93d | ||
|
|
677f31ffa8 | ||
|
|
5848a46149 | ||
|
|
6b6cdf07fb | ||
|
|
8432dab142 | ||
|
|
0b0263a605 | ||
|
|
51dd55c5b9 | ||
|
|
17ea2cd510 | ||
|
|
325c427219 | ||
|
|
a3e87d893a | ||
|
|
480a629349 | ||
|
|
03fac6f285 | ||
|
|
2108a282c8 | ||
|
|
404e7c1f37 | ||
|
|
47916f823f | ||
|
|
ab4add1954 | ||
|
|
10fff31dca | ||
|
|
7fb85092ce | ||
|
|
7584e96759 | ||
|
|
f8ab64d71e | ||
|
|
ccb4c77d1f | ||
|
|
5ef1510fc7 | ||
|
|
1afd712ff4 | ||
|
|
699110258b | ||
|
|
3fb1df9bb3 | ||
|
|
14c03ead31 | ||
|
|
4480c2f0f0 | ||
|
|
b3ac5d8f41 | ||
|
|
6fb74c88cd | ||
|
|
38fd028a9f | ||
|
|
4e84310d63 | ||
|
|
66440f9e4c | ||
|
|
b8457ebece | ||
|
|
668fd1c7c0 | ||
|
|
aff934c814 | ||
|
|
6bb04f6cc6 | ||
|
|
0e815439b3 | ||
|
|
c7b01c37af | ||
|
|
ac8f3c811f | ||
|
|
1b820ea85c | ||
|
|
ec7b91bb68 | ||
|
|
c773cde191 | ||
|
|
db1065a69e | ||
|
|
9fa4bad0b4 | ||
|
|
a52c4b542c | ||
|
|
283abf9a88 | ||
|
|
f691d1aafd | ||
|
|
5724c3bb0a | ||
|
|
06200c9d3c | ||
|
|
2412658e51 | ||
|
|
03fa3e2269 | ||
|
|
11fbe7d5c9 | ||
|
|
799c1f08de | ||
|
|
6b66458bfd | ||
|
|
3e0a5bac6a | ||
|
|
2c2023df03 | ||
|
|
7306d379a5 | ||
|
|
e15c61d862 | ||
|
|
1eba17f048 | ||
|
|
00e57c8a55 | ||
|
|
2e74e64e22 | ||
|
|
fc5d623465 | ||
|
|
3b399b899c | ||
|
|
01d69c89aa | ||
|
|
a2801e40a2 | ||
|
|
abd4f0b923 |
@@ -33,7 +33,6 @@ Layout/LineLength:
|
||||
- app/controllers/admin/inventory_items_controller.rb
|
||||
- app/controllers/admin/manager_invitations_controller.rb
|
||||
- app/controllers/admin/product_import_controller.rb
|
||||
- app/controllers/admin/proxy_orders_controller.rb
|
||||
- app/controllers/admin/schedules_controller.rb
|
||||
- app/controllers/admin/subscriptions_controller.rb
|
||||
- app/controllers/admin/variant_overrides_controller.rb
|
||||
@@ -98,7 +97,6 @@ Layout/LineLength:
|
||||
- app/services/embedded_page_service.rb
|
||||
- app/services/order_cycle_form.rb
|
||||
- app/services/order_factory.rb
|
||||
- app/services/subscriptions_count.rb
|
||||
- app/services/variants_stock_levels.rb
|
||||
- engines/web/app/helpers/web/cookies_policy_helper.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
@@ -239,10 +237,7 @@ Layout/LineLength:
|
||||
- spec/lib/open_food_network/packing_report_spec.rb
|
||||
- spec/lib/open_food_network/permissions_spec.rb
|
||||
- spec/lib/open_food_network/products_and_inventory_report_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
|
||||
- spec/lib/open_food_network/subscription_summarizer_spec.rb
|
||||
- spec/lib/open_food_network/tag_rule_applicator_spec.rb
|
||||
- spec/lib/open_food_network/users_and_enterprises_report_spec.rb
|
||||
- spec/lib/open_food_network/xero_invoices_report_spec.rb
|
||||
@@ -317,10 +312,6 @@ Layout/LineLength:
|
||||
- spec/services/permissions/order_spec.rb
|
||||
- spec/services/product_tag_rules_filterer_spec.rb
|
||||
- spec/services/products_renderer_spec.rb
|
||||
- spec/services/subscription_estimator_spec.rb
|
||||
- spec/services/subscription_form_spec.rb
|
||||
- spec/services/subscription_validator_spec.rb
|
||||
- spec/services/subscription_variants_service_spec.rb
|
||||
- spec/spec_helper.rb
|
||||
- spec/support/cancan_helper.rb
|
||||
- spec/support/delayed_job_helper.rb
|
||||
@@ -386,7 +377,6 @@ Metrics/AbcSize:
|
||||
- app/helpers/spree/admin/base_helper.rb
|
||||
- app/helpers/spree/admin/zones_helper.rb
|
||||
- app/helpers/spree/orders_helper.rb
|
||||
- app/jobs/subscription_placement_job.rb
|
||||
- app/mailers/producer_mailer.rb
|
||||
- app/models/calculator/flat_percent_per_item.rb
|
||||
- app/models/column_preference.rb
|
||||
@@ -412,7 +402,9 @@ Metrics/AbcSize:
|
||||
- app/services/cart_service.rb
|
||||
- app/services/create_order_cycle.rb
|
||||
- app/services/order_syncer.rb
|
||||
- app/services/subscription_validator.rb
|
||||
- engines/order_management/app/services/order_management/subscriptions/validator.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_decorator.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/customers_report.rb
|
||||
@@ -506,6 +498,7 @@ Metrics/CyclomaticComplexity:
|
||||
- app/models/spree/product_decorator.rb
|
||||
- app/models/variant_override_set.rb
|
||||
- app/services/cart_service.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/enterprise_issue_validator.rb
|
||||
@@ -531,6 +524,7 @@ Metrics/PerceivedComplexity:
|
||||
- app/models/spree/ability_decorator.rb
|
||||
- app/models/spree/order_decorator.rb
|
||||
- app/models/spree/product_decorator.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/enterprise_issue_validator.rb
|
||||
@@ -600,6 +594,7 @@ Metrics/MethodLength:
|
||||
- app/serializers/api/cached_enterprise_serializer.rb
|
||||
- app/services/order_cycle_form.rb
|
||||
- engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/discourse/single_sign_on.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/column_preference_defaults.rb
|
||||
@@ -663,6 +658,7 @@ Metrics/ClassLength:
|
||||
- app/serializers/api/enterprise_shopfront_serializer.rb
|
||||
- app/services/cart_service.rb
|
||||
- engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb
|
||||
- lib/active_merchant/billing/gateways/stripe_payment_intents.rb
|
||||
- lib/open_food_network/bulk_coop_report.rb
|
||||
- lib/open_food_network/enterprise_fee_calculator.rb
|
||||
- lib/open_food_network/order_cycle_form_applicator.rb
|
||||
@@ -681,6 +677,13 @@ Metrics/ModuleLength:
|
||||
- app/helpers/injection_helper.rb
|
||||
- app/helpers/spree/admin/navigation_helper.rb
|
||||
- app/helpers/spree/admin/base_helper.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/estimator_spec.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/form_spec.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/proxy_order_syncer_spec.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/payment_setup_spec.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/summarizer_spec.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb
|
||||
- engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb
|
||||
- lib/open_food_network/column_preference_defaults.rb
|
||||
- spec/controllers/admin/enterprises_controller_spec.rb
|
||||
- spec/controllers/admin/order_cycles_controller_spec.rb
|
||||
@@ -696,9 +699,7 @@ Metrics/ModuleLength:
|
||||
- spec/lib/open_food_network/order_grouper_spec.rb
|
||||
- spec/lib/open_food_network/permissions_spec.rb
|
||||
- spec/lib/open_food_network/products_and_inventory_report_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
|
||||
- spec/lib/open_food_network/tag_rule_applicator_spec.rb
|
||||
- spec/lib/open_food_network/users_and_enterprises_report_spec.rb
|
||||
- spec/models/spree/ability_spec.rb
|
||||
|
||||
@@ -117,6 +117,15 @@ Style/FormatString:
|
||||
Enabled: false
|
||||
StyleGuide: http://relaxed.ruby.style/#styleformatstring
|
||||
|
||||
Style/HashEachMethods:
|
||||
Enabled: false
|
||||
|
||||
Style/HashTransformKeys:
|
||||
Enabled: false
|
||||
|
||||
Style/HashTransformValues:
|
||||
Enabled: false
|
||||
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
StyleGuide: http://relaxed.ruby.style/#styleifunlessmodifier
|
||||
|
||||
@@ -71,7 +71,6 @@ Lint/DuplicateHashKey:
|
||||
Lint/DuplicateMethods:
|
||||
Exclude:
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
- 'lib/open_food_network/subscription_summary.rb'
|
||||
|
||||
# Offense count: 10
|
||||
Lint/IneffectiveAccessModifier:
|
||||
@@ -159,7 +158,7 @@ Naming/MethodParameterName:
|
||||
Exclude:
|
||||
- 'app/helpers/spree/admin/base_helper_decorator.rb'
|
||||
- 'app/helpers/spree/base_helper_decorator.rb'
|
||||
- 'app/services/subscription_validator.rb'
|
||||
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
|
||||
- 'lib/open_food_network/reports/bulk_coop_report.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'spec/lib/open_food_network/reports/report_spec.rb'
|
||||
@@ -681,7 +680,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'app/models/product_import/entry_validator.rb'
|
||||
- 'app/models/product_import/inventory_reset_strategy.rb'
|
||||
- 'app/models/product_import/product_importer.rb'
|
||||
- 'app/models/product_import/products_reset_strategy.rb'
|
||||
- 'app/models/product_import/reset_absent.rb'
|
||||
- 'app/models/product_import/settings.rb'
|
||||
- 'app/models/product_import/spreadsheet_data.rb'
|
||||
@@ -850,11 +848,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'app/services/reset_order_service.rb'
|
||||
- 'app/services/restart_checkout.rb'
|
||||
- 'app/services/search_orders.rb'
|
||||
- 'app/services/subscription_estimator.rb'
|
||||
- 'app/services/subscription_form.rb'
|
||||
- 'app/services/subscription_validator.rb'
|
||||
- 'app/services/subscription_variants_service.rb'
|
||||
- 'app/services/subscriptions_count.rb'
|
||||
- 'app/services/tax_rate_finder.rb'
|
||||
- 'app/services/upload_sanitizer.rb'
|
||||
- 'app/services/variant_deleter.rb'
|
||||
@@ -893,6 +886,7 @@ Style/FrozenStringLiteralComment:
|
||||
- 'engines/order_management/lib/order_management/engine.rb'
|
||||
- 'engines/order_management/lib/order_management/version.rb'
|
||||
- 'engines/order_management/order_management.gemspec'
|
||||
- 'engines/order_management/spec/performance/order_management/subscriptions/proxy_order_syncer_spec.rb'
|
||||
- 'engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/authorizer_spec.rb'
|
||||
- 'engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/parameters_spec.rb'
|
||||
- 'engines/order_management/spec/services/order_management/reports/enterprise_fee_summary/permissions_spec.rb'
|
||||
@@ -950,7 +944,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'lib/open_food_network/products_and_inventory_report.rb'
|
||||
- 'lib/open_food_network/products_and_inventory_report_base.rb'
|
||||
- 'lib/open_food_network/property_merge.rb'
|
||||
- 'lib/open_food_network/proxy_order_syncer.rb'
|
||||
- 'lib/open_food_network/rack_request_blocker.rb'
|
||||
- 'lib/open_food_network/referer_parser.rb'
|
||||
- 'lib/open_food_network/reports/bulk_coop_allocation_report.rb'
|
||||
@@ -967,8 +960,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'lib/open_food_network/scope_variants_for_search.rb'
|
||||
- 'lib/open_food_network/spree_api_key_loader.rb'
|
||||
- 'lib/open_food_network/subscription_payment_updater.rb'
|
||||
- 'lib/open_food_network/subscription_summarizer.rb'
|
||||
- 'lib/open_food_network/subscription_summary.rb'
|
||||
- 'lib/open_food_network/tag_rule_applicator.rb'
|
||||
- 'lib/open_food_network/user_balance_calculator.rb'
|
||||
- 'lib/open_food_network/users_and_enterprises_report.rb'
|
||||
@@ -1210,7 +1201,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'spec/lib/open_food_network/permissions_spec.rb'
|
||||
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/property_merge_spec.rb'
|
||||
- 'spec/lib/open_food_network/proxy_order_syncer_spec.rb'
|
||||
- 'spec/lib/open_food_network/referer_parser_spec.rb'
|
||||
- 'spec/lib/open_food_network/reports/report_spec.rb'
|
||||
- 'spec/lib/open_food_network/reports/row_spec.rb'
|
||||
@@ -1219,8 +1209,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'spec/lib/open_food_network/scope_variant_to_hub_spec.rb'
|
||||
- 'spec/lib/open_food_network/scope_variants_to_search_spec.rb'
|
||||
- 'spec/lib/open_food_network/subscription_payment_updater_spec.rb'
|
||||
- 'spec/lib/open_food_network/subscription_summarizer_spec.rb'
|
||||
- 'spec/lib/open_food_network/subscription_summary_spec.rb'
|
||||
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/user_balance_calculator_spec.rb'
|
||||
- 'spec/lib/open_food_network/users_and_enterprises_report_spec.rb'
|
||||
@@ -1257,7 +1245,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'spec/models/producer_property_spec.rb'
|
||||
- 'spec/models/product_import/entry_processor_spec.rb'
|
||||
- 'spec/models/product_import/inventory_reset_strategy_spec.rb'
|
||||
- 'spec/models/product_import/products_reset_strategy_spec.rb'
|
||||
- 'spec/models/product_import/reset_absent_spec.rb'
|
||||
- 'spec/models/product_import/settings_spec.rb'
|
||||
- 'spec/models/product_importer_spec.rb'
|
||||
@@ -1306,7 +1293,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'spec/models/variant_override_spec.rb'
|
||||
- 'spec/performance/injection_helper_spec.rb'
|
||||
- 'spec/performance/orders_controller_spec.rb'
|
||||
- 'spec/performance/proxy_order_syncer_spec.rb'
|
||||
- 'spec/performance/shop_controller_spec.rb'
|
||||
- 'spec/requests/checkout/failed_checkout_spec.rb'
|
||||
- 'spec/requests/checkout/paypal_spec.rb'
|
||||
@@ -1357,11 +1343,6 @@ Style/FrozenStringLiteralComment:
|
||||
- 'spec/services/reset_order_service_spec.rb'
|
||||
- 'spec/services/restart_checkout_spec.rb'
|
||||
- 'spec/services/search_orders_spec.rb'
|
||||
- 'spec/services/subscription_estimator_spec.rb'
|
||||
- 'spec/services/subscription_form_spec.rb'
|
||||
- 'spec/services/subscription_validator_spec.rb'
|
||||
- 'spec/services/subscription_variants_service_spec.rb'
|
||||
- 'spec/services/subscriptions_count_spec.rb'
|
||||
- 'spec/services/tax_rate_finder_spec.rb'
|
||||
- 'spec/services/upload_sanitizer_spec.rb'
|
||||
- 'spec/services/variants_stock_levels_spec.rb'
|
||||
@@ -1559,7 +1540,6 @@ Style/Send:
|
||||
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/sales_tax_report_spec.rb'
|
||||
- 'spec/lib/open_food_network/subscription_payment_updater_spec.rb'
|
||||
- 'spec/lib/open_food_network/subscription_summarizer_spec.rb'
|
||||
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
|
||||
- 'spec/lib/open_food_network/xero_invoices_report_spec.rb'
|
||||
- 'spec/lib/stripe/webhook_handler_spec.rb'
|
||||
|
||||
11
DOCKER.md
11
DOCKER.md
@@ -35,13 +35,20 @@ Download the Docker images and build the containers:
|
||||
$ docker-compose build
|
||||
```
|
||||
|
||||
Run the app with all the required containers:
|
||||
Setup the database and seed it with sample data:
|
||||
```sh
|
||||
$ docker-compose run web bundle exec rake db:reset
|
||||
$ docker-compose run web bundle exec rake db:test:prepare
|
||||
$ docker-compose run web bundle exec rake ofn:sample_data
|
||||
```
|
||||
|
||||
Finally, run the app with all the required containers:
|
||||
|
||||
```sh
|
||||
$ docker-compose up
|
||||
```
|
||||
|
||||
This command will setup the database and seed it with sample data. The default admin user is 'ofn@example.com' with 'ofn123' password.
|
||||
The default admin user is 'ofn@example.com' with 'ofn123' password.
|
||||
Check the app in the browser at `http://localhost:3000`.
|
||||
|
||||
You will then get the trace of the containers in the terminal. You can stop the containers using Ctrl-C in the terminal.
|
||||
|
||||
@@ -12,7 +12,7 @@ ENV BUNDLE_PATH /bundles
|
||||
WORKDIR /usr/src/app
|
||||
COPY .ruby-version .
|
||||
|
||||
# Rbenv & Ruby part
|
||||
# Install Rbenv & Ruby
|
||||
RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
|
||||
git clone https://github.com/rbenv/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build && \
|
||||
${RBENV_ROOT}/plugins/ruby-build/install.sh && \
|
||||
@@ -21,7 +21,7 @@ RUN git clone https://github.com/rbenv/rbenv.git ${RBENV_ROOT} && \
|
||||
rbenv global $(cat .ruby-version) && \
|
||||
gem install bundler --version=1.17.2
|
||||
|
||||
# Postgres
|
||||
# Install Postgres
|
||||
RUN sh -c "echo 'deb https://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main' > /etc/apt/sources.list.d/pgdg.list" && \
|
||||
wget --quiet -O - https://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | apt-key add - && \
|
||||
apt-get update && \
|
||||
@@ -38,4 +38,6 @@ RUN wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.z
|
||||
unzip chromedriver_linux64.zip -d /usr/bin && \
|
||||
chmod u+x /usr/bin/chromedriver
|
||||
|
||||
# Copy code and install app dependencies
|
||||
COPY . /usr/src/app/
|
||||
RUN bundle install
|
||||
|
||||
11
Gemfile
11
Gemfile
@@ -12,6 +12,7 @@ gem "activerecord-import"
|
||||
# Patched version. See http://rubysec.com/advisories/CVE-2015-5312/.
|
||||
gem 'nokogiri', '>= 1.6.7.1'
|
||||
|
||||
gem "catalog", path: "./engines/catalog"
|
||||
gem "order_management", path: "./engines/order_management"
|
||||
gem 'web', path: './engines/web'
|
||||
|
||||
@@ -44,10 +45,6 @@ gem 'daemons'
|
||||
gem 'delayed_job_active_record'
|
||||
gem 'delayed_job_web'
|
||||
|
||||
# Fix bug in simple_form preventing collection_check_boxes usage within form_for block
|
||||
# When merged, revert to upstream gem
|
||||
gem 'simple_form', github: 'RohanM/simple_form'
|
||||
|
||||
# Spree's default pagination gem (locked to the current version used by Spree)
|
||||
# We use it's methods in OFN code as well, so this is a direct dependency
|
||||
gem 'kaminari', '~> 0.14.1'
|
||||
@@ -111,10 +108,9 @@ gem 'momentjs-rails'
|
||||
gem 'turbo-sprockets-rails3'
|
||||
|
||||
gem "foundation-rails"
|
||||
gem 'foundation_rails_helper', github: 'willrjmarshall/foundation_rails_helper', branch: "rails3"
|
||||
|
||||
gem 'jquery-migrate-rails'
|
||||
gem 'jquery-rails', '3.0.4'
|
||||
gem 'jquery-rails', '3.1.5'
|
||||
gem 'jquery-ui-rails', '~> 4.0.0'
|
||||
gem 'select2-rails', '~> 3.4.7'
|
||||
|
||||
@@ -122,6 +118,7 @@ gem 'ofn-qz', github: 'openfoodfoundation/ofn-qz', ref: '60da2ae4c44cbb4c8d602f5
|
||||
|
||||
group :production, :staging do
|
||||
gem 'ddtrace'
|
||||
gem 'unicorn-worker-killer'
|
||||
end
|
||||
|
||||
group :test, :development do
|
||||
@@ -166,5 +163,5 @@ group :development do
|
||||
# greater than 1.0.9, so we just required the latest available version here.
|
||||
gem 'eventmachine', '>= 1.2.3'
|
||||
|
||||
gem 'rack-mini-profiler', '< 2.0.0'
|
||||
gem 'rack-mini-profiler', '< 3.0.0'
|
||||
end
|
||||
|
||||
83
Gemfile.lock
83
Gemfile.lock
@@ -1,11 +1,3 @@
|
||||
GIT
|
||||
remote: https://github.com/RohanM/simple_form.git
|
||||
revision: 45f08a213b40f3d4bda5f5398db841137587160a
|
||||
specs:
|
||||
simple_form (2.0.2)
|
||||
actionpack (~> 3.0)
|
||||
activemodel (~> 3.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/jeremydurham/custom-err-msg.git
|
||||
revision: 3a8ec9dddc7a5b0aab7c69a6060596de300c68f4
|
||||
@@ -65,15 +57,10 @@ GIT
|
||||
rails-i18n
|
||||
spree_core (>= 1.1)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/willrjmarshall/foundation_rails_helper.git
|
||||
revision: 4d5d53fdc4b1fb71e66524d298c5c635de82cfbb
|
||||
branch: rails3
|
||||
PATH
|
||||
remote: engines/catalog
|
||||
specs:
|
||||
foundation_rails_helper (0.4)
|
||||
actionpack (>= 3.0)
|
||||
activemodel (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
catalog (0.0.1)
|
||||
|
||||
PATH
|
||||
remote: engines/order_management
|
||||
@@ -168,7 +155,7 @@ GEM
|
||||
xpath (>= 2.0, < 4.0)
|
||||
childprocess (3.0.0)
|
||||
chronic (0.10.2)
|
||||
chunky_png (1.3.10)
|
||||
chunky_png (1.3.11)
|
||||
climate_control (0.2.0)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
@@ -195,7 +182,7 @@ GEM
|
||||
sass (>= 3.3.0, < 3.5)
|
||||
compass-import-once (1.0.5)
|
||||
sass (>= 3.2, < 3.5)
|
||||
compass-rails (3.1.0)
|
||||
compass-rails (4.0.0)
|
||||
compass (~> 1.0.0)
|
||||
sass-rails (< 5.1)
|
||||
sprockets (< 4.0)
|
||||
@@ -211,7 +198,7 @@ GEM
|
||||
activerecord (>= 3.2.0, < 5.0)
|
||||
fog (~> 1.0)
|
||||
rails (>= 3.2.0, < 5.0)
|
||||
ddtrace (0.32.0)
|
||||
ddtrace (0.34.1)
|
||||
msgpack
|
||||
debugger-linecache (1.2.0)
|
||||
deface (1.0.2)
|
||||
@@ -252,7 +239,7 @@ GEM
|
||||
faraday (1.0.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffaker (1.22.1)
|
||||
ffi (1.11.3)
|
||||
ffi (1.12.2)
|
||||
figaro (1.1.1)
|
||||
thor (~> 0.14)
|
||||
fission (0.5.0)
|
||||
@@ -416,10 +403,12 @@ GEM
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
geocoder (1.1.8)
|
||||
get_process_mem (0.2.5)
|
||||
ffi (~> 1.0)
|
||||
gmaps4rails (1.5.6)
|
||||
haml (4.0.7)
|
||||
tilt
|
||||
hashdiff (1.0.0)
|
||||
hashdiff (1.0.1)
|
||||
highline (1.6.18)
|
||||
hike (1.2.3)
|
||||
httparty (0.16.2)
|
||||
@@ -433,7 +422,7 @@ GEM
|
||||
jaro_winkler (1.5.4)
|
||||
journey (1.0.4)
|
||||
jquery-migrate-rails (1.2.1)
|
||||
jquery-rails (3.0.4)
|
||||
jquery-rails (3.1.5)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (4.0.5)
|
||||
@@ -467,7 +456,7 @@ GEM
|
||||
railties (>= 3.1)
|
||||
money (5.1.1)
|
||||
i18n (~> 0.6.0)
|
||||
msgpack (1.3.1)
|
||||
msgpack (1.3.3)
|
||||
multi_json (1.14.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.1.1)
|
||||
@@ -480,7 +469,7 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
oj (3.10.2)
|
||||
oj (3.10.6)
|
||||
orm_adapter (0.5.0)
|
||||
paper_trail (5.2.3)
|
||||
activerecord (>= 3.0, < 6.0)
|
||||
@@ -494,7 +483,7 @@ GEM
|
||||
parallel (1.19.1)
|
||||
paranoia (1.3.4)
|
||||
activerecord (~> 3.1)
|
||||
parser (2.7.0.2)
|
||||
parser (2.7.0.5)
|
||||
ast (~> 2.4.0)
|
||||
paypal-sdk-core (0.2.10)
|
||||
multi_json (~> 1.0)
|
||||
@@ -516,9 +505,9 @@ GEM
|
||||
rabl (0.8.4)
|
||||
activesupport (>= 2.3.14)
|
||||
rack (1.4.7)
|
||||
rack-cache (1.9.0)
|
||||
rack-cache (1.11.0)
|
||||
rack (>= 0.4)
|
||||
rack-mini-profiler (1.1.6)
|
||||
rack-mini-profiler (2.0.1)
|
||||
rack (>= 1.2.0)
|
||||
rack-protection (1.5.5)
|
||||
rack
|
||||
@@ -548,14 +537,14 @@ GEM
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.1)
|
||||
rake (13.0.0)
|
||||
rake (13.0.1)
|
||||
ransack (0.7.2)
|
||||
actionpack (~> 3.0)
|
||||
activerecord (~> 3.0)
|
||||
polyamorous (~> 0.5.0)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rbvmomi (1.13.0)
|
||||
builder (~> 3.0)
|
||||
json (>= 1.8)
|
||||
@@ -580,15 +569,15 @@ GEM
|
||||
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-core (3.9.1)
|
||||
rspec-support (~> 3.9.1)
|
||||
rspec-expectations (3.9.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-mocks (3.9.0)
|
||||
rspec-mocks (3.9.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-rails (3.9.0)
|
||||
rspec-rails (3.9.1)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
@@ -598,16 +587,17 @@ GEM
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-retry (0.6.2)
|
||||
rspec-core (> 3.3)
|
||||
rspec-support (3.9.0)
|
||||
rubocop (0.80.0)
|
||||
rspec-support (3.9.2)
|
||||
rubocop (0.81.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.0.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
rexml
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 1.7)
|
||||
rubocop-rails (2.4.2)
|
||||
unicode-display_width (>= 1.4.0, < 2.0)
|
||||
rubocop-rails (2.5.1)
|
||||
activesupport
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 0.72.0)
|
||||
ruby-progressbar (1.10.1)
|
||||
@@ -665,13 +655,16 @@ GEM
|
||||
tzinfo (0.3.56)
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (1.6.1)
|
||||
unicorn (5.5.3)
|
||||
unicode-display_width (1.7.0)
|
||||
unicorn (5.5.4)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
unicorn-rails (2.2.1)
|
||||
rack
|
||||
unicorn
|
||||
unicorn-worker-killer (0.4.4)
|
||||
get_process_mem (~> 0)
|
||||
unicorn (>= 4, < 6)
|
||||
uuidtools (2.1.5)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
@@ -679,7 +672,7 @@ GEM
|
||||
nokogiri (~> 1.6)
|
||||
rubyzip (>= 1.3.0)
|
||||
selenium-webdriver (>= 3.0, < 4.0)
|
||||
webmock (3.8.2)
|
||||
webmock (3.8.3)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
@@ -711,6 +704,7 @@ DEPENDENCIES
|
||||
bugsnag
|
||||
byebug (~> 9.0.0)
|
||||
capybara (>= 2.18.0)
|
||||
catalog!
|
||||
coffee-rails (~> 3.2.1)
|
||||
combine_pdf
|
||||
compass-rails
|
||||
@@ -733,7 +727,6 @@ DEPENDENCIES
|
||||
foreigner
|
||||
foundation-icons-sass-rails
|
||||
foundation-rails
|
||||
foundation_rails_helper!
|
||||
fuubar (~> 2.5.0)
|
||||
geocoder
|
||||
gmaps4rails
|
||||
@@ -742,7 +735,7 @@ DEPENDENCIES
|
||||
i18n-js (~> 3.6.0)
|
||||
immigrant
|
||||
jquery-migrate-rails
|
||||
jquery-rails (= 3.0.4)
|
||||
jquery-rails (= 3.1.5)
|
||||
jquery-ui-rails (~> 4.0.0)
|
||||
json_spec (~> 1.1.4)
|
||||
jwt (~> 2.2)
|
||||
@@ -762,7 +755,7 @@ DEPENDENCIES
|
||||
pg (~> 0.21.0)
|
||||
pry-byebug (>= 3.4.3)
|
||||
rabl
|
||||
rack-mini-profiler (< 2.0.0)
|
||||
rack-mini-profiler (< 3.0.0)
|
||||
rack-rewrite
|
||||
rack-ssl
|
||||
rails (~> 3.2.22)
|
||||
@@ -780,7 +773,6 @@ DEPENDENCIES
|
||||
select2-rails (~> 3.4.7)
|
||||
selenium-webdriver
|
||||
shoulda-matchers
|
||||
simple_form!
|
||||
simplecov
|
||||
spinjs-rails
|
||||
spree_core!
|
||||
@@ -796,6 +788,7 @@ DEPENDENCIES
|
||||
uglifier (>= 1.0.3)
|
||||
unicorn
|
||||
unicorn-rails
|
||||
unicorn-worker-killer
|
||||
web!
|
||||
webdrivers
|
||||
webmock
|
||||
|
||||
@@ -35,7 +35,7 @@ We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. B
|
||||
Copyright (c) 2012 - 2020 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/enQtNzY3NDEwNzM2MDM0LWFmNGRhNDUwYzNmNWNkYmFkMzgxNDg1OTg1ODNjNWY4Y2FhNDIwNmE4ZWI0OThiMGNmZjFkODczNGZiYTJmNWI
|
||||
[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/zt-9sjkjdlu-r02kUMP1zbrTgUhZhYPF~A
|
||||
[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
|
||||
|
||||
23
app/assets/images/black-caret.svg
Normal file
23
app/assets/images/black-caret.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg4"
|
||||
xml:space="preserve"
|
||||
enable-background="new 0 0 6 3"
|
||||
viewBox="0 0 6 3"
|
||||
height="3px"
|
||||
width="24px"
|
||||
y="0px"
|
||||
x="12px"
|
||||
version="1.1"><metadata
|
||||
id="metadata10"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs8" /><polygon
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
id="polygon2"
|
||||
points="5.992,0 2.992,3 -0.008,0 " /></svg>
|
||||
|
After Width: | Height: | Size: 832 B |
23
app/assets/images/white-caret.svg
Normal file
23
app/assets/images/white-caret.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg4"
|
||||
xml:space="preserve"
|
||||
enable-background="new 0 0 6 3"
|
||||
viewBox="0 0 6 3"
|
||||
height="3px"
|
||||
width="24px"
|
||||
y="0px"
|
||||
x="12px"
|
||||
version="1.1"><metadata
|
||||
id="metadata10"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs8" /><polygon
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
id="polygon2"
|
||||
points="5.992,0 2.992,3 -0.008,0 " /></svg>
|
||||
|
After Width: | Height: | Size: 832 B |
@@ -20,7 +20,6 @@
|
||||
//= require angular
|
||||
//= require angular-resource
|
||||
//= require angular-animate
|
||||
//= require angular-sanitize
|
||||
//= require angularjs-file-upload
|
||||
//= require ../shared/ng-infinite-scroll.min.js
|
||||
//= require ../shared/ng-tags-input.min.js
|
||||
@@ -67,6 +66,8 @@
|
||||
|
||||
// text, dates and translations
|
||||
//= require textAngular-rangy.min.js
|
||||
// This replaces angular-sanitize. We should include only one.
|
||||
// https://github.com/textAngular/textAngular#where-to-get-it
|
||||
//= require textAngular-sanitize.min.js
|
||||
//= require textAngular.min.js
|
||||
//= require i18n/translations
|
||||
|
||||
@@ -2,19 +2,24 @@ angular.module("admin.indexUtils").factory "PagedFetcher", (dataFetcher) ->
|
||||
new class PagedFetcher
|
||||
# Given a URL like http://example.com/foo?page=::page::&per_page=20
|
||||
# And the response includes an attribute pages with the number of pages to fetch
|
||||
# Fetch each page async, and call the processData callback with the resulting data
|
||||
fetch: (url, processData, onLastPageComplete) ->
|
||||
dataFetcher(@urlForPage(url, 1)).then (data) =>
|
||||
processData data
|
||||
# Fetch each page async, and call the pageCallback callback with the resulting data
|
||||
# Developer note: this class should not be re-used!
|
||||
page: 1
|
||||
last_page: 1
|
||||
|
||||
if data.pages > 1
|
||||
for page in [2..data.pages]
|
||||
lastPromise = dataFetcher(@urlForPage(url, page)).then (data) ->
|
||||
processData data
|
||||
onLastPageComplete && lastPromise.then onLastPageComplete
|
||||
return
|
||||
else
|
||||
onLastPageComplete && onLastPageComplete()
|
||||
fetch: (url, pageCallback) ->
|
||||
@fetchPages(url, @page, pageCallback)
|
||||
|
||||
urlForPage: (url, page) ->
|
||||
url.replace("::page::", page)
|
||||
|
||||
fetchPages: (url, page, pageCallback) ->
|
||||
dataFetcher(@urlForPage(url, page)).then (data) =>
|
||||
@page++
|
||||
@last_page = data.pages
|
||||
|
||||
pageCallback(data) if pageCallback
|
||||
|
||||
if @page <= @last_page
|
||||
@fetchPages(url, @page, pageCallback)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, SortOptions, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, VariantUnitManager, RequestMonitor) ->
|
||||
$scope.initialized = false
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.filteredLineItems = []
|
||||
$scope.line_items = LineItems.all
|
||||
$scope.confirmDelete = true
|
||||
$scope.startDate = moment().startOf('day').subtract(7, 'days').format('YYYY-MM-DD')
|
||||
$scope.endDate = moment().startOf('day').format('YYYY-MM-DD')
|
||||
@@ -15,50 +15,77 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
$scope.confirmRefresh = ->
|
||||
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))
|
||||
|
||||
$scope.resetFilters = ->
|
||||
$scope.distributorFilter = ''
|
||||
$scope.supplierFilter = ''
|
||||
$scope.orderCycleFilter = ''
|
||||
$scope.quickSearch = ''
|
||||
|
||||
$scope.resetSelectFilters = ->
|
||||
$scope.distributorFilter = 0
|
||||
$scope.supplierFilter = 0
|
||||
$scope.orderCycleFilter = 0
|
||||
$scope.quickSearch = ""
|
||||
$scope.resetFilters()
|
||||
$scope.refreshData()
|
||||
|
||||
$scope.refreshData = ->
|
||||
unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == 0
|
||||
$scope.startDate = moment(OrderCycles.byID[$scope.orderCycleFilter].orders_open_at).format('YYYY-MM-DD')
|
||||
$scope.endDate = moment(OrderCycles.byID[$scope.orderCycleFilter].orders_close_at).startOf('day').format('YYYY-MM-DD')
|
||||
unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == ''
|
||||
$scope.setOrderCycleDateRange()
|
||||
|
||||
formatted_start_date = moment($scope.startDate).format()
|
||||
formatted_end_date = moment($scope.endDate).add(1,'day').format()
|
||||
$scope.formattedStartDate = moment($scope.startDate).format()
|
||||
$scope.formattedEndDate = moment($scope.endDate).add(1,'day').format()
|
||||
|
||||
return unless moment($scope.formattedStartDate).isValid() and moment($scope.formattedEndDate).isValid()
|
||||
|
||||
$scope.loadOrders()
|
||||
$scope.loadLineItems()
|
||||
|
||||
unless $scope.initialized
|
||||
$scope.loadAssociatedData()
|
||||
|
||||
$scope.dereferenceLoadedData()
|
||||
|
||||
$scope.setOrderCycleDateRange = ->
|
||||
start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at
|
||||
end_date = OrderCycles.byID[$scope.orderCycleFilter].orders_close_at
|
||||
format = "YYYY-MM-DD HH:mm:ss Z"
|
||||
$scope.startDate = moment(start_date, format).format('YYYY-MM-DD')
|
||||
$scope.endDate = moment(end_date, format).startOf('day').format('YYYY-MM-DD')
|
||||
|
||||
$scope.loadOrders = ->
|
||||
RequestMonitor.load $scope.orders = Orders.index(
|
||||
"q[state_not_eq]": "canceled",
|
||||
"q[completed_at_not_null]": "true",
|
||||
"q[completed_at_gteq]": formatted_start_date,
|
||||
"q[completed_at_lt]": formatted_end_date
|
||||
"q[distributor_id_eq]": $scope.distributorFilter,
|
||||
"q[order_cycle_id_eq]": $scope.orderCycleFilter,
|
||||
"q[completed_at_gteq]": $scope.formattedStartDate,
|
||||
"q[completed_at_lt]": $scope.formattedEndDate
|
||||
)
|
||||
|
||||
RequestMonitor.load $scope.lineItems = LineItems.index(
|
||||
"q[order][state_not_eq]": "canceled",
|
||||
"q[order][completed_at_not_null]": "true",
|
||||
"q[order][completed_at_gteq]": formatted_start_date,
|
||||
"q[order][completed_at_lt]": formatted_end_date
|
||||
$scope.loadLineItems = ->
|
||||
RequestMonitor.load LineItems.index(
|
||||
"q[order_state_not_eq]": "canceled",
|
||||
"q[order_completed_at_not_null]": "true",
|
||||
"q[order_distributor_id_eq]": $scope.distributorFilter,
|
||||
"q[variant_product_supplier_id_eq]": $scope.supplierFilter,
|
||||
"q[order_order_cycle_id_eq]": $scope.orderCycleFilter,
|
||||
"q[order_completed_at_gteq]": $scope.formattedStartDate,
|
||||
"q[order_completed_at_lt]": $scope.formattedEndDate
|
||||
)
|
||||
|
||||
unless $scope.initialized
|
||||
RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"])
|
||||
RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}")
|
||||
RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true")
|
||||
$scope.loadAssociatedData = ->
|
||||
RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"])
|
||||
RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}")
|
||||
RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true")
|
||||
|
||||
RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.lineItems.$promise]).then ->
|
||||
$scope.dereferenceLoadedData = ->
|
||||
RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.line_items.$promise]).then ->
|
||||
Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID
|
||||
Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID
|
||||
Dereferencer.dereferenceAttr $scope.lineItems, "supplier", Enterprises.byID
|
||||
Dereferencer.dereferenceAttr $scope.lineItems, "order", Orders.byID
|
||||
Dereferencer.dereferenceAttr $scope.line_items, "supplier", Enterprises.byID
|
||||
Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID
|
||||
$scope.bulk_order_form.$setPristine()
|
||||
StatusMessage.clear()
|
||||
|
||||
unless $scope.initialized
|
||||
$scope.initialized = true
|
||||
$timeout ->
|
||||
$scope.resetSelectFilters()
|
||||
|
||||
$scope.$watch 'bulk_order_form.$dirty', (newVal, oldVal) ->
|
||||
if newVal == true
|
||||
@@ -77,13 +104,12 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
|
||||
$scope.deleteLineItem = (lineItem) ->
|
||||
if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete
|
||||
LineItems.delete lineItem, =>
|
||||
$scope.lineItems.splice $scope.lineItems.indexOf(lineItem), 1
|
||||
LineItems.delete lineItem
|
||||
|
||||
$scope.deleteLineItems = (lineItems) ->
|
||||
$scope.deleteLineItems = (lineItemsToDelete) ->
|
||||
existingState = $scope.confirmDelete
|
||||
$scope.confirmDelete = false
|
||||
$scope.deleteLineItem lineItem for lineItem in lineItems when lineItem.checked
|
||||
$scope.deleteLineItem lineItem for lineItem in lineItemsToDelete when lineItem.checked
|
||||
$scope.confirmDelete = existingState
|
||||
|
||||
$scope.allBoxesChecked = ->
|
||||
@@ -154,4 +180,5 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
|
||||
lineItem.final_weight_volume = LineItems.pristineByID[lineItem.id].final_weight_volume * lineItem.quantity / LineItems.pristineByID[lineItem.id].quantity
|
||||
$scope.weightAdjustedPrice(lineItem)
|
||||
|
||||
$scope.resetFilters()
|
||||
$scope.refreshData()
|
||||
|
||||
@@ -21,7 +21,7 @@ angular.module('admin.payments').factory 'Payment', (AdminStripeElements, curren
|
||||
year: @form_data.card_year
|
||||
verification_value: @form_data.card_verification_value
|
||||
}
|
||||
when 'stripe'
|
||||
when 'stripe', 'stripe_sca'
|
||||
angular.extend munged_payment.payment, {
|
||||
source_attributes:
|
||||
gateway_payment_profile_id: @form_data.token
|
||||
@@ -35,6 +35,8 @@ angular.module('admin.payments').factory 'Payment', (AdminStripeElements, curren
|
||||
purchase: ->
|
||||
if @paymentMethodType() == 'stripe'
|
||||
AdminStripeElements.requestToken(@form_data, @submit)
|
||||
else if @paymentMethodType() == 'stripe_sca'
|
||||
AdminStripeElements.createPaymentMethod(@form_data, @submit)
|
||||
else
|
||||
@submit()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
|
||||
stripe: null
|
||||
card: null
|
||||
|
||||
# New Stripe Elements method
|
||||
# Create Token to be used with the Stripe Charges API
|
||||
requestToken: (secrets, submit) ->
|
||||
return unless @stripe? && @card?
|
||||
|
||||
@@ -14,15 +14,32 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
|
||||
@stripe.createToken(@card, cardData).then (response) =>
|
||||
if(response.error)
|
||||
StatusMessage.display 'error', response.error.message
|
||||
console.error(JSON.stringify(response.error))
|
||||
else
|
||||
secrets.token = response.token.id
|
||||
secrets.cc_type = @mapCC(response.token.card.brand)
|
||||
secrets.cc_type = @mapTokenApiCardBrand(response.token.card.brand)
|
||||
secrets.card = response.token.card
|
||||
submit()
|
||||
|
||||
# Maps the brand returned by Stripe to that required by activemerchant
|
||||
mapCC: (ccType) ->
|
||||
switch ccType
|
||||
# Create Payment Method to be used with the Stripe Payment Intents API
|
||||
createPaymentMethod: (secrets, submit) ->
|
||||
return unless @stripe? && @card?
|
||||
|
||||
cardData = @makeCardData(secrets)
|
||||
|
||||
@stripe.createPaymentMethod({ type: 'card', card: @card }, @card, cardData).then (response) =>
|
||||
if(response.error)
|
||||
StatusMessage.display 'error', response.error.message
|
||||
console.error(JSON.stringify(response.error))
|
||||
else
|
||||
secrets.token = response.paymentMethod.id
|
||||
secrets.cc_type = @mapPaymentMethodsApiCardBrand(response.paymentMethod.card.brand)
|
||||
secrets.card = response.paymentMethod.card
|
||||
submit()
|
||||
|
||||
# Maps the brand returned by Stripe's tokenAPI to that required by activemerchant
|
||||
mapTokenApiCardBrand: (cardBrand) ->
|
||||
switch cardBrand
|
||||
when 'MasterCard' then return 'master'
|
||||
when 'Visa' then return 'visa'
|
||||
when 'American Express' then return 'american_express'
|
||||
@@ -30,6 +47,14 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
|
||||
when 'JCB' then return 'jcb'
|
||||
when 'Diners Club' then return 'diners_club'
|
||||
|
||||
# Maps the brand returned by Stripe's paymentMethodsAPI to that required by activemerchant
|
||||
mapPaymentMethodsApiCardBrand: (cardBrand) ->
|
||||
switch cardBrand
|
||||
when 'mastercard' then return 'master'
|
||||
when 'amex' then return 'american_express'
|
||||
when 'diners' then return 'diners_club'
|
||||
else return cardBrand # a few brands are equal, for example, visa
|
||||
|
||||
# It doesn't matter if any of these are nil, all are optional.
|
||||
makeCardData: (secrets) ->
|
||||
{'name': secrets.name,
|
||||
|
||||
@@ -2,7 +2,6 @@ angular.module("admin.resources").factory 'LineItemResource', ($resource) ->
|
||||
$resource('/admin/bulk_line_items/:id.json', {}, {
|
||||
'index':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
'update':
|
||||
method: 'PUT'
|
||||
transformRequest: (data, headersGetter) =>
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) ->
|
||||
new class LineItems
|
||||
all: []
|
||||
byID: {}
|
||||
pristineByID: {}
|
||||
pagination: {}
|
||||
|
||||
index: (params={}, callback=null) ->
|
||||
LineItemResource.index params, (data) =>
|
||||
request = LineItemResource.index params, (data) =>
|
||||
@load(data)
|
||||
(callback || angular.noop)(data)
|
||||
@all.$promise = request.$promise
|
||||
@all
|
||||
|
||||
resetData: ->
|
||||
@all.length = 0
|
||||
@byID = {}
|
||||
@pristineByID = {}
|
||||
|
||||
load: (lineItems) ->
|
||||
load: (data) ->
|
||||
angular.extend(@pagination, data.pagination)
|
||||
@resetData()
|
||||
for lineItem in lineItems
|
||||
for lineItem in data.line_items
|
||||
@all.push lineItem
|
||||
@byID[lineItem.id] = lineItem
|
||||
@pristineByID[lineItem.id] = angular.copy(lineItem)
|
||||
|
||||
@@ -25,8 +32,9 @@ angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) ->
|
||||
|
||||
save: (lineItem) ->
|
||||
deferred = $q.defer()
|
||||
lineItemResource = new LineItemResource(lineItem)
|
||||
lineItem.errors = {}
|
||||
lineItem.$update({id: lineItem.id})
|
||||
lineItemResource.$update({id: lineItem.id})
|
||||
.then( (data) =>
|
||||
@pristineByID[lineItem.id] = angular.copy(lineItem)
|
||||
deferred.resolve(data)
|
||||
@@ -54,8 +62,10 @@ angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) ->
|
||||
|
||||
delete: (lineItem, callback=null) ->
|
||||
deferred = $q.defer()
|
||||
lineItem.$delete({id: lineItem.id})
|
||||
lineItemResource = new LineItemResource(lineItem)
|
||||
lineItemResource.$delete({id: lineItem.id})
|
||||
.then( (data) =>
|
||||
@all.splice(@all.indexOf(lineItem),1)
|
||||
delete @byID[lineItem.id]
|
||||
delete @pristineByID[lineItem.id]
|
||||
(callback || angular.noop)(data)
|
||||
|
||||
@@ -16,7 +16,7 @@ angular.module("admin.subscriptions").controller "DetailsController", ($scope, $
|
||||
return if !newValue?
|
||||
paymentMethod = ($scope.paymentMethods.filter (pm) -> pm.id == newValue)[0]
|
||||
return unless paymentMethod?
|
||||
$scope.cardRequired = (paymentMethod.type == "Spree::Gateway::StripeConnect")
|
||||
$scope.cardRequired = (paymentMethod.type == "Spree::Gateway::StripeConnect" || paymentMethod.type == "Spree::Gateway::StripeSCA")
|
||||
$scope.loadCustomer() if $scope.cardRequired && !$scope.customer
|
||||
|
||||
$scope.loadCustomer = ->
|
||||
|
||||
@@ -2,4 +2,10 @@ angular.module("admin.utils").directive "textangularStrip", () ->
|
||||
restrict: 'CA'
|
||||
link: (scope, element, attrs) ->
|
||||
scope.stripFormatting = ($html) ->
|
||||
return String($html).replace(/<[^>]+>/gm, '')
|
||||
element = document.createElement("div")
|
||||
element.innerHTML = String($html)
|
||||
allTags = element.getElementsByTagName("*")
|
||||
for child in allTags
|
||||
child.removeAttribute("style")
|
||||
child.removeAttribute("class")
|
||||
return element.innerHTML
|
||||
|
||||
@@ -43,12 +43,11 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
|
||||
|
||||
$scope.fetchProducts = ->
|
||||
url = "/api/products/overridable?page=::page::;per_page=100"
|
||||
PagedFetcher.fetch url, (data) => $scope.addProducts data.products
|
||||
PagedFetcher.fetch url, $scope.addProducts
|
||||
|
||||
|
||||
$scope.addProducts = (products) ->
|
||||
$scope.products = $scope.products.concat products
|
||||
VariantOverrides.ensureDataFor hubs, products
|
||||
$scope.addProducts = (data) ->
|
||||
$scope.products = $scope.products.concat data.products
|
||||
VariantOverrides.ensureDataFor hubs, data.products
|
||||
|
||||
$scope.displayDirty = ->
|
||||
if DirtyVariantOverrides.count() > 0
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// This is a manifest file that'll be compiled into including all the files listed below.
|
||||
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
||||
// be included in the compiled file accessible from http://example.com/assets/application.js
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// the compiled file.
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require_tree .
|
||||
@@ -9,6 +9,8 @@
|
||||
#= require angular-animate
|
||||
#= require angular-resource
|
||||
#= require lodash.underscore.js
|
||||
# bluebird.js is a dependency of angular-google-maps.js 2.0.0
|
||||
#= require bluebird.js
|
||||
#= require angular-scroll.min.js
|
||||
#= require angular-google-maps.min.js
|
||||
#= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js
|
||||
|
||||
@@ -9,8 +9,13 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, $location
|
||||
$scope.show_closed = false
|
||||
$scope.filtersActive = false
|
||||
$scope.distanceMatchesShown = false
|
||||
$scope.closed_shops_loading = false
|
||||
$scope.closed_shops_loaded = false
|
||||
|
||||
$scope.$watch "query", (query)->
|
||||
$scope.resetSearch(query)
|
||||
|
||||
$scope.resetSearch = (query) ->
|
||||
Enterprises.flagMatching query
|
||||
Search.search query
|
||||
$rootScope.$broadcast 'enterprisesChanged'
|
||||
@@ -19,6 +24,7 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, $location
|
||||
$timeout ->
|
||||
Enterprises.calculateDistance query, $scope.firstNameMatch()
|
||||
$rootScope.$broadcast 'enterprisesChanged'
|
||||
$scope.closed_shops_loading = false
|
||||
|
||||
$timeout ->
|
||||
if $location.search()['show_closed']?
|
||||
@@ -73,6 +79,12 @@ Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, $location
|
||||
undefined
|
||||
|
||||
$scope.showClosedShops = ->
|
||||
unless $scope.closed_shops_loaded
|
||||
$scope.closed_shops_loading = true
|
||||
$scope.closed_shops_loaded = true
|
||||
Enterprises.loadClosedEnterprises().then ->
|
||||
$scope.resetSearch($scope.query)
|
||||
|
||||
$scope.show_closed = true
|
||||
$location.search('show_closed', '1')
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, CurrentHub, $http,
|
||||
$scope.shopfront_loading = true
|
||||
$scope.toggle_tab(event)
|
||||
|
||||
$http.get("/api/enterprises/" + $scope.hub.id + "/shopfront")
|
||||
$http.get("/api/shops/" + $scope.hub.id)
|
||||
.success (data) ->
|
||||
$scope.shopfront_loading = false
|
||||
$scope.hub = data
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Darkswarm.controller "PageSelectionCtrl", ($scope, $location) ->
|
||||
$scope.selectedPage = ->
|
||||
# The path looks like `/contact` for the URL `https://ofn.org/shop#/contact`.
|
||||
# We remove the slash at the beginning.
|
||||
page = $location.path()[1..]
|
||||
|
||||
return $scope.whitelist[0] unless page
|
||||
|
||||
# If the path points to an unrelated path like `/login`, stay where we were.
|
||||
return $scope.lastPage unless page in $scope.whitelist
|
||||
|
||||
$scope.lastPage = page
|
||||
page
|
||||
|
||||
$scope.whitelistPages = (pages) ->
|
||||
$scope.whitelist = pages
|
||||
$scope.lastPage = pages[0]
|
||||
@@ -24,7 +24,7 @@ Darkswarm.controller "ProducerNodeCtrl", ($scope, HashNavigation, $anchorScroll,
|
||||
$scope.shopfront_loading = true
|
||||
$scope.toggle_tab(event)
|
||||
|
||||
$http.get("/api/enterprises/" + $scope.producer.id + "/shopfront")
|
||||
$http.get("/api/shops/" + $scope.producer.id)
|
||||
.success (data) ->
|
||||
$scope.shopfront_loading = false
|
||||
$scope.producer = data
|
||||
|
||||
@@ -7,7 +7,7 @@ window.Darkswarm = angular.module("Darkswarm", [
|
||||
'templates',
|
||||
'ngSanitize',
|
||||
'ngAnimate',
|
||||
'google-maps',
|
||||
'uiGmapgoogle-maps',
|
||||
'duScroll',
|
||||
'angularFileUpload',
|
||||
'angularSlideables'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Darkswarm.directive 'mapOsmTiles', ($timeout) ->
|
||||
restrict: 'E'
|
||||
require: '^googleMap'
|
||||
require: '^uiGmapGoogleMap'
|
||||
scope: {}
|
||||
link: (scope, elem, attrs, ctrl) ->
|
||||
$timeout =>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Darkswarm.directive 'mapSearch', ($timeout, Search) ->
|
||||
# Install a basic search field in a map
|
||||
restrict: 'E'
|
||||
require: ['^googleMap', 'ngModel']
|
||||
require: ['^uiGmapGoogleMap', 'ngModel']
|
||||
replace: true
|
||||
template: '<input id="pac-input" ng-model="query" placeholder="' + t('location_placeholder') + '"></input>'
|
||||
scope: {}
|
||||
|
||||
@@ -7,6 +7,8 @@ Darkswarm.factory 'Checkout', ($injector, CurrentOrder, ShippingMethods, StripeE
|
||||
purchase: ->
|
||||
if @paymentMethod()?.method_type == 'stripe' && !@secrets.selected_card
|
||||
StripeElements.requestToken(@secrets, @submit)
|
||||
else if @paymentMethod()?.method_type == 'stripe_sca' && !@secrets.selected_card
|
||||
StripeElements.createPaymentMethod(@secrets, @submit)
|
||||
else
|
||||
@submit()
|
||||
|
||||
@@ -59,7 +61,7 @@ Darkswarm.factory 'Checkout', ($injector, CurrentOrder, ShippingMethods, StripeE
|
||||
last_name: @order.bill_address.lastname
|
||||
}
|
||||
|
||||
if @paymentMethod()?.method_type == 'stripe'
|
||||
if @paymentMethod()?.method_type == 'stripe' || @paymentMethod()?.method_type == 'stripe_sca'
|
||||
if @secrets.selected_card
|
||||
angular.extend munged_order, {
|
||||
existing_card_id: @secrets.selected_card
|
||||
|
||||
@@ -5,7 +5,7 @@ Darkswarm.factory "EnterpriseModal", ($modal, $rootScope, $http)->
|
||||
scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
|
||||
scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1
|
||||
|
||||
$http.get("/api/enterprises/" + enterprise.id + "/shopfront").success (data) ->
|
||||
$http.get("/api/shops/" + enterprise.id).success (data) ->
|
||||
scope.enterprise = data
|
||||
$modal.open(templateUrl: "enterprise_modal.html", scope: scope)
|
||||
.error (data) ->
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, Matcher, Geo, $rootScope) ->
|
||||
Darkswarm.factory 'Enterprises', (enterprises, ShopsResource, CurrentHub, Taxons, Dereferencer, Matcher, Geo, $rootScope) ->
|
||||
new class Enterprises
|
||||
enterprises: []
|
||||
enterprises_by_id: {}
|
||||
|
||||
constructor: ->
|
||||
# Populate Enterprises.enterprises from json in page.
|
||||
@enterprises = enterprises
|
||||
@initEnterprises(enterprises)
|
||||
|
||||
initEnterprises: (enterprises) ->
|
||||
# Map enterprises to id/object pairs for lookup.
|
||||
for enterprise in enterprises
|
||||
@enterprises.push enterprise
|
||||
@enterprises_by_id[enterprise.id] = enterprise
|
||||
|
||||
# Replace enterprise and taxons ids with actual objects.
|
||||
@dereferenceEnterprises()
|
||||
@dereferenceEnterprises(enterprises)
|
||||
|
||||
@producers = @enterprises.filter (enterprise)->
|
||||
enterprise.category in ["producer_hub", "producer_shop", "producer"]
|
||||
@hubs = @enterprises.filter (enterprise)->
|
||||
enterprise.category in ["hub", "hub_profile", "producer_hub", "producer_shop"]
|
||||
|
||||
dereferenceEnterprises: ->
|
||||
dereferenceEnterprises: (enteprises) ->
|
||||
if CurrentHub.hub?.id
|
||||
CurrentHub.hub = @enterprises_by_id[CurrentHub.hub.id]
|
||||
for enterprise in @enterprises
|
||||
for enterprise in enterprises
|
||||
@dereferenceEnterprise enterprise
|
||||
|
||||
dereferenceEnterprise: (enterprise) ->
|
||||
@@ -42,6 +45,12 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer,
|
||||
for enterprise in new_enterprises
|
||||
@enterprises_by_id[enterprise.id] = enterprise
|
||||
|
||||
loadClosedEnterprises: ->
|
||||
request = ShopsResource.closed_shops {}, (data) =>
|
||||
@initEnterprises(data)
|
||||
|
||||
request.$promise
|
||||
|
||||
flagMatching: (query) ->
|
||||
for enterprise in @enterprises
|
||||
enterprise.matches_name_query = if query? && query.length > 0
|
||||
@@ -50,7 +59,7 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer,
|
||||
false
|
||||
|
||||
calculateDistance: (query, firstMatching) ->
|
||||
if query?.length > 0
|
||||
if query?.length > 0 and Geo.OK
|
||||
if firstMatching?
|
||||
@setDistanceFrom firstMatching
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Darkswarm.service "Geo", ->
|
||||
new class Geo
|
||||
OK: google.maps.GeocoderStatus.OK
|
||||
OK: google?.maps?.GeocoderStatus?.OK
|
||||
|
||||
# Usage:
|
||||
# Geo.geocode address, (results, status) ->
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
Darkswarm.factory 'ShopsResource', ($resource) ->
|
||||
$resource('/api/shops/:id.json', {}, {
|
||||
'closed_shops':
|
||||
method: 'GET'
|
||||
isArray: true
|
||||
url: '/api/shops/closed_shops.json'
|
||||
})
|
||||
@@ -1,12 +1,10 @@
|
||||
Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
|
||||
new class StripeElements
|
||||
# TODO: add locale here for translations of error messages etc. from Stripe
|
||||
|
||||
# These are both set from the StripeElements directive
|
||||
stripe: null
|
||||
card: null
|
||||
|
||||
# New Stripe Elements method
|
||||
# Create Token to be used with the Stripe Charges API
|
||||
requestToken: (secrets, submit, loading_message = t("processing_payment")) ->
|
||||
return unless @stripe? && @card?
|
||||
|
||||
@@ -17,15 +15,40 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
|
||||
if(response.error)
|
||||
Loading.clear()
|
||||
RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"})
|
||||
@triggerAngularDigest()
|
||||
console.error(JSON.stringify(response.error))
|
||||
else
|
||||
secrets.token = response.token.id
|
||||
secrets.cc_type = @mapCC(response.token.card.brand)
|
||||
secrets.cc_type = @mapTokenApiCardBrand(response.token.card.brand)
|
||||
secrets.card = response.token.card
|
||||
submit()
|
||||
|
||||
# Maps the brand returned by Stripe to that required by activemerchant
|
||||
mapCC: (ccType) ->
|
||||
switch ccType
|
||||
# Create Payment Method to be used with the Stripe Payment Intents API
|
||||
createPaymentMethod: (secrets, submit, loading_message = t("processing_payment")) ->
|
||||
return unless @stripe? && @card?
|
||||
|
||||
Loading.message = loading_message
|
||||
cardData = @makeCardData(secrets)
|
||||
|
||||
@stripe.createPaymentMethod({ type: 'card', card: @card }, @card, cardData).then (response) =>
|
||||
if(response.error)
|
||||
Loading.clear()
|
||||
RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"})
|
||||
@triggerAngularDigest()
|
||||
console.error(JSON.stringify(response.error))
|
||||
else
|
||||
secrets.token = response.paymentMethod.id
|
||||
secrets.cc_type = @mapPaymentMethodsApiCardBrand(response.paymentMethod.card.brand)
|
||||
secrets.card = response.paymentMethod.card
|
||||
submit()
|
||||
|
||||
triggerAngularDigest: ->
|
||||
# $evalAsync is improved way of triggering a digest without calling $apply
|
||||
$rootScope.$evalAsync()
|
||||
|
||||
# Maps the brand returned by Stripe's tokenAPI to that required by activemerchant
|
||||
mapTokenApiCardBrand: (cardBrand) ->
|
||||
switch cardBrand
|
||||
when 'MasterCard' then return 'master'
|
||||
when 'Visa' then return 'visa'
|
||||
when 'American Express' then return 'american_express'
|
||||
@@ -33,6 +56,14 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
|
||||
when 'JCB' then return 'jcb'
|
||||
when 'Diners Club' then return 'diners_club'
|
||||
|
||||
# Maps the brand returned by Stripe's paymentMethodsAPI to that required by activemerchant
|
||||
mapPaymentMethodsApiCardBrand: (cardBrand) ->
|
||||
switch cardBrand
|
||||
when 'mastercard' then return 'master'
|
||||
when 'amex' then return 'american_express'
|
||||
when 'diners' then return 'diners_club'
|
||||
else return cardBrand # a few brands are equal, for example, visa
|
||||
|
||||
# It doesn't matter if any of these are nil, all are optional.
|
||||
makeCardData: (secrets) ->
|
||||
{'name': secrets.name,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
||||
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
*= require_self
|
||||
*= require_tree .
|
||||
*/
|
||||
@@ -1,103 +1,172 @@
|
||||
@import "typography";
|
||||
|
||||
.darkswarm navigation {
|
||||
display: block;
|
||||
background: #f7f7f7;
|
||||
ordercycle {
|
||||
float: right;
|
||||
background: $grey-050;
|
||||
color: $grey-800;
|
||||
width: 100%;
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
margin-top: 1em;
|
||||
padding: 1em 1.25em 0;
|
||||
|
||||
distributor.details {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
min-height: 150px;
|
||||
padding: 30px 0 20px 0;
|
||||
p {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
h4 i {
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
|
||||
@media all and (max-width: 1024px) {
|
||||
float: none;
|
||||
padding: 0.5em 1em;
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
display: inline-block;
|
||||
border-radius: 0;
|
||||
position: relative;
|
||||
right: 0;
|
||||
height: auto;
|
||||
|
||||
p {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
padding: 0.5em 1em 0.75em;
|
||||
}
|
||||
|
||||
.order-cycle-select {
|
||||
border: 1px solid $teal-300;
|
||||
display: inline-block;
|
||||
font-size: 1em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
.select-label {
|
||||
background-color: rgba($teal-300, 0.5);
|
||||
display: inline-block;
|
||||
border-radius: 0.25em 0 0 0.25em;
|
||||
float: left;
|
||||
font-size: 1em;
|
||||
line-height: 1.5em;
|
||||
padding: 0.5em 0.75em;
|
||||
height: 2.35em;
|
||||
|
||||
span {
|
||||
width: max-content;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
width: 200px;
|
||||
}
|
||||
img {
|
||||
display: block;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
width: inherit;
|
||||
display: inline-block;
|
||||
color: $white;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
margin-bottom: 0;
|
||||
font-size: 1em;
|
||||
line-height: 1.5em;
|
||||
padding: 0.5em 1.25em 0.5em 0.75em;
|
||||
height: 2.35em;
|
||||
background-image: url('/assets/white-caret.svg');
|
||||
background-size: 30px auto;
|
||||
border-radius: 0 0.25em 0.25em 0;
|
||||
min-width: 13em;
|
||||
|
||||
location {
|
||||
@include headingFont;
|
||||
}
|
||||
@media all and (max-width: 768px) {
|
||||
location, location + small {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#distributor_title h3 {
|
||||
margin-top: 0;
|
||||
@media all and (max-width: 768px) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ordercycle {
|
||||
text-align: right;
|
||||
float: right;
|
||||
p {
|
||||
max-width: 400px;
|
||||
}
|
||||
h4 i {
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
@media all and (max-width: 640px) {
|
||||
float: left;
|
||||
clear: left;
|
||||
text-align: left;
|
||||
padding: 12px 10px;
|
||||
@media all and (max-width: 480px) {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
background: #e5e5e5;
|
||||
p {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
form.custom {
|
||||
text-align: right;
|
||||
& > strong {
|
||||
line-height: 2.5;
|
||||
font-size: 1.29em;
|
||||
padding-right: 14px;
|
||||
}
|
||||
select {
|
||||
width: inherit;
|
||||
display: inline-block;
|
||||
border: 1px #999;
|
||||
color: #666;
|
||||
font-size: 1em;
|
||||
margin-bottom: 0;
|
||||
padding: 8px 20px 8px 12px;
|
||||
@media all and (max-width: 768px) {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
closing {
|
||||
@include headingFont;
|
||||
color: black;
|
||||
font-size: 1.5em;
|
||||
display: block;
|
||||
padding-bottom: 12px;
|
||||
@media all and (max-width: 768px) {
|
||||
font-size: 1.2em;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
span {
|
||||
@media all and (max-width: 768px) {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 1024px) {
|
||||
float: none;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
@media all and (max-width: 768px) {
|
||||
float: none;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
closing {
|
||||
@include headingFont;
|
||||
color: $grey-800;
|
||||
font-size: 1.25rem;
|
||||
display: block;
|
||||
padding: 0.5em 0;
|
||||
|
||||
span {
|
||||
@media all and (max-width: 768px) {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shop ordercycle {
|
||||
background: $teal-400;
|
||||
color: $white;
|
||||
|
||||
&.requires-selection {
|
||||
background-color: $red-700;
|
||||
|
||||
.order-cycle-select {
|
||||
border: 1px solid $red-500;
|
||||
|
||||
.select-label {
|
||||
background-color: rgba($red-500, 0.5);
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: $white;
|
||||
background-image: url('/assets/black-caret.svg');
|
||||
color: $grey-500;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closing {
|
||||
color: $white;
|
||||
padding: 0 0 12px;
|
||||
|
||||
@media all and (max-width: 1024px) {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
padding: 0.2em 0 0;
|
||||
font-size: 1.2em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
@media all and (max-width: 768px) {
|
||||
float: none;
|
||||
padding: 0 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
form.custom {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
shop navigation ordercycle {
|
||||
margin-top: 3em;
|
||||
padding: 1em;
|
||||
height: 7.6em;
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
}
|
||||
|
||||
.sub-header {
|
||||
form {
|
||||
p {
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,6 @@
|
||||
@import "mixins";
|
||||
@import "branding";
|
||||
|
||||
// .darkswarm
|
||||
// product
|
||||
|
||||
ordercycle {
|
||||
.joyride-tip-guide {
|
||||
background-color: $clr-brick;
|
||||
|
||||
.joyride-nub.right {
|
||||
border-color: $clr-brick !important;
|
||||
border-top-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@media all and (max-width: 768px) {
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pop over
|
||||
// Foundation overrides
|
||||
.joyride-tip-guide.price_breakdown {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
products .filter-box {
|
||||
background: #f7f7f7;
|
||||
background: $grey-050;
|
||||
}
|
||||
|
||||
.filter-box {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
$ofn-brand: #f27052;
|
||||
|
||||
$distributor-header-shadow: 0 1px 0 rgba(0, 0, 0, 0.05), 0 8px 6px -6px rgba(0, 0, 0, 0.2);
|
||||
|
||||
// e.g. australia, uk, norway specific color
|
||||
|
||||
$ofn-grey: #808184;
|
||||
@@ -38,3 +40,27 @@ $light-grey: #ddd;
|
||||
$light-grey-transparency: rgba(0, 0, 0, .1);
|
||||
$black: #000;
|
||||
$white: #fff;
|
||||
|
||||
$grey-050: #f7f7f7;
|
||||
|
||||
$grey-400: #bbb;
|
||||
$grey-500: #999;
|
||||
$grey-600: #777;
|
||||
$grey-700: #555;
|
||||
$grey-800: #333;
|
||||
|
||||
$teal-300: #80d3df;
|
||||
$teal-400: #4cb5c5;
|
||||
$teal-500: #0096ad;
|
||||
|
||||
$orange-400: #ff9466;
|
||||
$orange-500: #f27052;
|
||||
$orange-600: #d7583a;
|
||||
|
||||
$red-500: #e54e47;
|
||||
$red-700: #c1122b;
|
||||
|
||||
$social-facebook: #3b5998;
|
||||
$social-instagram: #e1306c;
|
||||
$social-linkedin: #0e76a8;
|
||||
$social-twitter: #00acee;
|
||||
|
||||
@@ -44,7 +44,7 @@ checkout {
|
||||
h5 {
|
||||
margin: 0;
|
||||
padding: 0.65em;
|
||||
background: #f7f7f7;
|
||||
background: $grey-050;
|
||||
|
||||
.label {
|
||||
font-size: 1em;
|
||||
@@ -109,9 +109,4 @@ checkout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #c82020;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
52
app/assets/stylesheets/darkswarm/distributor_header.css.scss
Normal file
52
app/assets/stylesheets/darkswarm/distributor_header.css.scss
Normal file
@@ -0,0 +1,52 @@
|
||||
@import 'typography';
|
||||
|
||||
section {
|
||||
:not(shop) navigation {
|
||||
box-shadow: $distributor-header-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
.darkswarm navigation {
|
||||
display: block;
|
||||
background: $white;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
.details {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
min-height: 150px;
|
||||
padding: 30px 0 0;
|
||||
position: relative;
|
||||
|
||||
select {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
location {
|
||||
@include headingFont;
|
||||
}
|
||||
|
||||
@media all and (max-width: 768px) {
|
||||
location, location + small {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#distributor_title h3 {
|
||||
margin-top: 0;
|
||||
padding-top: 0.45em;
|
||||
|
||||
@media all and (max-width: 768px) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,5 +14,10 @@
|
||||
|
||||
.more-controls {
|
||||
text-align: center;
|
||||
|
||||
.spinner {
|
||||
height: 2.25em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,22 @@
|
||||
|
||||
i {
|
||||
font-size: 2rem;
|
||||
|
||||
&.facebook {
|
||||
color: $social-facebook;
|
||||
}
|
||||
|
||||
&.twitter {
|
||||
color: $social-twitter;
|
||||
}
|
||||
|
||||
&.linkedin {
|
||||
color: $social-linkedin;
|
||||
}
|
||||
|
||||
&.instagram {
|
||||
color: $social-instagram;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
@@ -3,22 +3,27 @@
|
||||
@import "branding";
|
||||
|
||||
// Tabs styling
|
||||
.tabset-ctrl#shop-tabs {
|
||||
#shop-tabs {
|
||||
|
||||
.tab-buttons {
|
||||
background: url("/assets/gray_jean.png") top left repeat;
|
||||
|
||||
@include box-shadow(inset 0 2px 3px 0 rgba(0, 0, 0, 0.15));
|
||||
|
||||
color: $dark-grey;
|
||||
box-shadow: $distributor-header-shadow;
|
||||
|
||||
.row:first-child {
|
||||
.columns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@media all and (max-width: 1024px) {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
.page {
|
||||
text-align: center;
|
||||
border-top: 4px solid transparent;
|
||||
display: inline-block;
|
||||
@@ -27,8 +32,7 @@
|
||||
>a {
|
||||
outline: none;
|
||||
display: block;
|
||||
background-color: #efefef;
|
||||
color: #222;
|
||||
color: $grey-500;
|
||||
font-family: "Oswald", sans-serif;
|
||||
}
|
||||
|
||||
@@ -39,12 +43,11 @@
|
||||
text-transform: uppercase;
|
||||
line-height: 1;
|
||||
font-size: 0.875em;
|
||||
text-shadow: 0 -1px 1px #ffffff;
|
||||
padding: 1em 2em;
|
||||
border: none;
|
||||
|
||||
&:hover, &:focus, &:active {
|
||||
color: $clr-brick-bright;
|
||||
&:hover, &:active {
|
||||
color: $teal-500;
|
||||
}
|
||||
|
||||
&, &:hover {
|
||||
@@ -53,29 +56,41 @@
|
||||
|
||||
@media all and (max-width: 640px) {
|
||||
padding: 0.35em 0 0.65em 0;
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-bottom: 4px solid $clr-brick;
|
||||
border-bottom: 4px solid $teal-500;
|
||||
|
||||
a {
|
||||
color: $clr-brick;
|
||||
color: $teal-500;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 1024px) {
|
||||
display: table-cell;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// content revealed in accordion
|
||||
|
||||
.tab-view {
|
||||
.page-view {
|
||||
margin-bottom: 5em;
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
.content {
|
||||
padding: 1.25em 0;
|
||||
background-color: $white;
|
||||
background-color: transparent;
|
||||
|
||||
a {
|
||||
color: $orange-500;
|
||||
|
||||
&:hover {
|
||||
color: $orange-600;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0px 0px 0px 40px;
|
||||
@@ -83,6 +98,7 @@
|
||||
|
||||
h5 {
|
||||
margin-bottom: 1em;
|
||||
font-family: $body-font;
|
||||
}
|
||||
|
||||
p {
|
||||
@@ -100,13 +116,9 @@
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
color: $dark-grey;
|
||||
border-bottom: 1px solid $disabled-dark;
|
||||
color: $grey-600;
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
}
|
||||
|
||||
$headingFont: "Oswald";
|
||||
$bodyFont: "Roboto";
|
||||
$body-font: "Roboto", Arial, sans-serif;
|
||||
|
||||
body {
|
||||
@include bodyFont;
|
||||
@@ -43,7 +43,7 @@ small, .small {
|
||||
.text-small {
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.5rem;
|
||||
font-family: $bodyFont;
|
||||
font-family: $body-font;
|
||||
|
||||
&, & * {
|
||||
font-size: 0.875rem;
|
||||
@@ -52,12 +52,12 @@ small, .small {
|
||||
|
||||
.text-normal {
|
||||
font-weight: 400;
|
||||
font-family: $bodyFont;
|
||||
font-family: $body-font;
|
||||
}
|
||||
|
||||
.text-skinny {
|
||||
font-weight: 300;
|
||||
font-family: $bodyFont;
|
||||
font-family: $body-font;
|
||||
}
|
||||
|
||||
.word-wrap {
|
||||
@@ -114,9 +114,9 @@ ul.bullet-list, ul.check-list {
|
||||
}
|
||||
|
||||
li:before {
|
||||
content: "";
|
||||
content: "•";
|
||||
font-family: "OFN";
|
||||
margin-left: -1.25em;
|
||||
margin: 0 0.25em 0 -1.25em;
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
@include border-radius(0.5em);
|
||||
|
||||
font-family: $bodyFont;
|
||||
font-family: $body-font;
|
||||
background-color: transparent;
|
||||
border: 2px solid rgba(200, 200, 200, 1);
|
||||
color: #999;
|
||||
@@ -63,7 +63,7 @@
|
||||
}
|
||||
|
||||
.button.primary, button.primary {
|
||||
font-family: $bodyFont;
|
||||
font-family: $body-font;
|
||||
background: $clr-brick;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -4,18 +4,17 @@ module Admin
|
||||
#
|
||||
def index
|
||||
order_params = params[:q].andand.delete :order
|
||||
orders = order_permissions.editable_orders.ransack(order_params).result
|
||||
|
||||
order_permissions = ::Permissions::Order.new(spree_current_user)
|
||||
orders = order_permissions.
|
||||
editable_orders.ransack(order_params).result
|
||||
|
||||
line_items = order_permissions.
|
||||
@line_items = order_permissions.
|
||||
editable_line_items.where(order_id: orders).
|
||||
includes(variant: { option_values: :option_type }).
|
||||
ransack(params[:q]).result.
|
||||
reorder('spree_line_items.order_id ASC, spree_line_items.id ASC')
|
||||
|
||||
render_as_json line_items
|
||||
@line_items = @line_items.page(page).per(params[:per_page]) if using_pagination?
|
||||
|
||||
render json: { line_items: serialized_line_items, pagination: pagination_data }
|
||||
end
|
||||
|
||||
# PUT /admin/bulk_line_items/:id.json
|
||||
@@ -65,6 +64,12 @@ module Admin
|
||||
Api::Admin::LineItemSerializer
|
||||
end
|
||||
|
||||
def serialized_line_items
|
||||
ActiveModel::ArraySerializer.new(
|
||||
@line_items, each_serializer: serializer(nil)
|
||||
)
|
||||
end
|
||||
|
||||
def authorize_update!
|
||||
authorize! :update, order
|
||||
authorize! :read, order
|
||||
@@ -73,5 +78,28 @@ module Admin
|
||||
def order
|
||||
@line_item.order
|
||||
end
|
||||
|
||||
def order_permissions
|
||||
::Permissions::Order.new(spree_current_user)
|
||||
end
|
||||
|
||||
def using_pagination?
|
||||
params[:per_page]
|
||||
end
|
||||
|
||||
def pagination_data
|
||||
return unless using_pagination?
|
||||
|
||||
{
|
||||
results: @line_items.total_count,
|
||||
pages: @line_items.num_pages,
|
||||
page: page.to_i,
|
||||
per_page: params[:per_page].to_i
|
||||
}
|
||||
end
|
||||
|
||||
def page
|
||||
params[:page] || 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,7 +23,6 @@ module Admin
|
||||
before_filter :setup_property, only: [:edit]
|
||||
|
||||
helper 'spree/products'
|
||||
include ActionView::Helpers::TextHelper
|
||||
include OrderCyclesHelper
|
||||
|
||||
def index
|
||||
@@ -33,6 +32,12 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@object = Enterprise.where(permalink: params[:id]).
|
||||
includes(users: [:ship_address, :bill_address]).first
|
||||
super
|
||||
end
|
||||
|
||||
def welcome
|
||||
render layout: "spree/layouts/bare_admin"
|
||||
end
|
||||
@@ -77,19 +82,12 @@ module Admin
|
||||
|
||||
def bulk_update
|
||||
@enterprise_set = EnterpriseSet.new(collection, params[:enterprise_set])
|
||||
touched_enterprises = @enterprise_set.collection.select(&:changed?)
|
||||
if @enterprise_set.save
|
||||
flash[:success] = I18n.t(:enterprise_bulk_update_success_notice)
|
||||
|
||||
# 18-3-2015: It seems that the form for this action sometimes loads bogus values for
|
||||
# the 'sells' field, and submitting that form results in a bunch of enterprises with
|
||||
# values that have mysteriously changed. This statement is here to help debug that
|
||||
# issue, and should be removed (along with its display in index.html.haml) when the
|
||||
# issue has been resolved.
|
||||
flash[:action] = "#{I18n.t(:updated)} #{pluralize(touched_enterprises.count, 'enterprise')}: #{touched_enterprises.map(&:name).join(', ')}"
|
||||
|
||||
redirect_to main_app.admin_enterprises_path
|
||||
else
|
||||
touched_enterprises = @enterprise_set.collection.select(&:changed?)
|
||||
@enterprise_set.collection.select! { |e| touched_enterprises.include? e }
|
||||
flash[:error] = I18n.t(:enterprise_bulk_update_error)
|
||||
render :index
|
||||
@@ -180,12 +178,14 @@ module Admin
|
||||
end
|
||||
|
||||
def load_methods_and_fees
|
||||
enterprise_payment_methods = @enterprise.payment_methods.to_a
|
||||
enterprise_shipping_methods = @enterprise.shipping_methods.to_a
|
||||
# rubocop:disable Style/TernaryParentheses
|
||||
@payment_methods = Spree::PaymentMethod.managed_by(spree_current_user).sort_by! do |pm|
|
||||
[(@enterprise.payment_methods.include? pm) ? 0 : 1, pm.name]
|
||||
[(enterprise_payment_methods.include? pm) ? 0 : 1, pm.name]
|
||||
end
|
||||
@shipping_methods = Spree::ShippingMethod.managed_by(spree_current_user).sort_by! do |sm|
|
||||
[(@enterprise.shipping_methods.include? sm) ? 0 : 1, sm.name]
|
||||
[(enterprise_shipping_methods.include? sm) ? 0 : 1, sm.name]
|
||||
end
|
||||
# rubocop:enable Style/TernaryParentheses
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ module Admin
|
||||
render_as_json @collection,
|
||||
ams_prefix: params[:ams_prefix],
|
||||
current_user: spree_current_user,
|
||||
subscriptions_count: SubscriptionsCount.new(@collection)
|
||||
subscriptions_count: OrderManagement::Subscriptions::Count.new(@collection)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -74,7 +74,7 @@ module Admin
|
||||
render_as_json @order_cycles,
|
||||
ams_prefix: 'index',
|
||||
current_user: spree_current_user,
|
||||
subscriptions_count: SubscriptionsCount.new(@collection)
|
||||
subscriptions_count: OrderManagement::Subscriptions::Count.new(@collection)
|
||||
else
|
||||
order_cycle = order_cycle_set.collection.find{ |oc| oc.errors.present? }
|
||||
render json: { errors: order_cycle.errors.full_messages }, status: :unprocessable_entity
|
||||
|
||||
@@ -9,25 +9,19 @@ module Admin
|
||||
|
||||
def cancel
|
||||
if @proxy_order.cancel
|
||||
respond_with(@proxy_order) do |format|
|
||||
format.json { render_as_json @proxy_order }
|
||||
end
|
||||
render_as_json @proxy_order
|
||||
else
|
||||
respond_with(@proxy_order) do |format|
|
||||
format.json { render json: { errors: [t('admin.proxy_orders.cancel.could_not_cancel_the_order')] }, status: :unprocessable_entity }
|
||||
end
|
||||
render json: { errors: [t('admin.proxy_orders.cancel.could_not_cancel_the_order')] },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def resume
|
||||
if @proxy_order.resume
|
||||
respond_with(@proxy_order) do |format|
|
||||
format.json { render_as_json @proxy_order }
|
||||
end
|
||||
render_as_json @proxy_order
|
||||
else
|
||||
respond_with(@proxy_order) do |format|
|
||||
format.json { render json: { errors: [t('admin.proxy_orders.resume.could_not_resume_the_order')] }, status: :unprocessable_entity }
|
||||
end
|
||||
render json: { errors: [t('admin.proxy_orders.resume.could_not_resume_the_order')] },
|
||||
status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
require 'open_food_network/permissions'
|
||||
require 'open_food_network/proxy_order_syncer'
|
||||
require 'order_management/subscriptions/proxy_order_syncer'
|
||||
|
||||
module Admin
|
||||
class SchedulesController < ResourceController
|
||||
@@ -81,7 +81,7 @@ module Admin
|
||||
return unless removed_ids.any? || new_ids.any?
|
||||
|
||||
subscriptions = Subscription.where(schedule_id: @schedule)
|
||||
syncer = OpenFoodNetwork::ProxyOrderSyncer.new(subscriptions)
|
||||
syncer = OrderManagement::Subscriptions::ProxyOrderSyncer.new(subscriptions)
|
||||
syncer.sync!
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,7 +56,7 @@ module Admin
|
||||
end
|
||||
|
||||
def variant_if_eligible(variant_id)
|
||||
SubscriptionVariantsService.eligible_variants(@shop).find_by_id(variant_id)
|
||||
OrderManagement::Subscriptions::VariantsList.eligible_variants(@shop).find_by_id(variant_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
require 'open_food_network/permissions'
|
||||
require 'open_food_network/proxy_order_syncer'
|
||||
|
||||
module Admin
|
||||
class SubscriptionsController < ResourceController
|
||||
@@ -65,7 +64,7 @@ module Admin
|
||||
private
|
||||
|
||||
def save_form_and_render(render_issues = true)
|
||||
form = SubscriptionForm.new(@subscription, params[:subscription])
|
||||
form = OrderManagement::Subscriptions::Form.new(@subscription, params[:subscription])
|
||||
unless form.save
|
||||
render json: { errors: form.json_errors }, status: :unprocessable_entity
|
||||
return
|
||||
|
||||
@@ -73,8 +73,10 @@ module Admin
|
||||
end
|
||||
|
||||
def collection
|
||||
@variant_overrides = VariantOverride.includes(:variant).for_hubs(params[:hub_id] || @hubs)
|
||||
@variant_overrides.select { |vo| vo.variant.present? }
|
||||
@variant_overrides = VariantOverride.
|
||||
includes(variant: :product).
|
||||
for_hubs(params[:hub_id] || @hubs).
|
||||
select { |vo| vo.variant.present? }
|
||||
end
|
||||
|
||||
def collection_actions
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class CustomersController < BaseController
|
||||
class CustomersController < Api::BaseController
|
||||
skip_authorization_check only: :index
|
||||
|
||||
def index
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class EnterpriseAttachmentController < BaseController
|
||||
class EnterpriseAttachmentController < Api::BaseController
|
||||
class MissingImplementationError < StandardError; end
|
||||
class UnknownEnterpriseAuthorizationActionError < StandardError; end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class EnterpriseFeesController < BaseController
|
||||
class EnterpriseFeesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def destroy
|
||||
|
||||
@@ -5,7 +5,6 @@ module Api
|
||||
before_filter :override_sells, only: [:create, :update]
|
||||
before_filter :override_visible, only: [:create, :update]
|
||||
respond_to :json
|
||||
skip_authorization_check only: [:shopfront]
|
||||
|
||||
def create
|
||||
authorize! :create, Enterprise
|
||||
@@ -42,12 +41,6 @@ module Api
|
||||
end
|
||||
end
|
||||
|
||||
def shopfront
|
||||
enterprise = Enterprise.find_by_id(params[:id])
|
||||
|
||||
render text: Api::EnterpriseShopfrontSerializer.new(enterprise).to_json, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def override_owner
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class LogosController < EnterpriseAttachmentController
|
||||
class LogosController < Api::EnterpriseAttachmentController
|
||||
private
|
||||
|
||||
def attachment_name
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class OrderCyclesController < BaseController
|
||||
class OrderCyclesController < Api::BaseController
|
||||
include EnterprisesHelper
|
||||
respond_to :json
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class OrdersController < BaseController
|
||||
class OrdersController < Api::BaseController
|
||||
def show
|
||||
authorize! :read, order
|
||||
render json: order, serializer: Api::OrderDetailedSerializer, current_order: order
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class ProductImagesController < BaseController
|
||||
class ProductImagesController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
def update_product_image
|
||||
|
||||
@@ -69,12 +69,12 @@ module Api
|
||||
end
|
||||
|
||||
def overridable
|
||||
producers = OpenFoodNetwork::Permissions.new(current_api_user).
|
||||
variant_override_producers.by_name
|
||||
producer_ids = OpenFoodNetwork::Permissions.new(current_api_user).
|
||||
variant_override_producers.by_name.select('enterprises.id')
|
||||
|
||||
@products = paged_products_for_producers producers
|
||||
@products = paged_products_for_producers producer_ids
|
||||
|
||||
render_paged_products @products
|
||||
render_paged_products @products, ::Api::Admin::ProductSimpleSerializer
|
||||
end
|
||||
|
||||
# POST /api/products/:product_id/clone
|
||||
@@ -118,19 +118,20 @@ module Api
|
||||
]
|
||||
end
|
||||
|
||||
def paged_products_for_producers(producers)
|
||||
def paged_products_for_producers(producer_ids)
|
||||
Spree::Product.scoped.
|
||||
merge(product_scope).
|
||||
where(supplier_id: producers).
|
||||
includes(variants: [:product, :default_price, :stock_items]).
|
||||
where(supplier_id: producer_ids).
|
||||
by_producer.by_name.
|
||||
ransack(params[:q]).result.
|
||||
page(params[:page]).per(params[:per_page])
|
||||
end
|
||||
|
||||
def render_paged_products(products)
|
||||
def render_paged_products(products, product_serializer = ::Api::Admin::ProductSerializer)
|
||||
serializer = ActiveModel::ArraySerializer.new(
|
||||
products,
|
||||
each_serializer: ::Api::Admin::ProductSerializer
|
||||
each_serializer: product_serializer
|
||||
)
|
||||
|
||||
render text: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class PromoImagesController < EnterpriseAttachmentController
|
||||
class PromoImagesController < Api::EnterpriseAttachmentController
|
||||
private
|
||||
|
||||
def attachment_name
|
||||
|
||||
27
app/controllers/api/shops_controller.rb
Normal file
27
app/controllers/api/shops_controller.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class ShopsController < BaseController
|
||||
respond_to :json
|
||||
skip_authorization_check only: [:show, :closed_shops]
|
||||
|
||||
def show
|
||||
enterprise = Enterprise.find_by_id(params[:id])
|
||||
|
||||
render text: Api::EnterpriseShopfrontSerializer.new(enterprise).to_json, status: :ok
|
||||
end
|
||||
|
||||
def closed_shops
|
||||
@active_distributor_ids = []
|
||||
@earliest_closing_times = []
|
||||
|
||||
serialized_closed_shops = ActiveModel::ArraySerializer.new(
|
||||
ShopsListService.new.closed_shops,
|
||||
each_serializer: Api::EnterpriseSerializer,
|
||||
data: OpenFoodNetwork::EnterpriseInjectionData.new
|
||||
)
|
||||
|
||||
render json: serialized_closed_shops
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
require 'open_food_network/referer_parser'
|
||||
require 'spree/authentication_helpers'
|
||||
require_dependency 'spree/authentication_helpers'
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
protect_from_forgery
|
||||
|
||||
@@ -29,17 +29,19 @@ class BaseController < ApplicationController
|
||||
return
|
||||
end
|
||||
|
||||
@order_cycles = OrderCycle.with_distributor(@distributor).active
|
||||
.order(@distributor.preferred_shopfront_order_cycle_order)
|
||||
@order_cycles = Shop::OrderCyclesList.new(@distributor, current_customer).call
|
||||
|
||||
applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor,
|
||||
"FilterOrderCycles",
|
||||
current_customer.andand.tag_list)
|
||||
applicator.filter!(@order_cycles)
|
||||
set_order_cycle
|
||||
end
|
||||
|
||||
# And default to the only order cycle if there's only the one
|
||||
if @order_cycles.count == 1
|
||||
current_order(true).set_order_cycle! @order_cycles.first
|
||||
end
|
||||
# Default to the only order cycle if there's only one
|
||||
#
|
||||
# Here we need to use @order_cycles.size not @order_cycles.count
|
||||
# because OrderCyclesList returns a modified ActiveRecord::Relation
|
||||
# and these modifications are not seen if it is reloaded with count
|
||||
def set_order_cycle
|
||||
return if @order_cycles.size != 1
|
||||
|
||||
current_order(true).set_order_cycle! @order_cycles.first
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,6 +35,8 @@ class CheckoutController < Spree::StoreController
|
||||
rescue_from Spree::Core::GatewayError, with: :rescue_from_spree_gateway_error
|
||||
|
||||
def edit
|
||||
return handle_redirect_from_stripe if valid_payment_intent_provided?
|
||||
|
||||
# This is only required because of spree_paypal_express. If we implement
|
||||
# a version of paypal that uses this controller, and more specifically
|
||||
# the #update_failed method, then we can remove this call
|
||||
@@ -51,9 +53,8 @@ class CheckoutController < Spree::StoreController
|
||||
rescue Spree::Core::GatewayError => e
|
||||
rescue_from_spree_gateway_error(e)
|
||||
rescue StandardError => e
|
||||
Bugsnag.notify(e)
|
||||
flash[:error] = I18n.t("checkout.failed")
|
||||
update_failed
|
||||
update_failed(e)
|
||||
end
|
||||
|
||||
# Clears the cached order. Required for #current_order to return a new order
|
||||
@@ -151,6 +152,23 @@ class CheckoutController < Spree::StoreController
|
||||
end
|
||||
end
|
||||
|
||||
def valid_payment_intent_provided?
|
||||
params["payment_intent"]&.starts_with?("pi_") &&
|
||||
@order.state == "payment" &&
|
||||
@order.payments.last.state == "pending" &&
|
||||
@order.payments.last.response_code == params["payment_intent"]
|
||||
end
|
||||
|
||||
def handle_redirect_from_stripe
|
||||
if advance_order_state(@order) && order_complete?
|
||||
checkout_succeeded
|
||||
redirect_to(order_path(@order)) && return
|
||||
else
|
||||
flash[:error] = order_error
|
||||
checkout_failed
|
||||
end
|
||||
end
|
||||
|
||||
def checkout_workflow(shipping_method_id)
|
||||
while @order.state != "complete"
|
||||
if @order.state == "payment"
|
||||
@@ -161,15 +179,15 @@ class CheckoutController < Spree::StoreController
|
||||
|
||||
next if advance_order_state(@order)
|
||||
|
||||
flash[:error] = order_workflow_error
|
||||
return update_failed
|
||||
end
|
||||
|
||||
update_result
|
||||
update_response
|
||||
end
|
||||
|
||||
def redirect_to_payment_gateway
|
||||
redirect_path = Checkout::PaymentRedirect.new(params).path
|
||||
redirect_path = Checkout::PaypalRedirect.new(params).path
|
||||
redirect_path = Checkout::StripeRedirect.new(params, @order).path if redirect_path.blank?
|
||||
return if redirect_path.blank?
|
||||
|
||||
render json: { path: redirect_path }, status: :ok
|
||||
@@ -185,7 +203,7 @@ class CheckoutController < Spree::StoreController
|
||||
false
|
||||
end
|
||||
|
||||
def order_workflow_error
|
||||
def order_error
|
||||
if @order.errors.present?
|
||||
@order.errors.full_messages.to_sentence
|
||||
else
|
||||
@@ -193,27 +211,27 @@ class CheckoutController < Spree::StoreController
|
||||
end
|
||||
end
|
||||
|
||||
def update_result
|
||||
if @order.state == "complete" || @order.completed?
|
||||
save_order_addresses_as_user_default
|
||||
ResetOrderService.new(self, current_order).call
|
||||
|
||||
update_succeeded
|
||||
def update_response
|
||||
if order_complete?
|
||||
checkout_succeeded
|
||||
update_succeeded_response
|
||||
else
|
||||
update_failed
|
||||
update_failed(RuntimeError.new("Order not complete after the checkout workflow"))
|
||||
end
|
||||
end
|
||||
|
||||
def save_order_addresses_as_user_default
|
||||
user_default_address_setter = UserDefaultAddressSetter.new(@order, spree_current_user)
|
||||
user_default_address_setter.set_default_bill_address if params[:order][:default_bill_address]
|
||||
user_default_address_setter.set_default_ship_address if params[:order][:default_ship_address]
|
||||
def order_complete?
|
||||
@order.state == "complete" || @order.completed?
|
||||
end
|
||||
|
||||
def update_succeeded
|
||||
def checkout_succeeded
|
||||
Checkout::PostCheckoutActions.new(@order).success(self, params, spree_current_user)
|
||||
|
||||
session[:access_token] = current_order.token
|
||||
flash[:notice] = t(:order_processed_successfully)
|
||||
end
|
||||
|
||||
def update_succeeded_response
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
respond_with(@order, location: order_path(@order))
|
||||
@@ -224,10 +242,19 @@ class CheckoutController < Spree::StoreController
|
||||
end
|
||||
end
|
||||
|
||||
def update_failed
|
||||
current_order.updater.shipping_address_from_distributor
|
||||
RestartCheckout.new(@order).call
|
||||
def update_failed(error = RuntimeError.new(order_error))
|
||||
Bugsnag.notify(error)
|
||||
|
||||
flash[:error] = order_error if flash.empty?
|
||||
checkout_failed
|
||||
update_failed_response
|
||||
end
|
||||
|
||||
def checkout_failed
|
||||
Checkout::PostCheckoutActions.new(@order).failure
|
||||
end
|
||||
|
||||
def update_failed_response
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render :edit
|
||||
|
||||
@@ -63,8 +63,6 @@ class EnterprisesController < BaseController
|
||||
end
|
||||
|
||||
def reset_order
|
||||
distributor = Enterprise.is_distributor.find_by_permalink(params[:id]) ||
|
||||
Enterprise.is_distributor.find(params[:id])
|
||||
order = current_order(true)
|
||||
|
||||
reset_distributor(order, distributor)
|
||||
@@ -74,6 +72,14 @@ class EnterprisesController < BaseController
|
||||
reset_order_cycle(order, distributor)
|
||||
|
||||
order.save!
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
flash[:error] = I18n.t(:enterprise_shop_show_error)
|
||||
redirect_to shops_path
|
||||
end
|
||||
|
||||
def distributor
|
||||
@distributor ||= Enterprise.is_distributor.find_by_permalink(params[:id]) ||
|
||||
Enterprise.is_distributor.find(params[:id])
|
||||
end
|
||||
|
||||
def reset_distributor(order, distributor)
|
||||
@@ -90,8 +96,8 @@ class EnterprisesController < BaseController
|
||||
end
|
||||
|
||||
def reset_order_cycle(order, distributor)
|
||||
order_cycle_options = OrderCycle.active.with_distributor(distributor)
|
||||
order.order_cycle = order_cycle_options.first if order_cycle_options.count == 1
|
||||
order_cycles = Shop::OrderCyclesList.new(distributor, current_customer).call
|
||||
order.order_cycle = order_cycles.first if order_cycles.size == 1
|
||||
end
|
||||
|
||||
def shop_order_cycles
|
||||
|
||||
@@ -5,12 +5,23 @@ class HomeController < BaseController
|
||||
|
||||
def index
|
||||
if ContentConfig.home_show_stats
|
||||
@num_distributors = Enterprise.is_distributor.activated.visible.count
|
||||
@num_producers = Enterprise.is_primary_producer.activated.visible.count
|
||||
@num_users = Spree::Order.complete.count('DISTINCT user_id')
|
||||
@num_orders = Spree::Order.complete.count
|
||||
@num_distributors = cached_count('distributors', Enterprise.is_distributor.activated.visible)
|
||||
@num_producers = cached_count('producers', Enterprise.is_primary_producer.activated.visible)
|
||||
@num_orders = cached_count('orders', Spree::Order.complete)
|
||||
@num_users = cached_count(
|
||||
'users', Spree::Order.complete.select('DISTINCT spree_orders.user_id')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def sell; end
|
||||
|
||||
private
|
||||
|
||||
# Cache the value of the query count for 24 hours
|
||||
def cached_count(key, query)
|
||||
Rails.cache.fetch("home_stats_count_#{key}", expires_in: 1.day, race_condition_ttl: 10) do
|
||||
query.count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,7 @@ class ProducersController < BaseController
|
||||
.activated
|
||||
.visible
|
||||
.is_primary_producer
|
||||
.includes(address: :state)
|
||||
.includes(address: [:state, :country])
|
||||
.includes(:properties)
|
||||
.includes(supplied_products: :properties)
|
||||
.all
|
||||
|
||||
@@ -4,13 +4,6 @@ class ShopsController < BaseController
|
||||
before_filter :enable_embedded_shopfront
|
||||
|
||||
def index
|
||||
@enterprises = Enterprise
|
||||
.activated
|
||||
.visible
|
||||
.is_distributor
|
||||
.includes(address: :state)
|
||||
.includes(:properties)
|
||||
.includes(supplied_products: :properties)
|
||||
.all
|
||||
@enterprises = ShopsListService.new.open_shops
|
||||
end
|
||||
end
|
||||
|
||||
@@ -109,7 +109,10 @@ module Spree
|
||||
private
|
||||
|
||||
def load_order
|
||||
@order = Order.find_by_number!(params[:id], include: :adjustments) if params[:id]
|
||||
if params[:id]
|
||||
@order = Order.includes(:adjustments, :shipments, line_items: :adjustments).
|
||||
find_by_number!(params[:id])
|
||||
end
|
||||
authorize! action, @order
|
||||
end
|
||||
|
||||
@@ -128,7 +131,7 @@ module Spree
|
||||
def load_distribution_choices
|
||||
@shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name
|
||||
|
||||
ocs = OrderCycle.managed_by(spree_current_user)
|
||||
ocs = OrderCycle.includes(:suppliers, :distributors).managed_by(spree_current_user)
|
||||
@order_cycles = ocs.soonest_closing +
|
||||
ocs.soonest_opening +
|
||||
ocs.closed +
|
||||
|
||||
@@ -110,7 +110,7 @@ module Spree
|
||||
else
|
||||
Gateway.providers.reject{ |p| p.name.include? "Bogus" }.sort_by(&:name)
|
||||
end
|
||||
@providers.reject!{ |p| p.name.ends_with? "StripeConnect" } unless show_stripe?
|
||||
@providers.reject!{ |provider| stripe_provider?(provider) } unless show_stripe?
|
||||
@calculators = PaymentMethod.calculators.sort_by(&:name)
|
||||
end
|
||||
|
||||
@@ -134,12 +134,12 @@ module Spree
|
||||
# current payment_method is already a Stripe method
|
||||
def show_stripe?
|
||||
Spree::Config.stripe_connect_enabled ||
|
||||
@payment_method.try(:type) == "Spree::Gateway::StripeConnect"
|
||||
stripe_payment_method?
|
||||
end
|
||||
|
||||
def restrict_stripe_account_change
|
||||
return unless @payment_method
|
||||
return unless @payment_method.type == "Spree::Gateway::StripeConnect"
|
||||
return unless stripe_payment_method?
|
||||
return unless @payment_method.preferred_enterprise_id.andand > 0
|
||||
|
||||
@stripe_account_holder = Enterprise.find(@payment_method.preferred_enterprise_id)
|
||||
@@ -147,6 +147,15 @@ module Spree
|
||||
|
||||
params[:payment_method][:preferred_enterprise_id] = @stripe_account_holder.id
|
||||
end
|
||||
|
||||
def stripe_payment_method?
|
||||
["Spree::Gateway::StripeConnect",
|
||||
"Spree::Gateway::StripeSCA"].include? @payment_method.try(:type)
|
||||
end
|
||||
|
||||
def stripe_provider?(provider)
|
||||
provider.name.ends_with?("StripeConnect", "StripeSCA")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -59,7 +59,7 @@ module Spree
|
||||
@collection ||= if @deleted.blank?
|
||||
super
|
||||
else
|
||||
Variant.where(product_id: parent.id).deleted
|
||||
Variant.unscoped.where(product_id: parent.id).deleted
|
||||
end
|
||||
@collection
|
||||
end
|
||||
|
||||
@@ -10,10 +10,14 @@ module Spree
|
||||
render json: @credit_card, serializer: ::Api::CreditCardSerializer, status: :ok
|
||||
else
|
||||
message = t(:card_could_not_be_saved)
|
||||
render json: { flash: { error: I18n.t(:spree_gateway_error_flash_for_checkout, error: message) } }, status: :bad_request
|
||||
render json: { flash: { error: I18n.t(:spree_gateway_error_flash_for_checkout,
|
||||
error: message) } },
|
||||
status: :bad_request
|
||||
end
|
||||
rescue Stripe::CardError => e
|
||||
render json: { flash: { error: I18n.t(:spree_gateway_error_flash_for_checkout, error: e.message) } }, status: :bad_request
|
||||
render json: { flash: { error: I18n.t(:spree_gateway_error_flash_for_checkout,
|
||||
error: e.message) } },
|
||||
status: :bad_request
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -52,12 +56,20 @@ module Spree
|
||||
|
||||
private
|
||||
|
||||
# Currently can only destroy the whole customer object
|
||||
# It destroys the whole customer object
|
||||
def destroy_at_stripe
|
||||
stripe_customer = Stripe::Customer.retrieve(@credit_card.gateway_customer_profile_id)
|
||||
stripe_customer = Stripe::Customer.retrieve(@credit_card.gateway_customer_profile_id, {})
|
||||
|
||||
stripe_customer.delete if stripe_customer
|
||||
end
|
||||
|
||||
def stripe_account_id
|
||||
StripeAccount.
|
||||
find_by_enterprise_id(@credit_card.payment_method.preferred_enterprise_id).
|
||||
andand.
|
||||
stripe_user_id
|
||||
end
|
||||
|
||||
def create_customer(token)
|
||||
Stripe::Customer.create(email: spree_current_user.email, source: token)
|
||||
end
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
module ApplicationHelper
|
||||
include FoundationRailsHelper::FlashHelper
|
||||
|
||||
def feature?(feature)
|
||||
OpenFoodNetwork::FeatureToggle.enabled? feature
|
||||
end
|
||||
|
||||
@@ -3,10 +3,10 @@ require 'open_food_network/enterprise_injection_data'
|
||||
module InjectionHelper
|
||||
include SerializerHelper
|
||||
|
||||
def inject_enterprises(enterprises = Enterprise.activated.includes(address: :state).all)
|
||||
def inject_enterprises(enterprises = nil)
|
||||
inject_json_ams(
|
||||
"enterprises",
|
||||
enterprises,
|
||||
enterprises || default_enterprise_query,
|
||||
Api::EnterpriseSerializer,
|
||||
enterprise_injection_data
|
||||
)
|
||||
@@ -17,7 +17,10 @@ module InjectionHelper
|
||||
|
||||
inject_json_ams(
|
||||
"groups",
|
||||
EnterpriseGroup.on_front_page.by_position.select(select_only).includes(address: :state).all,
|
||||
EnterpriseGroup.on_front_page.by_position.select(select_only).
|
||||
includes(enterprises: [:shipping_methods, { address: [:state, :country] }],
|
||||
address: :state).
|
||||
all,
|
||||
Api::GroupListSerializer
|
||||
)
|
||||
end
|
||||
@@ -35,13 +38,21 @@ module InjectionHelper
|
||||
|
||||
inject_json_ams(
|
||||
"enterprises",
|
||||
Enterprise.activated.visible.select(select_only).includes(address: :state).all,
|
||||
Enterprise.activated.visible.select(select_only).includes(address: [:state, :country]).all,
|
||||
Api::EnterpriseShopfrontListSerializer
|
||||
)
|
||||
end
|
||||
|
||||
def inject_enterprise_and_relatives
|
||||
inject_json_ams "enterprises", current_distributor.relatives_including_self.activated.includes(address: :state).all, Api::EnterpriseSerializer, enterprise_injection_data
|
||||
enterprises_and_relatives = current_distributor.
|
||||
relatives_including_self.
|
||||
activated.
|
||||
includes(address: [:state, :country]).
|
||||
all
|
||||
|
||||
inject_json_ams "enterprises",
|
||||
enterprises_and_relatives,
|
||||
Api::EnterpriseSerializer, enterprise_injection_data
|
||||
end
|
||||
|
||||
def inject_group_enterprises
|
||||
@@ -138,6 +149,10 @@ module InjectionHelper
|
||||
|
||||
private
|
||||
|
||||
def default_enterprise_query
|
||||
Enterprise.activated.includes(address: [:state, :country]).all
|
||||
end
|
||||
|
||||
def enterprise_injection_data
|
||||
@enterprise_injection_data ||= OpenFoodNetwork::EnterpriseInjectionData.new
|
||||
{ data: @enterprise_injection_data }
|
||||
|
||||
@@ -47,17 +47,6 @@ module OrderCyclesHelper
|
||||
end
|
||||
end
|
||||
|
||||
def order_cycle_options
|
||||
@order_cycles.
|
||||
with_distributor(current_distributor).
|
||||
map { |oc| [order_cycle_close_to_s(oc.orders_close_at), oc.id] }
|
||||
end
|
||||
|
||||
def order_cycle_close_to_s(orders_close_at)
|
||||
"%s (%s)" % [orders_close_at.strftime("#{orders_close_at.day.ordinalize} %b"),
|
||||
distance_of_time_in_words_to_now(orders_close_at)]
|
||||
end
|
||||
|
||||
def active_order_cycle_for_distributor?(_distributor)
|
||||
OrderCycle.active.with_distributor(@distributor).present?
|
||||
end
|
||||
|
||||
@@ -16,4 +16,8 @@ module SharedHelper
|
||||
def admin_user?
|
||||
spree_current_user.andand.has_spree_role? 'admin'
|
||||
end
|
||||
|
||||
def current_shop_products_path
|
||||
"#{main_app.enterprise_shop_path(current_distributor)}#/shop"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,6 +8,10 @@ module ShopHelper
|
||||
end
|
||||
end
|
||||
|
||||
def oc_select_options
|
||||
@order_cycles.map { |oc| { time: pickup_time(oc), id: oc.id } }
|
||||
end
|
||||
|
||||
def require_customer?
|
||||
current_distributor.require_login? && !user_is_related_to_distributor?
|
||||
end
|
||||
@@ -31,6 +35,10 @@ module ShopHelper
|
||||
].select{ |tab| tab[:show] }
|
||||
end
|
||||
|
||||
def shop_tab_names
|
||||
shop_tabs.map { |tab| tab[:name] }
|
||||
end
|
||||
|
||||
def show_home_tab?
|
||||
require_customer? || current_distributor.preferred_shopfront_message.present?
|
||||
end
|
||||
|
||||
@@ -5,5 +5,21 @@ module Spree
|
||||
def variant_options(v, _options = {})
|
||||
v.options_text
|
||||
end
|
||||
|
||||
# Overriden to eager-load :states
|
||||
def available_countries
|
||||
checkout_zone = Zone.find_by_name(Spree::Config[:checkout_zone])
|
||||
|
||||
countries = if checkout_zone && checkout_zone.kind == 'country'
|
||||
checkout_zone.country_list
|
||||
else
|
||||
Country.includes(:states).all
|
||||
end
|
||||
|
||||
countries.collect do |country|
|
||||
country.name = Spree.t(country.iso, scope: 'country_names', default: country.name)
|
||||
country
|
||||
end.sort { |a, b| a.name <=> b.name }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
require 'open_food_network/subscription_payment_updater'
|
||||
require 'open_food_network/subscription_summarizer'
|
||||
require 'order_management/subscriptions/summarizer'
|
||||
|
||||
# Confirms orders of unconfirmed proxy orders in recently closed Order Cycles
|
||||
class SubscriptionConfirmJob
|
||||
def perform
|
||||
ids = proxy_orders.pluck(:id)
|
||||
proxy_orders.update_all(confirmed_at: Time.zone.now)
|
||||
ProxyOrder.where(id: ids).each do |proxy_order|
|
||||
@order = proxy_order.order
|
||||
process!
|
||||
end
|
||||
|
||||
send_confirmation_summary_emails
|
||||
confirm_proxy_orders!
|
||||
end
|
||||
|
||||
private
|
||||
@@ -19,10 +12,26 @@ class SubscriptionConfirmJob
|
||||
delegate :record_and_log_error, :send_confirmation_summary_emails, to: :summarizer
|
||||
|
||||
def summarizer
|
||||
@summarizer ||= OpenFoodNetwork::SubscriptionSummarizer.new
|
||||
@summarizer ||= OrderManagement::Subscriptions::Summarizer.new
|
||||
end
|
||||
|
||||
def proxy_orders
|
||||
def confirm_proxy_orders!
|
||||
# Fetch all unconfirmed proxy orders
|
||||
unconfirmed_proxy_orders_ids = unconfirmed_proxy_orders.pluck(:id)
|
||||
|
||||
# Mark these proxy orders as confirmed
|
||||
unconfirmed_proxy_orders.update_all(confirmed_at: Time.zone.now)
|
||||
|
||||
# Confirm these proxy orders
|
||||
ProxyOrder.where(id: unconfirmed_proxy_orders_ids).each do |proxy_order|
|
||||
Rails.logger.info "Confirming Order for Proxy Order #{proxy_order.id}"
|
||||
confirm_order!(proxy_order.order)
|
||||
end
|
||||
|
||||
send_confirmation_summary_emails
|
||||
end
|
||||
|
||||
def unconfirmed_proxy_orders
|
||||
ProxyOrder.not_canceled.where('confirmed_at IS NULL AND placed_at IS NOT NULL')
|
||||
.joins(:order_cycle).merge(recently_closed_order_cycles)
|
||||
.joins(:order).merge(Spree::Order.complete.not_state('canceled'))
|
||||
@@ -32,30 +41,43 @@ class SubscriptionConfirmJob
|
||||
OrderCycle.closed.where('order_cycles.orders_close_at BETWEEN (?) AND (?) OR order_cycles.updated_at BETWEEN (?) AND (?)', 1.hour.ago, Time.zone.now, 1.hour.ago, Time.zone.now)
|
||||
end
|
||||
|
||||
def process!
|
||||
record_order(@order)
|
||||
update_payment! if @order.payment_required?
|
||||
return send_failed_payment_email if @order.errors.present?
|
||||
# It sets up payments, processes payments and sends confirmation emails
|
||||
def confirm_order!(order)
|
||||
record_order(order)
|
||||
|
||||
@order.process_payments! if @order.payment_required?
|
||||
return send_failed_payment_email if @order.errors.present?
|
||||
|
||||
send_confirm_email
|
||||
if process_payment!(order)
|
||||
send_confirmation_email(order)
|
||||
else
|
||||
send_failed_payment_email(order)
|
||||
end
|
||||
end
|
||||
|
||||
def update_payment!
|
||||
OpenFoodNetwork::SubscriptionPaymentUpdater.new(@order).update!
|
||||
def process_payment!(order)
|
||||
return false if order.errors.present?
|
||||
return true unless order.payment_required?
|
||||
|
||||
setup_payment!(order)
|
||||
return false if order.errors.present?
|
||||
|
||||
order.process_payments!
|
||||
return false if order.errors.present?
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def send_confirm_email
|
||||
@order.update!
|
||||
record_success(@order)
|
||||
SubscriptionMailer.confirmation_email(@order).deliver
|
||||
def setup_payment!(order)
|
||||
OrderManagement::Subscriptions::PaymentSetup.new(order).call!
|
||||
end
|
||||
|
||||
def send_failed_payment_email
|
||||
@order.update!
|
||||
record_and_log_error(:failed_payment, @order)
|
||||
SubscriptionMailer.failed_payment_email(@order).deliver
|
||||
def send_confirmation_email(order)
|
||||
order.update!
|
||||
record_success(order)
|
||||
SubscriptionMailer.confirmation_email(order).deliver
|
||||
end
|
||||
|
||||
def send_failed_payment_email(order)
|
||||
order.update!
|
||||
record_and_log_error(:failed_payment, order)
|
||||
SubscriptionMailer.failed_payment_email(order).deliver
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
require 'open_food_network/subscription_summarizer'
|
||||
require 'order_management/subscriptions/summarizer'
|
||||
|
||||
class SubscriptionPlacementJob
|
||||
def perform
|
||||
ids = proxy_orders.pluck(:id)
|
||||
proxy_orders.update_all(placed_at: Time.zone.now)
|
||||
ProxyOrder.where(id: ids).each do |proxy_order|
|
||||
proxy_order.initialise_order!
|
||||
process(proxy_order.order)
|
||||
place_order_for(proxy_order)
|
||||
end
|
||||
|
||||
send_placement_summary_emails
|
||||
@@ -18,7 +17,7 @@ class SubscriptionPlacementJob
|
||||
delegate :record_and_log_error, :send_placement_summary_emails, to: :summarizer
|
||||
|
||||
def summarizer
|
||||
@summarizer ||= OpenFoodNetwork::SubscriptionSummarizer.new
|
||||
@summarizer ||= OrderManagement::Subscriptions::Summarizer.new
|
||||
end
|
||||
|
||||
def proxy_orders
|
||||
@@ -28,16 +27,18 @@ class SubscriptionPlacementJob
|
||||
.joins(:subscription).merge(Subscription.not_canceled.not_paused)
|
||||
end
|
||||
|
||||
def process(order)
|
||||
def place_order_for(proxy_order)
|
||||
Rails.logger.info "Placing Order for Proxy Order #{proxy_order.id}"
|
||||
proxy_order.initialise_order!
|
||||
place_order(proxy_order.order)
|
||||
end
|
||||
|
||||
def place_order(order)
|
||||
record_order(order)
|
||||
return record_issue(:complete, order) if order.completed?
|
||||
|
||||
changes = cap_quantity_and_store_changes(order)
|
||||
if order.line_items.where('quantity > 0').empty?
|
||||
order.reload.adjustments.destroy_all
|
||||
order.update!
|
||||
return send_empty_email(order, changes)
|
||||
end
|
||||
return handle_empty_order(order, changes) if order.line_items.where('quantity > 0').empty?
|
||||
|
||||
move_to_completion(order)
|
||||
send_placement_email(order, changes)
|
||||
@@ -58,12 +59,18 @@ class SubscriptionPlacementJob
|
||||
changes
|
||||
end
|
||||
|
||||
def handle_empty_order(order, changes)
|
||||
order.reload.adjustments.destroy_all
|
||||
order.update!
|
||||
send_empty_email(order, changes)
|
||||
end
|
||||
|
||||
def move_to_completion(order)
|
||||
AdvanceOrderService.new(order).call!
|
||||
end
|
||||
|
||||
def unavailable_stock_lines_for(order)
|
||||
order.line_items.where('variant_id NOT IN (?)', available_variants_for(order))
|
||||
order.line_items.where('variant_id NOT IN (?)', available_variants_for(order).select(&:id))
|
||||
end
|
||||
|
||||
def available_variants_for(order)
|
||||
|
||||
@@ -54,6 +54,8 @@ module Calculator
|
||||
# Customer ends up getting 350mL (line_item.final_weight_volume) of wine
|
||||
# that represent 2.8 (quantity_implied_in_final_weight_volume) glasses of wine
|
||||
def quantity_implied_in_final_weight_volume(line_item)
|
||||
return line_item.quantity if line_item.variant.unit_value.to_f.zero?
|
||||
|
||||
(1.0 * line_item.final_weight_volume / line_item.variant.unit_value).round(3)
|
||||
end
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ class Enterprise < ActiveRecord::Base
|
||||
def distributed_taxons
|
||||
Spree::Taxon.
|
||||
joins(:products).
|
||||
where('spree_products.id IN (?)', Spree::Product.in_distributor(self)).
|
||||
where('spree_products.id IN (?)', Spree::Product.in_distributor(self).select(&:id)).
|
||||
select('DISTINCT spree_taxons.*')
|
||||
end
|
||||
|
||||
@@ -337,7 +337,7 @@ class Enterprise < ActiveRecord::Base
|
||||
def supplied_taxons
|
||||
Spree::Taxon.
|
||||
joins(:products).
|
||||
where('spree_products.id IN (?)', Spree::Product.in_supplier(self)).
|
||||
where('spree_products.id IN (?)', Spree::Product.in_supplier(self).select(&:id)).
|
||||
select('DISTINCT spree_taxons.*')
|
||||
end
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class EnterpriseFee < ActiveRecord::Base
|
||||
if user.has_spree_role?('admin')
|
||||
scoped
|
||||
else
|
||||
where('enterprise_id IN (?)', user.enterprises)
|
||||
where('enterprise_id IN (?)', user.enterprises.select(&:id))
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class EnterpriseRelationship < ActiveRecord::Base
|
||||
}
|
||||
|
||||
scope :involving_enterprises, ->(enterprises) {
|
||||
where('parent_id IN (?) OR child_id IN (?)', enterprises, enterprises)
|
||||
where('parent_id IN (?) OR child_id IN (?)', enterprises.select(&:id), enterprises.select(&:id))
|
||||
}
|
||||
|
||||
scope :permitting, ->(enterprise_ids) { where('child_id IN (?)', enterprise_ids) }
|
||||
|
||||
@@ -49,7 +49,7 @@ class Exchange < ActiveRecord::Base
|
||||
}
|
||||
scope :with_product, lambda { |product|
|
||||
joins(:exchange_variants).
|
||||
where('exchange_variants.variant_id IN (?)', product.variants_including_master)
|
||||
where('exchange_variants.variant_id IN (?)', product.variants_including_master.select(&:id))
|
||||
}
|
||||
scope :by_enterprise_name, -> {
|
||||
joins('INNER JOIN enterprises AS sender ON (sender.id = exchanges.sender_id)').
|
||||
|
||||
@@ -17,6 +17,7 @@ class OrderCycle < ActiveRecord::Base
|
||||
has_many :distributors, source: :receiver, through: :cached_outgoing_exchanges, uniq: true
|
||||
|
||||
has_and_belongs_to_many :schedules, join_table: 'order_cycle_schedules'
|
||||
has_paper_trail meta: { custom_data: proc { |order_cycle| order_cycle.schedule_ids.to_s } }
|
||||
|
||||
attr_accessor :incoming_exchanges, :outgoing_exchanges
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ module ProductImport
|
||||
if settings.importing_into_inventory?
|
||||
InventoryResetStrategy
|
||||
else
|
||||
ProductsResetStrategy
|
||||
Catalog::ProductImport::ProductsResetStrategy
|
||||
end
|
||||
end
|
||||
|
||||
@@ -122,7 +122,6 @@ module ProductImport
|
||||
|
||||
def save_new_inventory_item(entry)
|
||||
new_item = entry.product_object
|
||||
assign_defaults(new_item, entry)
|
||||
new_item.import_date = @import_time
|
||||
|
||||
if new_item.valid? && new_item.save
|
||||
@@ -136,7 +135,6 @@ module ProductImport
|
||||
|
||||
def save_existing_inventory_item(entry)
|
||||
existing_item = entry.product_object
|
||||
assign_defaults(existing_item, entry)
|
||||
existing_item.import_date = @import_time
|
||||
|
||||
if existing_item.valid? && existing_item.save
|
||||
@@ -164,7 +162,6 @@ module ProductImport
|
||||
product = Spree::Product.new
|
||||
product.assign_attributes(entry.attributes.except('id', 'on_hand', 'on_demand'))
|
||||
product.supplier_id = entry.producer_id
|
||||
assign_defaults(product, entry)
|
||||
|
||||
if product.save
|
||||
ensure_variant_updated(product, entry)
|
||||
@@ -179,7 +176,6 @@ module ProductImport
|
||||
|
||||
def save_variant(entry)
|
||||
variant = entry.product_object
|
||||
assign_defaults(variant, entry)
|
||||
variant.import_date = @import_time
|
||||
|
||||
if variant.valid? && variant.save
|
||||
@@ -199,37 +195,6 @@ module ProductImport
|
||||
)
|
||||
end
|
||||
|
||||
def assign_defaults(object, entry)
|
||||
# Assigns a default value for a specified field e.g. category='Vegetables', setting this value
|
||||
# either for all entries (overwrite_all), or only for those entries where the field was blank
|
||||
# in the spreadsheet (overwrite_empty), depending on selected import settings
|
||||
return unless settings.defaults(entry)
|
||||
|
||||
settings.defaults(entry).each do |attribute, setting|
|
||||
next unless setting['active']
|
||||
|
||||
case setting['mode']
|
||||
when 'overwrite_all'
|
||||
object.assign_attributes(attribute => setting['value'])
|
||||
# In case of new products, some attributes are saved on the variant.
|
||||
# We write them to the entry here to be copied to the variant later.
|
||||
if entry.respond_to? "#{attribute}="
|
||||
entry.public_send("#{attribute}=", setting['value'])
|
||||
end
|
||||
when 'overwrite_empty'
|
||||
if object.public_send(attribute).blank? ||
|
||||
((attribute == 'on_hand') &&
|
||||
entry.on_hand_nil)
|
||||
|
||||
object.assign_attributes(attribute => setting['value'])
|
||||
if entry.respond_to? "#{attribute}="
|
||||
entry.public_send("#{attribute}=", setting['value'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def display_in_inventory(variant_override, is_new = false)
|
||||
unless is_new
|
||||
existing_item = InventoryItem.where(
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
module ProductImport
|
||||
class ProductsResetStrategy
|
||||
def initialize(excluded_items_ids)
|
||||
@excluded_items_ids = excluded_items_ids
|
||||
end
|
||||
|
||||
def reset(enterprise_ids)
|
||||
@enterprise_ids = enterprise_ids
|
||||
|
||||
return 0 if enterprise_ids.blank?
|
||||
|
||||
reset_variants_on_hand(enterprise_variants_relation)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :excluded_items_ids, :enterprise_ids
|
||||
|
||||
def enterprise_variants_relation
|
||||
relation = Spree::Variant
|
||||
.joins(:product)
|
||||
.where(
|
||||
spree_products: { supplier_id: enterprise_ids },
|
||||
spree_variants: { is_master: false, deleted_at: nil }
|
||||
)
|
||||
|
||||
return relation if excluded_items_ids.blank?
|
||||
|
||||
relation.where('spree_variants.id NOT IN (?)', excluded_items_ids)
|
||||
end
|
||||
|
||||
def reset_variants_on_hand(variants)
|
||||
updated_records_count = 0
|
||||
variants.each do |variant|
|
||||
updated_records_count += 1 if reset_variant_on_hand(variant)
|
||||
end
|
||||
updated_records_count
|
||||
end
|
||||
|
||||
def reset_variant_on_hand(variant)
|
||||
variant.on_hand = 0
|
||||
variant.on_hand.zero?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,7 @@
|
||||
class Schedule < ActiveRecord::Base
|
||||
has_and_belongs_to_many :order_cycles, join_table: 'order_cycle_schedules'
|
||||
has_paper_trail meta: { custom_data: proc { |schedule| schedule.order_cycle_ids.to_s } }
|
||||
|
||||
has_many :coordinators, uniq: true, through: :order_cycles
|
||||
|
||||
attr_accessible :name, :order_cycle_ids
|
||||
|
||||
@@ -13,14 +13,15 @@ module Spree
|
||||
def compute(object = nil)
|
||||
return 0 if object.nil?
|
||||
|
||||
preferred_amount * line_items_for(object).reduce(0) do |sum, value|
|
||||
value_to_add = if matching_products.blank? || matching_products.include?(value.product)
|
||||
value.quantity
|
||||
number_of_line_items = line_items_for(object).reduce(0) do |sum, line_item|
|
||||
value_to_add = if matching_products.blank? || matching_products.include?(line_item.product)
|
||||
line_item.quantity
|
||||
else
|
||||
0
|
||||
end
|
||||
sum + value_to_add
|
||||
end
|
||||
preferred_amount * number_of_line_items
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,12 +9,6 @@ module Spree
|
||||
|
||||
attr_accessible :preferred_enterprise_id
|
||||
|
||||
CARD_TYPE_MAPPING = {
|
||||
'American Express' => 'american_express',
|
||||
'Diners Club' => 'diners_club',
|
||||
'Visa' => 'visa'
|
||||
}.freeze
|
||||
|
||||
def method_type
|
||||
'stripe'
|
||||
end
|
||||
@@ -77,11 +71,6 @@ module Spree
|
||||
[money, creditcard, options]
|
||||
end
|
||||
|
||||
def update_source!(source)
|
||||
source.cc_type = CARD_TYPE_MAPPING[source.cc_type] if CARD_TYPE_MAPPING.include?(source.cc_type)
|
||||
source
|
||||
end
|
||||
|
||||
def token_from_card_profile_ids(creditcard)
|
||||
token_or_card_id = creditcard.gateway_payment_profile_id
|
||||
customer = creditcard.gateway_customer_profile_id
|
||||
|
||||
139
app/models/spree/gateway/stripe_sca.rb
Normal file
139
app/models/spree/gateway/stripe_sca.rb
Normal file
@@ -0,0 +1,139 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'stripe/profile_storer'
|
||||
require 'stripe/credit_card_cloner'
|
||||
require 'stripe/authorize_response_patcher'
|
||||
require 'stripe/payment_intent_validator'
|
||||
require 'active_merchant/billing/gateways/stripe_payment_intents'
|
||||
require 'active_merchant/billing/gateways/stripe_decorator'
|
||||
|
||||
module Spree
|
||||
class Gateway
|
||||
class StripeSCA < Gateway
|
||||
preference :enterprise_id, :integer
|
||||
|
||||
validate :ensure_enterprise_selected
|
||||
|
||||
attr_accessible :preferred_enterprise_id
|
||||
|
||||
def method_type
|
||||
'stripe_sca'
|
||||
end
|
||||
|
||||
def provider_class
|
||||
ActiveMerchant::Billing::StripePaymentIntentsGateway
|
||||
end
|
||||
|
||||
def payment_profiles_supported?
|
||||
true
|
||||
end
|
||||
|
||||
def stripe_account_id
|
||||
StripeAccount.find_by_enterprise_id(preferred_enterprise_id).andand.stripe_user_id
|
||||
end
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
def purchase(money, creditcard, gateway_options)
|
||||
begin
|
||||
payment_intent_id = fetch_payment_intent(creditcard, gateway_options)
|
||||
rescue Stripe::StripeError => e
|
||||
return failed_activemerchant_billing_response(e.message)
|
||||
end
|
||||
|
||||
options = basic_options(gateway_options)
|
||||
options[:customer] = creditcard.gateway_customer_profile_id
|
||||
provider.capture(money, payment_intent_id, options)
|
||||
rescue Stripe::StripeError => e
|
||||
failed_activemerchant_billing_response(e.message)
|
||||
end
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
def authorize(money, creditcard, gateway_options)
|
||||
authorize_response = provider.authorize(*options_for_authorize(money,
|
||||
creditcard,
|
||||
gateway_options))
|
||||
Stripe::AuthorizeResponsePatcher.new(authorize_response).call!
|
||||
rescue Stripe::StripeError => e
|
||||
failed_activemerchant_billing_response(e.message)
|
||||
end
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
def void(response_code, _creditcard, gateway_options)
|
||||
gateway_options[:stripe_account] = stripe_account_id
|
||||
provider.void(response_code, gateway_options)
|
||||
end
|
||||
|
||||
# NOTE: the name of this method is determined by Spree::Payment::Processing
|
||||
def credit(money, _creditcard, response_code, gateway_options)
|
||||
gateway_options[:stripe_account] = stripe_account_id
|
||||
provider.refund(money, response_code, gateway_options)
|
||||
end
|
||||
|
||||
def create_profile(payment)
|
||||
return unless payment.source.gateway_customer_profile_id.nil?
|
||||
|
||||
profile_storer = Stripe::ProfileStorer.new(payment, provider)
|
||||
profile_storer.create_customer_from_token
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# In this gateway, what we call 'secret_key' is the 'login'
|
||||
def options
|
||||
options = super
|
||||
options.merge(login: Stripe.api_key)
|
||||
end
|
||||
|
||||
def basic_options(gateway_options)
|
||||
options = {}
|
||||
options[:description] = "Spree Order ID: #{gateway_options[:order_id]}"
|
||||
options[:currency] = gateway_options[:currency]
|
||||
options[:stripe_account] = stripe_account_id
|
||||
options
|
||||
end
|
||||
|
||||
def options_for_authorize(money, creditcard, gateway_options)
|
||||
options = basic_options(gateway_options)
|
||||
options[:return_url] = full_checkout_path
|
||||
|
||||
customer_id, payment_method_id = Stripe::CreditCardCloner.new.clone(creditcard,
|
||||
stripe_account_id)
|
||||
options[:customer] = customer_id
|
||||
[money, payment_method_id, options]
|
||||
end
|
||||
|
||||
def fetch_payment_intent(creditcard, gateway_options)
|
||||
payment = fetch_payment(creditcard, gateway_options)
|
||||
raise Stripe::StripeError, I18n.t(:no_pending_payments) unless payment&.response_code
|
||||
|
||||
Stripe::PaymentIntentValidator.new.call(payment.response_code, stripe_account_id)
|
||||
end
|
||||
|
||||
def fetch_payment(creditcard, gateway_options)
|
||||
order_number = gateway_options[:order_id].split('-').first
|
||||
|
||||
Spree::Order.find_by_number(order_number).payments.merge(creditcard.payments).last
|
||||
end
|
||||
|
||||
def failed_activemerchant_billing_response(error_message)
|
||||
ActiveMerchant::Billing::Response.new(false, error_message)
|
||||
end
|
||||
|
||||
def ensure_enterprise_selected
|
||||
return if preferred_enterprise_id.andand.positive?
|
||||
|
||||
errors.add(:stripe_account_owner, I18n.t(:error_required))
|
||||
end
|
||||
|
||||
def full_checkout_path
|
||||
URI.join(url_helpers.root_url, url_helpers.checkout_path).to_s
|
||||
end
|
||||
|
||||
def url_helpers
|
||||
# This is how we can get the helpers with a usable root_url outside the controllers
|
||||
Rails.application.routes.default_url_options = ActionMailer::Base.default_url_options
|
||||
Rails.application.routes.url_helpers
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -36,6 +36,10 @@ Spree::LineItem.class_eval do
|
||||
end
|
||||
}
|
||||
|
||||
scope :in_orders, lambda { |orders|
|
||||
where(order_id: orders)
|
||||
}
|
||||
|
||||
# Find line items that are from order sorted by variant name and unit value
|
||||
scope :sorted_by_name_and_unit_value, -> {
|
||||
joins(variant: :product).
|
||||
|
||||
@@ -56,7 +56,9 @@ Spree::Order.class_eval do
|
||||
# Find orders that are distributed by the user or have products supplied by the user
|
||||
# WARNING: This only filters orders, you'll need to filter line items separately using LineItem.managed_by
|
||||
with_line_items_variants_and_products_outer.
|
||||
where('spree_orders.distributor_id IN (?) OR spree_products.supplier_id IN (?)', user.enterprises, user.enterprises).
|
||||
where('spree_orders.distributor_id IN (?) OR spree_products.supplier_id IN (?)',
|
||||
user.enterprises.select(&:id),
|
||||
user.enterprises.select(&:id)).
|
||||
select('DISTINCT spree_orders.*')
|
||||
end
|
||||
}
|
||||
@@ -65,7 +67,7 @@ Spree::Order.class_eval do
|
||||
if user.has_spree_role?('admin')
|
||||
scoped
|
||||
else
|
||||
where('spree_orders.distributor_id IN (?)', user.enterprises)
|
||||
where('spree_orders.distributor_id IN (?)', user.enterprises.select(&:id))
|
||||
end
|
||||
}
|
||||
|
||||
@@ -115,6 +117,12 @@ Spree::Order.class_eval do
|
||||
end
|
||||
end
|
||||
|
||||
# "Checkout" is the initial state and, for card payments, "pending" is the state after authorization
|
||||
# These are both valid states to process the payment
|
||||
def pending_payments
|
||||
(payments.select(&:pending?) + payments.select(&:processing?) + payments.select(&:checkout?)).uniq
|
||||
end
|
||||
|
||||
def remove_variant(variant)
|
||||
line_items(:reload)
|
||||
current_item = find_line_item_by_variant(variant)
|
||||
|
||||
@@ -20,7 +20,7 @@ Spree::PaymentMethod.class_eval do
|
||||
scoped
|
||||
else
|
||||
joins(:distributors).
|
||||
where('distributors_payment_methods.distributor_id IN (?)', user.enterprises).
|
||||
where('distributors_payment_methods.distributor_id IN (?)', user.enterprises.select(&:id)).
|
||||
select('DISTINCT spree_payment_methods.*')
|
||||
end
|
||||
}
|
||||
@@ -68,6 +68,8 @@ Spree::PaymentMethod.class_eval do
|
||||
"Pin Payments"
|
||||
when "Spree::Gateway::StripeConnect"
|
||||
"Stripe"
|
||||
when "Spree::Gateway::StripeSCA"
|
||||
"Stripe SCA"
|
||||
when "Spree::Gateway::PayPalExpress"
|
||||
"PayPal Express"
|
||||
else
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user