mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-13 18:46:49 +00:00
Compare commits
501 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49de11567b | ||
|
|
3af0365c6b | ||
|
|
be92b0049b | ||
|
|
a1c94d0d9f | ||
|
|
f6bb8a9a04 | ||
|
|
d254df7ccc | ||
|
|
8caf10f634 | ||
|
|
2966dd9536 | ||
|
|
21e1c0ed0b | ||
|
|
48b99d02b9 | ||
|
|
f17a2eeaea | ||
|
|
18419d0276 | ||
|
|
bd19d8b0bd | ||
|
|
4bcd665379 | ||
|
|
e63dbcfa89 | ||
|
|
c3283adcf5 | ||
|
|
b2ed69831b | ||
|
|
7daba62f43 | ||
|
|
a08020490d | ||
|
|
eb4d970bc7 | ||
|
|
7a3549209f | ||
|
|
52ebd1b402 | ||
|
|
a3a26f704f | ||
|
|
cff8f6dd96 | ||
|
|
81537d92cf | ||
|
|
91e88bd028 | ||
|
|
f5e254a105 | ||
|
|
b41b5d0395 | ||
|
|
296d2e5edb | ||
|
|
ac0a62e962 | ||
|
|
2c5db8935b | ||
|
|
24c8f38111 | ||
|
|
b801bffcd9 | ||
|
|
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 | ||
|
|
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 | ||
|
|
129de8fd57 | ||
|
|
e24d858af9 | ||
|
|
d2cacf5330 | ||
|
|
af231d2ebe | ||
|
|
711a3debe7 | ||
|
|
d09b0849e4 | ||
|
|
c639821dc3 | ||
|
|
d9b643d795 | ||
|
|
6b8b3be524 | ||
|
|
1db8283e98 | ||
|
|
5fce40ee71 | ||
|
|
c64493ca77 | ||
|
|
5b942e6933 | ||
|
|
ffbb5934d7 | ||
|
|
bae9df8214 | ||
|
|
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 | ||
|
|
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 | ||
|
|
d280bf0d4d | ||
|
|
4c3916a93d | ||
|
|
677f31ffa8 | ||
|
|
5848a46149 | ||
|
|
03c91dfac1 | ||
|
|
60e12063cd | ||
|
|
e5b57af315 | ||
|
|
a396a7f0af | ||
|
|
6b6cdf07fb | ||
|
|
d084789c56 | ||
|
|
dafac32e70 | ||
|
|
8432dab142 | ||
|
|
0b0263a605 | ||
|
|
6ca39f3aa5 | ||
|
|
234a9ef1b4 | ||
|
|
24a1327805 | ||
|
|
33d77d57f4 | ||
|
|
51dd55c5b9 | ||
|
|
0571b657aa | ||
|
|
e4958baddc | ||
|
|
c985747297 | ||
|
|
17ea2cd510 | ||
|
|
325c427219 | ||
|
|
a3e87d893a | ||
|
|
480a629349 | ||
|
|
03fac6f285 | ||
|
|
f5ffdfc258 | ||
|
|
2108a282c8 | ||
|
|
a3a61967a8 | ||
|
|
404e7c1f37 | ||
|
|
1bdeda4a21 | ||
|
|
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 | ||
|
|
49ba83da6d | ||
|
|
19d1497c4b | ||
|
|
06200c9d3c | ||
|
|
2412658e51 | ||
|
|
0c4f22f847 | ||
|
|
1803ea3c38 | ||
|
|
93fda02e43 | ||
|
|
77958f9afe | ||
|
|
82e402f31a | ||
|
|
03fa3e2269 | ||
|
|
11fbe7d5c9 | ||
|
|
799c1f08de | ||
|
|
6b66458bfd | ||
|
|
3e0a5bac6a | ||
|
|
2c2023df03 | ||
|
|
7306d379a5 | ||
|
|
e15c61d862 | ||
|
|
35aeb98d45 | ||
|
|
d99cba3b6e | ||
|
|
4c6fd96bcc | ||
|
|
1eba17f048 | ||
|
|
ff088c6203 | ||
|
|
a1cb6928db | ||
|
|
40a5b60dcb | ||
|
|
2711736004 | ||
|
|
18c165e893 | ||
|
|
0aaa04295b | ||
|
|
7639e9a38d | ||
|
|
d783bd771f | ||
|
|
9dd9d14107 | ||
|
|
e942266dd7 | ||
|
|
c8f78904d6 | ||
|
|
a982fd1e2b | ||
|
|
7e8b2f6be5 | ||
|
|
921c7bbc3a | ||
|
|
eaff6b0c68 | ||
|
|
e1ab424481 | ||
|
|
00e57c8a55 | ||
|
|
2e74e64e22 | ||
|
|
fc5d623465 | ||
|
|
e59c9720fc | ||
|
|
b25f0007f0 | ||
|
|
65c5cdd52f | ||
|
|
a2873ea553 | ||
|
|
3a593ff255 | ||
|
|
92e1193ffb | ||
|
|
016968dcb9 | ||
|
|
9d8608f210 | ||
|
|
323ca906bc | ||
|
|
c43b34e0fa | ||
|
|
bc7f0e0962 | ||
|
|
cf4f7c562a | ||
|
|
4c7bd4d6a8 | ||
|
|
523b266308 | ||
|
|
212413c8b3 | ||
|
|
b248dc598e | ||
|
|
e7b74b99ba | ||
|
|
89d2750fc4 | ||
|
|
7100111f93 | ||
|
|
3dcb66014e | ||
|
|
06971b7198 | ||
|
|
56f9adc5b7 | ||
|
|
38374a9835 | ||
|
|
8d6a8ee214 | ||
|
|
fec653186a | ||
|
|
ebe7456b66 | ||
|
|
8187669a25 | ||
|
|
a6aa0df53b | ||
|
|
116695b1d9 | ||
|
|
387ac40dc9 | ||
|
|
66320b5055 | ||
|
|
858d2cc6c2 | ||
|
|
43280da187 | ||
|
|
3b399b899c | ||
|
|
01d69c89aa | ||
|
|
a2801e40a2 | ||
|
|
abd4f0b923 | ||
|
|
e1eface5f8 | ||
|
|
5cd14253d0 | ||
|
|
be691df7ac | ||
|
|
7783b28ca2 | ||
|
|
6d51856821 | ||
|
|
890704b75c | ||
|
|
922484b2e7 | ||
|
|
3e7288648b | ||
|
|
f0f537ff8f | ||
|
|
b7f920c4b6 | ||
|
|
e5c9468d09 | ||
|
|
8aed173127 | ||
|
|
61ecca7257 | ||
|
|
7eba657b2f | ||
|
|
784de340d1 | ||
|
|
9191628f29 | ||
|
|
88410b1efd | ||
|
|
afea032361 | ||
|
|
3decb4056c | ||
|
|
8e9b08feca | ||
|
|
694995ea5d | ||
|
|
22de7252d0 | ||
|
|
2acf8e5125 | ||
|
|
e1f61e645d | ||
|
|
687d827ceb | ||
|
|
2885e38113 | ||
|
|
9f3ca58b55 | ||
|
|
ffe3228848 | ||
|
|
834231eb8f | ||
|
|
68f5aabd3f | ||
|
|
7e7ea92833 | ||
|
|
ebf22ceb19 | ||
|
|
c38c7c35bc | ||
|
|
2663f74767 | ||
|
|
b41de52012 | ||
|
|
214eb43122 | ||
|
|
01fc4e0513 | ||
|
|
6ce50a5fa5 | ||
|
|
4fbd2cfa52 | ||
|
|
383b28e170 | ||
|
|
bf55a15f81 | ||
|
|
eb7e6dc5b8 | ||
|
|
139ecfe604 | ||
|
|
43a6798db2 | ||
|
|
06d6579486 | ||
|
|
76df526002 | ||
|
|
06569ea24c | ||
|
|
25431f851b | ||
|
|
bab2420bb3 | ||
|
|
0b2acb3a76 | ||
|
|
27db9e604f | ||
|
|
c4e58ebb9e | ||
|
|
9a0ee254af | ||
|
|
5ce3e1e0d2 | ||
|
|
4b345d928c | ||
|
|
52b1e6c71a | ||
|
|
140e0b9cb1 | ||
|
|
b3f05d1a98 | ||
|
|
9644b145cc | ||
|
|
6f644936b0 | ||
|
|
b86759d7a7 | ||
|
|
9d1e3f0318 | ||
|
|
67adf3c801 | ||
|
|
6e78ae762a | ||
|
|
9535c5647f | ||
|
|
6f8bb793e1 | ||
|
|
2476050f29 | ||
|
|
1cce106977 | ||
|
|
98b55287f1 | ||
|
|
48a75c956f | ||
|
|
bec73adc89 | ||
|
|
23ec66e338 |
@@ -39,7 +39,6 @@ Layout/LineLength:
|
||||
- app/controllers/admin/variant_overrides_controller.rb
|
||||
- app/controllers/api/enterprise_attachment_controller.rb
|
||||
- app/controllers/api/product_images_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/adjustments_controller_decorator.rb
|
||||
- app/controllers/spree/admin/orders_controller_decorator.rb
|
||||
- app/controllers/spree/credit_cards_controller.rb
|
||||
@@ -354,7 +353,6 @@ Metrics/AbcSize:
|
||||
- app/controllers/api/variants_controller.rb
|
||||
- app/controllers/base_controller.rb
|
||||
- app/controllers/cart_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/discourse_sso_controller.rb
|
||||
- app/controllers/enterprises_controller.rb
|
||||
- app/controllers/spree/admin/adjustments_controller_decorator.rb
|
||||
@@ -371,7 +369,6 @@ Metrics/AbcSize:
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/admin/users_controller.rb
|
||||
- app/controllers/spree/admin/variants_controller.rb
|
||||
- app/controllers/spree/checkout_controller.rb
|
||||
- app/controllers/spree/credit_cards_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/controllers/spree/user_passwords_controller.rb
|
||||
@@ -389,7 +386,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
|
||||
@@ -416,6 +412,8 @@ Metrics/AbcSize:
|
||||
- app/services/create_order_cycle.rb
|
||||
- app/services/order_syncer.rb
|
||||
- app/services/subscription_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
|
||||
@@ -493,7 +491,6 @@ Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
- app/controllers/admin/enterprise_fees_controller.rb
|
||||
- app/controllers/admin/enterprises_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/helpers/checkout_helper.rb
|
||||
@@ -510,6 +507,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
|
||||
@@ -523,7 +521,6 @@ Metrics/PerceivedComplexity:
|
||||
Exclude:
|
||||
- app/controllers/admin/enterprises_controller.rb
|
||||
- app/controllers/api/variants_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/spree/admin/taxons_controller.rb
|
||||
- app/controllers/spree/orders_controller.rb
|
||||
- app/helpers/checkout_helper.rb
|
||||
@@ -536,6 +533,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
|
||||
@@ -560,7 +558,6 @@ Metrics/MethodLength:
|
||||
- app/controllers/api/variants_controller.rb
|
||||
- app/controllers/base_controller.rb
|
||||
- app/controllers/cart_controller.rb
|
||||
- app/controllers/checkout_controller.rb
|
||||
- app/controllers/shop_controller.rb
|
||||
- app/controllers/spree/admin/image_settings_controller.rb
|
||||
- app/controllers/spree/admin/orders/customer_details_controller.rb
|
||||
@@ -606,6 +603,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
|
||||
@@ -669,6 +667,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -681,7 +681,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'
|
||||
@@ -1257,7 +1256,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'
|
||||
|
||||
38
Gemfile
38
Gemfile
@@ -3,7 +3,7 @@ ruby "2.3.7"
|
||||
git_source(:github) { |repo_name| "https://github.com/#{repo_name}.git" }
|
||||
|
||||
gem 'i18n', '~> 0.6.11'
|
||||
gem 'i18n-js', '~> 3.5.1'
|
||||
gem 'i18n-js', '~> 3.6.0'
|
||||
gem 'rails', '~> 3.2.22'
|
||||
gem 'rails-i18n', '~> 3.0.0'
|
||||
gem 'rails_safe_tasks', '~> 1.0'
|
||||
@@ -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'
|
||||
|
||||
@@ -19,9 +20,8 @@ gem 'activerecord-postgresql-adapter'
|
||||
gem 'pg', '~> 0.21.0'
|
||||
|
||||
# OFN-maintained and patched version of Spree v2.0.4. See
|
||||
# https://github.com/openfoodfoundation/openfoodnetwork/wiki/Spree-2.0-upgrade
|
||||
# https://github.com/openfoodfoundation/openfoodnetwork/wiki/Tech-Doc:-OFN's-Spree-fork%F0%9F%8D%B4
|
||||
# for details.
|
||||
gem 'spree_backend', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
|
||||
gem 'spree_core', github: 'openfoodfoundation/spree', branch: '2-0-4-stable'
|
||||
|
||||
gem 'spree_i18n', github: 'spree/spree_i18n', branch: '1-3-stable'
|
||||
@@ -39,7 +39,7 @@ gem 'activemerchant', '~> 1.78'
|
||||
gem 'devise', '~> 2.2.5'
|
||||
gem 'devise-encryptable', '0.2.0'
|
||||
gem 'jwt', '~> 2.2'
|
||||
gem 'oauth2', '~> 1.4.2' # Used for Stripe Connect
|
||||
gem 'oauth2', '~> 1.4.4' # Used for Stripe Connect
|
||||
|
||||
gem 'daemons'
|
||||
gem 'delayed_job_active_record'
|
||||
@@ -62,7 +62,7 @@ gem 'haml'
|
||||
gem 'rabl'
|
||||
gem 'redcarpet'
|
||||
gem 'sass', "~> 3.3"
|
||||
gem 'sass-rails', '~> 3.2.3', groups: [:default, :assets]
|
||||
gem 'sass-rails', '~> 3.2.3'
|
||||
gem 'truncate_html'
|
||||
gem 'unicorn'
|
||||
|
||||
@@ -93,33 +93,31 @@ gem 'wkhtmltopdf-binary'
|
||||
|
||||
gem 'foreigner'
|
||||
gem 'immigrant'
|
||||
gem 'roo', '~> 2.8.2'
|
||||
gem 'roo', '~> 2.8.3'
|
||||
|
||||
gem 'whenever', require: false
|
||||
|
||||
gem 'test-unit', '~> 3.3'
|
||||
|
||||
# Gems used only for assets and not required
|
||||
# in production environments by default.
|
||||
group :assets do
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
gem 'compass-rails'
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
gem 'compass-rails'
|
||||
|
||||
gem 'mini_racer', '0.2.9'
|
||||
gem 'mini_racer', '0.2.9'
|
||||
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
|
||||
gem 'angular-rails-templates', '~> 0.3.0'
|
||||
gem 'foundation-icons-sass-rails'
|
||||
gem 'momentjs-rails'
|
||||
gem 'turbo-sprockets-rails3'
|
||||
end
|
||||
gem 'angular-rails-templates', '~> 0.3.0'
|
||||
gem 'foundation-icons-sass-rails'
|
||||
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'
|
||||
|
||||
gem 'ofn-qz', github: 'openfoodfoundation/ofn-qz', ref: '60da2ae4c44cbb4c8d602f59fb5fff8d0f21db3c'
|
||||
|
||||
@@ -169,5 +167,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
|
||||
|
||||
90
Gemfile.lock
90
Gemfile.lock
@@ -34,18 +34,6 @@ GIT
|
||||
revision: 8a8585a43cd04d1a50dc65227f337a91b18d66d5
|
||||
branch: 2-0-4-stable
|
||||
specs:
|
||||
spree_api (2.0.4)
|
||||
rabl (= 0.8.4)
|
||||
spree_core (= 2.0.4)
|
||||
versioncake (= 1.0.0)
|
||||
spree_backend (2.0.4)
|
||||
deface (>= 0.9.0)
|
||||
jquery-rails (~> 3.0.0)
|
||||
jquery-ui-rails (~> 4.0.0)
|
||||
rails (~> 3.2.8)
|
||||
select2-rails (~> 3.4.7)
|
||||
spree_api (= 2.0.4)
|
||||
spree_core (= 2.0.4)
|
||||
spree_core (2.0.4)
|
||||
activemerchant (~> 1.34)
|
||||
acts_as_list (= 0.2.0)
|
||||
@@ -87,6 +75,11 @@ GIT
|
||||
activemodel (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
|
||||
PATH
|
||||
remote: engines/catalog
|
||||
specs:
|
||||
catalog (0.0.1)
|
||||
|
||||
PATH
|
||||
remote: engines/order_management
|
||||
specs:
|
||||
@@ -223,7 +216,7 @@ GEM
|
||||
activerecord (>= 3.2.0, < 5.0)
|
||||
fog (~> 1.0)
|
||||
rails (>= 3.2.0, < 5.0)
|
||||
ddtrace (0.32.0)
|
||||
ddtrace (0.33.1)
|
||||
msgpack
|
||||
debugger-linecache (1.2.0)
|
||||
deface (1.0.2)
|
||||
@@ -261,7 +254,7 @@ GEM
|
||||
factory_bot_rails (4.10.0)
|
||||
factory_bot (~> 4.10.0)
|
||||
railties (>= 3.0.0)
|
||||
faraday (0.17.1)
|
||||
faraday (1.0.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffaker (1.22.1)
|
||||
ffi (1.11.3)
|
||||
@@ -431,13 +424,13 @@ GEM
|
||||
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)
|
||||
multi_xml (>= 0.5.2)
|
||||
i18n (0.6.11)
|
||||
i18n-js (3.5.1)
|
||||
i18n-js (3.6.0)
|
||||
i18n (>= 0.6.6)
|
||||
immigrant (0.3.6)
|
||||
activerecord (>= 3.0)
|
||||
@@ -445,7 +438,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)
|
||||
@@ -458,7 +451,7 @@ GEM
|
||||
kaminari (0.14.1)
|
||||
actionpack (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
kgio (2.11.2)
|
||||
kgio (2.11.3)
|
||||
knapsack (1.18.0)
|
||||
rake
|
||||
launchy (2.4.3)
|
||||
@@ -479,20 +472,20 @@ 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)
|
||||
newrelic_rpm (3.18.1.330)
|
||||
nokogiri (1.6.8.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
oauth2 (1.4.2)
|
||||
oauth2 (1.4.4)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
oj (3.10.2)
|
||||
oj (3.10.5)
|
||||
orm_adapter (0.5.0)
|
||||
paper_trail (5.2.3)
|
||||
activerecord (>= 3.0, < 6.0)
|
||||
@@ -506,7 +499,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)
|
||||
@@ -528,9 +521,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
|
||||
@@ -559,8 +552,8 @@ GEM
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.0)
|
||||
rake (13.0.0)
|
||||
raindrops (0.19.1)
|
||||
rake (13.0.1)
|
||||
ransack (0.7.2)
|
||||
actionpack (~> 3.0)
|
||||
activerecord (~> 3.0)
|
||||
@@ -578,28 +571,29 @@ GEM
|
||||
redcarpet (3.5.0)
|
||||
request_store (1.4.1)
|
||||
rack (>= 1.4)
|
||||
rexml (3.2.4)
|
||||
roadie (3.4.0)
|
||||
css_parser (~> 1.4)
|
||||
nokogiri (~> 1.5)
|
||||
roadie-rails (1.3.0)
|
||||
railties (>= 3.0, < 5.3)
|
||||
roadie (~> 3.1)
|
||||
roo (2.8.2)
|
||||
roo (2.8.3)
|
||||
nokogiri (~> 1)
|
||||
rubyzip (>= 1.2.1, < 2.0.0)
|
||||
rubyzip (>= 1.3.0, < 3.0.0)
|
||||
rspec (3.9.0)
|
||||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
rspec-mocks (~> 3.9.0)
|
||||
rspec-core (3.9.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-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)
|
||||
@@ -609,15 +603,17 @@ GEM
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-retry (0.6.2)
|
||||
rspec-core (> 3.3)
|
||||
rspec-support (3.9.0)
|
||||
rubocop (0.79.0)
|
||||
rspec-support (3.9.2)
|
||||
rubocop (0.80.1)
|
||||
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)
|
||||
rubocop-rails (2.5.0)
|
||||
activesupport
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 0.72.0)
|
||||
ruby-progressbar (1.10.1)
|
||||
@@ -658,7 +654,7 @@ GEM
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
state_machine (1.2.0)
|
||||
stringex (1.5.1)
|
||||
stripe (5.11.0)
|
||||
stripe (5.15.0)
|
||||
test-unit (3.3.5)
|
||||
power_assert
|
||||
thor (0.20.3)
|
||||
@@ -676,24 +672,20 @@ GEM
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (1.6.1)
|
||||
unicorn (5.5.2)
|
||||
unicorn (5.5.4)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
unicorn-rails (2.2.1)
|
||||
rack
|
||||
unicorn
|
||||
uuidtools (2.1.5)
|
||||
versioncake (1.0.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
webdrivers (4.2.0)
|
||||
nokogiri (~> 1.6)
|
||||
rubyzip (>= 1.3.0)
|
||||
selenium-webdriver (>= 3.0, < 4.0)
|
||||
webmock (3.8.0)
|
||||
webmock (3.8.3)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
@@ -725,6 +717,7 @@ DEPENDENCIES
|
||||
bugsnag
|
||||
byebug (~> 9.0.0)
|
||||
capybara (>= 2.18.0)
|
||||
catalog!
|
||||
coffee-rails (~> 3.2.1)
|
||||
combine_pdf
|
||||
compass-rails
|
||||
@@ -753,10 +746,11 @@ DEPENDENCIES
|
||||
gmaps4rails
|
||||
haml
|
||||
i18n (~> 0.6.11)
|
||||
i18n-js (~> 3.5.1)
|
||||
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)
|
||||
kaminari (~> 0.14.1)
|
||||
@@ -766,7 +760,7 @@ DEPENDENCIES
|
||||
momentjs-rails
|
||||
newrelic_rpm (~> 3.0)
|
||||
nokogiri (>= 1.6.7.1)
|
||||
oauth2 (~> 1.4.2)
|
||||
oauth2 (~> 1.4.4)
|
||||
ofn-qz!
|
||||
oj
|
||||
order_management!
|
||||
@@ -775,7 +769,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)
|
||||
@@ -783,19 +777,19 @@ DEPENDENCIES
|
||||
rails_safe_tasks (~> 1.0)
|
||||
redcarpet
|
||||
roadie-rails (~> 1.3.0)
|
||||
roo (~> 2.8.2)
|
||||
roo (~> 2.8.3)
|
||||
rspec-rails (>= 3.5.2)
|
||||
rspec-retry
|
||||
rubocop
|
||||
rubocop-rails
|
||||
sass (~> 3.3)
|
||||
sass-rails (~> 3.2.3)
|
||||
select2-rails (~> 3.4.7)
|
||||
selenium-webdriver
|
||||
shoulda-matchers
|
||||
simple_form!
|
||||
simplecov
|
||||
spinjs-rails
|
||||
spree_backend!
|
||||
spree_core!
|
||||
spree_i18n!
|
||||
spree_paypal_express!
|
||||
|
||||
@@ -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 |
@@ -16,10 +16,10 @@
|
||||
//= require jquery.jstree/jquery.jstree
|
||||
//= require jquery.vAlign
|
||||
//= require jquery.horizontalNav
|
||||
//= require jquery.adaptivemenu
|
||||
//= 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
|
||||
@@ -28,11 +28,9 @@
|
||||
|
||||
// spree
|
||||
//= require spree
|
||||
//= require admin/spree-select2
|
||||
//= require admin/spree_backend
|
||||
//= require admin/spree/spree-select2
|
||||
//= require modernizr
|
||||
//= require spin
|
||||
//= require jquery.adaptivemenu
|
||||
//= require equalize
|
||||
//= require css_browser_selector_dev
|
||||
//= require responsive-tables
|
||||
@@ -68,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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor) ->
|
||||
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor, SortOptions, ErrorsParser) ->
|
||||
$scope.StatusMessage = StatusMessage
|
||||
|
||||
$scope.columns = Columns.columns
|
||||
@@ -38,6 +38,8 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
$scope.query = ""
|
||||
$scope.DisplayProperties = DisplayProperties
|
||||
|
||||
$scope.sortOptions = SortOptions
|
||||
|
||||
$scope.initialise = ->
|
||||
$scope.fetchProducts()
|
||||
|
||||
@@ -54,6 +56,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
'q[name_cont]': $scope.query,
|
||||
'q[supplier_id_eq]': $scope.producerFilter,
|
||||
'q[primary_taxon_id_eq]': $scope.categoryFilter,
|
||||
'q[s]': $scope.sorting,
|
||||
import_date: $scope.importDateFilter,
|
||||
page: $scope.page,
|
||||
per_page: $scope.per_page
|
||||
@@ -103,9 +106,16 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
$scope.categoryFilter = "0"
|
||||
$scope.importDateFilter = "0"
|
||||
|
||||
$scope.$watch 'sortOptions', (sort) ->
|
||||
return unless sort && sort.predicate != ""
|
||||
|
||||
$scope.sorting = sort.getSortingExpr()
|
||||
$scope.fetchProducts()
|
||||
, true
|
||||
|
||||
confirm_unsaved_changes = () ->
|
||||
(DirtyProducts.count() > 0 and confirm(t("unsaved_changes_confirmation"))) or (DirtyProducts.count() == 0)
|
||||
|
||||
|
||||
editProductUrl = (product, variant) ->
|
||||
"/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit"
|
||||
|
||||
@@ -220,10 +230,9 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
BulkProducts.updateVariantLists(data.products || [])
|
||||
$timeout -> $scope.displaySuccess()
|
||||
).error (data, status) ->
|
||||
if status == 400 && data.errors? && data.errors.length > 0
|
||||
errors = error + "\n" for error in data.errors
|
||||
alert t("products_update_error") + "\n" + errors
|
||||
$scope.displayFailure t("products_update_error")
|
||||
if status == 400 && data.errors?
|
||||
errorsString = ErrorsParser.toString(data.errors, status)
|
||||
$scope.displayFailure t("products_update_error") + "\n" + errorsString
|
||||
else
|
||||
$scope.displayFailure t("products_update_error_data") + status
|
||||
|
||||
@@ -274,7 +283,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
|
||||
|
||||
$scope.displayFailure = (failMessage) ->
|
||||
StatusMessage.display 'failure', t("products_update_error_msg") + "#{failMessage}"
|
||||
StatusMessage.display 'failure', t("products_update_error_msg") + " #{failMessage}"
|
||||
|
||||
|
||||
$scope.displayDirtyProducts = ->
|
||||
|
||||
@@ -3,6 +3,11 @@ angular.module("admin.indexUtils").factory 'SortOptions', ->
|
||||
predicate: ""
|
||||
reverse: true
|
||||
|
||||
getSortingExpr: () ->
|
||||
sortingExpr = this.predicate + ' desc' if this.reverse
|
||||
sortingExpr = this.predicate + ' asc' if !this.reverse
|
||||
sortingExpr
|
||||
|
||||
toggle: (predicate) ->
|
||||
@reverse = (@predicate == predicate) && !@reverse
|
||||
@predicate = predicate
|
||||
|
||||
@@ -48,13 +48,15 @@ angular.module('admin.orderCycles')
|
||||
|
||||
return if enterprise.last_page_loaded? && enterprise.last_page_loaded >= page
|
||||
enterprise.last_page_loaded = page
|
||||
enterprise.loaded_variants ?= 0
|
||||
|
||||
incoming = true if $scope.view == 'incoming'
|
||||
params = { exchange_id: exchange.id, enterprise_id: exchange.enterprise_id, order_cycle_id: $scope.order_cycle.id, incoming: incoming, page: page}
|
||||
ExchangeProduct.index params, (products, num_of_pages, num_of_products) ->
|
||||
ExchangeProduct.index params, (products, num_of_pages) ->
|
||||
enterprise.num_of_pages = num_of_pages
|
||||
enterprise.num_of_products = num_of_products
|
||||
enterprise.supplied_products.push products...
|
||||
angular.forEach products, (product) ->
|
||||
enterprise.loaded_variants += product.variants.length
|
||||
|
||||
$scope.loadMoreExchangeProducts = (exchange) ->
|
||||
$scope.loadExchangeProducts(exchange, $scope.enterprises[exchange.enterprise_id].last_page_loaded + 1)
|
||||
|
||||
@@ -64,10 +64,10 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, Reque
|
||||
$scope.selected_orders.push order.id if $scope.select_all
|
||||
|
||||
$scope.$watch 'sortOptions', (sort) ->
|
||||
if sort && sort.predicate != ""
|
||||
$scope.sorting = sort.predicate + ' desc' if sort.reverse
|
||||
$scope.sorting = sort.predicate + ' asc' if !sort.reverse
|
||||
$scope.fetchResults()
|
||||
return unless sort && sort.predicate != ""
|
||||
|
||||
$scope.sorting = sort.getSortingExpr()
|
||||
$scope.fetchProducts()
|
||||
, true
|
||||
|
||||
$scope.capturePayment = (order) ->
|
||||
|
||||
@@ -3,6 +3,9 @@ angular.module("admin.orders").directive 'customerSearchOverride', ->
|
||||
scope:
|
||||
distributorId: '@'
|
||||
link: (scope, element, attr) ->
|
||||
if $('#customer_autocomplete_template').length > 0
|
||||
customerTemplate = Handlebars.compile($('#customer_autocomplete_template').text())
|
||||
|
||||
formatCustomerResult = (customer) ->
|
||||
customerTemplate
|
||||
customer: customer
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -20,6 +20,21 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta
|
||||
secrets.card = response.token.card
|
||||
submit()
|
||||
|
||||
# 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
|
||||
else
|
||||
secrets.token = response.paymentMethod.id
|
||||
secrets.cc_type = response.paymentMethod.card.brand
|
||||
secrets.card = response.paymentMethod.card
|
||||
submit()
|
||||
|
||||
# Maps the brand returned by Stripe to that required by activemerchant
|
||||
mapCC: (ccType) ->
|
||||
switch ccType
|
||||
|
||||
@@ -21,9 +21,7 @@ angular.module("admin.products").factory "OptionValueNamer", (VariantUnitManager
|
||||
|
||||
else
|
||||
value = @variant.unit_value
|
||||
unit_name = @variant.product.variant_unit_name
|
||||
# TODO needs to add pluralize to line below
|
||||
# unit_name = unit_name if value > 1
|
||||
unit_name = @pluralize(@variant.product.variant_unit_name, value)
|
||||
|
||||
value = parseInt(value, 10) if value == parseInt(value, 10)
|
||||
|
||||
@@ -32,6 +30,21 @@ angular.module("admin.products").factory "OptionValueNamer", (VariantUnitManager
|
||||
|
||||
[value, unit_name]
|
||||
|
||||
pluralize: (unit_name, count) ->
|
||||
return unit_name if count == undefined
|
||||
unit_key = @unit_key(unit_name)
|
||||
return unit_name unless unit_key
|
||||
I18n.t(["inflections", unit_key], {count: count, defaultValue: unit_name})
|
||||
|
||||
unit_key: (unit_name) ->
|
||||
unless I18n.unit_keys
|
||||
I18n.unit_keys = {}
|
||||
for key, translations of I18n.t("inflections")
|
||||
for quantifier, translation of translations
|
||||
I18n.unit_keys[translation.toLowerCase()] = key
|
||||
|
||||
I18n.unit_keys[unit_name.toLowerCase()]
|
||||
|
||||
option_value_value_unit_scaled: ->
|
||||
[unit_scale, unit_name] = @scale_for_unit_value()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//= require_self
|
||||
//= require admin/handlebar_extensions
|
||||
//= require admin/variant_autocomplete
|
||||
//= require admin/spree/orders/variant_autocomplete
|
||||
|
||||
/**
|
||||
This is a collection of javascript functions and whatnot
|
||||
@@ -21,12 +21,14 @@ jQuery(function($) {
|
||||
if (typeof $('.field.checkbox label').vAlign === 'function' )
|
||||
$('.field.checkbox label').vAlign()
|
||||
|
||||
// if (typeof Spree !== 'undefined') {
|
||||
// $('.main-menu-wrapper ul').AdaptiveMenu({
|
||||
// text: "<a href='#'><i class='icon-chevron-down'></i> " + Spree.translations.more + "</a>",
|
||||
// klass: "dropdown"
|
||||
// });
|
||||
// }
|
||||
// We activate AdaptiveMenu only if not on webdriver
|
||||
// Re-adjusting the admin menu during tests causes tests to fail.
|
||||
if (!navigator.webdriver && typeof Spree !== 'undefined') {
|
||||
$('.main-menu-wrapper ul').AdaptiveMenu({
|
||||
text: "<a href='#'><i class='icon-chevron-down'></i> " + Spree.translations.more + "</a>",
|
||||
klass: "dropdown"
|
||||
});
|
||||
}
|
||||
|
||||
// Add some tips
|
||||
if (typeof $('.with-tip').powerTip === 'function' ) {
|
||||
@@ -174,6 +176,28 @@ $(document).ready(function() {
|
||||
$(target).prepend(new_table_row);
|
||||
})
|
||||
|
||||
$('body').on('click', '.delete-resource', function() {
|
||||
var el = $(this);
|
||||
if (confirm(el.data("confirm"))) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: $(this).attr("href"),
|
||||
data: {
|
||||
_method: 'delete',
|
||||
authenticity_token: AUTH_TOKEN
|
||||
},
|
||||
dataType: 'html',
|
||||
success: function(response) {
|
||||
el.parents("tr").fadeOut('hide');
|
||||
},
|
||||
error: function(response, textStatus, errorThrown) {
|
||||
show_flash_error(response.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Fix sortable helper
|
||||
var fixHelper = function(e, ui) {
|
||||
ui.children().each(function() {
|
||||
|
||||
16
app/assets/javascripts/admin/spree/calculator.js
Normal file
16
app/assets/javascripts/admin/spree/calculator.js
Normal file
@@ -0,0 +1,16 @@
|
||||
$(function() {
|
||||
var calculator_select = $('select#calc_type')
|
||||
var original_calc_type = calculator_select.attr('value');
|
||||
$('.calculator-settings-warning').hide();
|
||||
calculator_select.change(function() {
|
||||
if (calculator_select.attr('value') == original_calc_type) {
|
||||
$('div.calculator-settings').show();
|
||||
$('.calculator-settings-warning').hide();
|
||||
$('.calculator-settings').find('input,textarea').prop("disabled", false);
|
||||
} else {
|
||||
$('div.calculator-settings').hide();
|
||||
$('.calculator-settings-warning').show();
|
||||
$('.calculator-settings').find('input,texttarea').prop("disabled", true);
|
||||
}
|
||||
});
|
||||
})
|
||||
59
app/assets/javascripts/admin/spree/image_settings.js.erb
Normal file
59
app/assets/javascripts/admin/spree/image_settings.js.erb
Normal file
@@ -0,0 +1,59 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
if ($('input#preferences_use_s3[type="checkbox"]:checked').length > 0) {
|
||||
$('#s3_settings, #s3_headers').show();
|
||||
}
|
||||
|
||||
// Toggle display of S3 settings based on value of use_s3 checkbox
|
||||
$('input#preferences_use_s3[type="checkbox"]').click(function() {
|
||||
$('#s3_settings, #s3_headers').toggle();
|
||||
});
|
||||
|
||||
$(document).on('click', '.destroy_style', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).parent().remove();
|
||||
});
|
||||
|
||||
$(document).on('click', '.destroy_new_attachment_styles', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).closest('.new_attachment_styles').remove();
|
||||
});
|
||||
|
||||
$(document).on('click', '.destroy_new_s3_headers', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).closest('.new_s3_headers').remove();
|
||||
});
|
||||
|
||||
// Handle adding new styles
|
||||
var styles_hash_index = 1;
|
||||
$(document).on('click', '.add_new_style', function(e) {
|
||||
e.preventDefault();
|
||||
$('#new-styles').append(generate_html_for_hash("new_attachment_styles", styles_hash_index));
|
||||
});
|
||||
|
||||
// Handle adding new headers
|
||||
var headers_hash_index = 1;
|
||||
$(document).on('click', '.add_header', function(e) {
|
||||
e.preventDefault();
|
||||
$('#headers_list').append(generate_html_for_hash("new_s3_headers", headers_hash_index));
|
||||
});
|
||||
|
||||
// Generates html for new paperclip styles form fields
|
||||
generate_html_for_hash = function(hash_name, index) {
|
||||
var html = '<div class="' + hash_name + ' row"><div class="field">';
|
||||
html += '<div class="five columns">';
|
||||
html += '<label for="' + hash_name + '_' + index + '_name">';
|
||||
html += Spree.translations.name + '</label>';
|
||||
html += '<input id="' + hash_name + '_' + index + '_name" name="' + hash_name + '[' + index + '][name]" type="text" class="fullwidth"><br>';
|
||||
html += '</div><div class="five columns">'
|
||||
html += '<label for="' + hash_name + '_' + index + '_value">';
|
||||
html += Spree.translations.value + '</label>';
|
||||
html += '<input id="' + hash_name + '_' + index + '_value" name="' + hash_name + '[' + index + '][value]" type="text" class="fullwidth">';
|
||||
html += '</div><div class="two columns">'
|
||||
html += '<a href="#" title="' + Spree.translations.destroy + '" class="destroy_' + hash_name + ' with-tip button" style="margin-top: 19px;"><i class="icon-trash"></i> ' + Spree.translations.destroy + '</a>';
|
||||
html += '</div></div></div>';
|
||||
|
||||
index += 1;
|
||||
return html;
|
||||
};
|
||||
});
|
||||
23
app/assets/javascripts/admin/spree/nested-attribute.js
Normal file
23
app/assets/javascripts/admin/spree/nested-attribute.js
Normal file
@@ -0,0 +1,23 @@
|
||||
//On page load
|
||||
replace_ids = function(s){
|
||||
var new_id = new Date().getTime();
|
||||
return s.replace(/NEW_RECORD/g, new_id);
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$('a[id*=nested]').click(function() {
|
||||
var template = $(this).attr('href').replace(/.*#/, '');
|
||||
html = replace_ids(eval(template));
|
||||
$('#ul-' + $(this).attr('id')).append(html);
|
||||
update_remove_links();
|
||||
});
|
||||
update_remove_links();
|
||||
})
|
||||
|
||||
var update_remove_links = function() {
|
||||
$('.remove').click(function() {
|
||||
$(this).prevAll(':first').val(1);
|
||||
$(this).parent().hide();
|
||||
return false;
|
||||
});
|
||||
};
|
||||
@@ -15,11 +15,11 @@ $(document).ready(function() {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
// $('[data-hook=admin_order_edit_form] a.ship').click(handle_ship_click);
|
||||
$('[data-hook=admin_order_edit_form] a.ship').click(handle_ship_click);
|
||||
|
||||
//handle shipping method edit click
|
||||
// $('a.edit-method').click(toggleMethodEdit);
|
||||
// $('a.cancel-method').click(toggleMethodEdit);
|
||||
$('a.edit-method').click(toggleMethodEdit);
|
||||
$('a.cancel-method').click(toggleMethodEdit);
|
||||
|
||||
handle_shipping_method_save = function(){
|
||||
var link = $(this);
|
||||
@@ -38,11 +38,11 @@ $(document).ready(function() {
|
||||
console.log(msg);
|
||||
});
|
||||
}
|
||||
// $('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
|
||||
$('[data-hook=admin_order_edit_form] a.save-method').click(handle_shipping_method_save);
|
||||
|
||||
//handle tracking edit click
|
||||
// $('a.edit-tracking').click(toggleTrackingEdit);
|
||||
// $('a.cancel-tracking').click(toggleTrackingEdit);
|
||||
$('a.edit-tracking').click(toggleTrackingEdit);
|
||||
$('a.cancel-tracking').click(toggleTrackingEdit);
|
||||
|
||||
handle_tracking_save = function(){
|
||||
var link = $(this);
|
||||
|
||||
@@ -29,10 +29,10 @@ $(document).ready(function() {
|
||||
});
|
||||
|
||||
//handle edit click
|
||||
// $('a.edit-item').click(toggleItemEdit);
|
||||
$('a.edit-item').click(toggleItemEdit);
|
||||
|
||||
//handle cancel click
|
||||
// $('a.cancel-item').click(toggleItemEdit);
|
||||
$('a.cancel-item').click(toggleItemEdit);
|
||||
|
||||
handle_save_click = function(){
|
||||
var save = $(this);
|
||||
@@ -46,7 +46,7 @@ $(document).ready(function() {
|
||||
adjustItems(shipment_number, variant_id, quantity);
|
||||
return false;
|
||||
}
|
||||
// $('a.save-item').click(handle_save_click);
|
||||
$('a.save-item').click(handle_save_click);
|
||||
|
||||
handle_delete_click = function(){
|
||||
var del = $(this);
|
||||
@@ -57,7 +57,7 @@ $(document).ready(function() {
|
||||
|
||||
adjustItems(shipment_number, variant_id, 0);
|
||||
}
|
||||
// $('a.delete-item').click(handle_delete_click);
|
||||
$('a.delete-item').click(handle_delete_click);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -139,39 +139,3 @@ addVariantFromStockLocation = function() {
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
formatVariantResult = function(variant) {
|
||||
if (variant["images"][0] != undefined && variant["images"][0].urls != undefined) {
|
||||
variant.image = variant.images[0].urls.mini
|
||||
}
|
||||
return variantTemplate({ variant: variant })
|
||||
}
|
||||
|
||||
$.fn.variantAutocomplete = function() {
|
||||
this.parent().children(".options_placeholder").attr('id', this.parent().data('index'))
|
||||
this.select2({
|
||||
placeholder: Spree.translations.variant_placeholder,
|
||||
minimumInputLength: 3,
|
||||
ajax: {
|
||||
url: Spree.url(Spree.routes.variants_search),
|
||||
datatype: 'json',
|
||||
data: function(term, page) {
|
||||
return {
|
||||
q: {
|
||||
"product_name_or_sku_cont": term
|
||||
}
|
||||
}
|
||||
},
|
||||
results: function (data, page) {
|
||||
window.variants = data['variants'];
|
||||
|
||||
return { results: data['variants'] }
|
||||
}
|
||||
},
|
||||
formatResult: formatVariantResult,
|
||||
formatSelection: function (variant) {
|
||||
$(this.element).parent().children('.options_placeholder').html(variant.options_text)
|
||||
return variant.name;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
8
app/assets/javascripts/admin/spree/states.js
Executable file
8
app/assets/javascripts/admin/spree/states.js
Executable file
@@ -0,0 +1,8 @@
|
||||
$(document).ready(function() {
|
||||
$("#country").change(function() {
|
||||
var new_state_link_href = $('#new_state_link a').attr('href');
|
||||
var selected_country_id = $('#country option:selected').attr('value');
|
||||
var new_link = new_state_link_href.replace(/countries\/(\d+)/, 'countries/'+selected_country_id);
|
||||
$('#new_state_link a').attr('href', new_link);
|
||||
});
|
||||
});
|
||||
43
app/assets/javascripts/admin/spree/zone.js.coffee
Normal file
43
app/assets/javascripts/admin/spree/zone.js.coffee
Normal file
@@ -0,0 +1,43 @@
|
||||
$ ->
|
||||
($ '#country_based').click ->
|
||||
show_country()
|
||||
|
||||
($ '#state_based').click ->
|
||||
show_state()
|
||||
|
||||
if ($ '#country_based').is(':checked')
|
||||
show_country()
|
||||
else if ($ '#state_based').is(':checked')
|
||||
show_state()
|
||||
else
|
||||
show_state()
|
||||
($ '#state_based').click()
|
||||
|
||||
|
||||
show_country = ->
|
||||
($ '#state_members :input').each ->
|
||||
($ this).prop 'disabled', true
|
||||
|
||||
($ '#state_members').hide()
|
||||
($ '#zone_members :input').each ->
|
||||
($ this).prop 'disabled', true
|
||||
|
||||
($ '#zone_members').hide()
|
||||
($ '#country_members :input').each ->
|
||||
($ this).prop 'disabled', false
|
||||
|
||||
($ '#country_members').show()
|
||||
|
||||
show_state = ->
|
||||
($ '#country_members :input').each ->
|
||||
($ this).prop 'disabled', true
|
||||
|
||||
($ '#country_members').hide()
|
||||
($ '#zone_members :input').each ->
|
||||
($ this).prop 'disabled', true
|
||||
|
||||
($ '#zone_members').hide()
|
||||
($ '#state_members :input').each ->
|
||||
($ this).prop 'disabled', false
|
||||
|
||||
($ '#state_members').show()
|
||||
0
app/assets/javascripts/admin/spree_backend.js
Normal file
0
app/assets/javascripts/admin/spree_backend.js
Normal file
@@ -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
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
angular.module("admin.utils").directive "variantAutocomplete", ($timeout) ->
|
||||
restrict: 'C'
|
||||
link: (scope, element, attrs) ->
|
||||
# Make variantAutocomplete do nothing because it is called
|
||||
# from core/app/assets/javascripts/admin/orders/edit.js
|
||||
$.fn.variantAutocomplete = angular.noop
|
||||
|
||||
$timeout ->
|
||||
if $("#variant_autocomplete_template").length > 0
|
||||
variantTemplate = Handlebars.compile($("#variant_autocomplete_template").text())
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Parses a structure of errors that came from the server
|
||||
angular.module("admin.utils").factory "ErrorsParser", ->
|
||||
new class ErrorsParser
|
||||
toString: (errors, defaultContent = "") =>
|
||||
return defaultContent unless errors?
|
||||
|
||||
errorsString = ""
|
||||
if errors.length > 0
|
||||
# it is an array of errors
|
||||
errorsString = errors.join("\n") + "\n"
|
||||
else
|
||||
# it is a hash of errors
|
||||
keys = Object.keys(errors)
|
||||
for key in keys
|
||||
errorsString += errors[key].join("\n") + "\n"
|
||||
|
||||
this.defaultIfEmpty(errorsString, defaultContent)
|
||||
|
||||
defaultIfEmpty: (content, defaultContent) =>
|
||||
return defaultContent if content == ""
|
||||
content
|
||||
@@ -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 .
|
||||
@@ -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]
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -23,6 +21,23 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) ->
|
||||
secrets.card = response.token.card
|
||||
submit()
|
||||
|
||||
# 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}"})
|
||||
else
|
||||
secrets.token = response.paymentMethod.id
|
||||
secrets.cc_type = response.paymentMethod.card.brand
|
||||
secrets.card = response.paymentMethod.card
|
||||
submit()
|
||||
|
||||
# Maps the brand returned by Stripe to that required by activemerchant
|
||||
mapCC: (ccType) ->
|
||||
switch ccType
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'ng-model' => 'exchange.select_all_variants',
|
||||
'ng-change' => 'setExchangeVariants(exchange, incomingExchangeVariantsFor(exchange.enterprise_id), exchange.select_all_variants)',
|
||||
'id' => 'order_cycle_outgoing_exchange_{{ $parent.$index }}_select_all_variants' }
|
||||
{{ 'js.admin.panels.exchange_products.select_all_products' | t:{ total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
{{ 'js.admin.panels.exchange_products.select_all_variants' | t:{ total_number_of_variants: exchangeTotalVariants(exchange) } }}
|
||||
|
||||
.exchange-products{ 'ng-hide' => 'productsLoading()' }
|
||||
.exchange-product{'ng-repeat' => 'product in enterprises[exchange.enterprise_id].supplied_products | filter:visibleProducts:exchange:order_cycle.visible_variants_for_outgoing_exchanges' }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
.pagination{ 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages && !productsLoading()'}
|
||||
.button{ 'ng-click' => 'loadMoreExchangeProducts(exchange)' }
|
||||
{{ 'js.admin.panels.exchange_products.load_more_products' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.load_more_variants' | t }}
|
||||
.button{ 'ng-click' => 'loadAllExchangeProducts(exchange)' }
|
||||
{{ 'js.admin.panels.exchange_products.load_all_products' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.load_all_variants' | t }}
|
||||
|
||||
.sixteen.columns.alpha#loading{ 'ng-show' => 'productsLoading()' }
|
||||
%br
|
||||
%img.spinner{ src: "/assets/spinning-circles.svg" }
|
||||
%h1
|
||||
{{ 'js.admin.panels.exchange_products.loading_products' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.loading_variants' | t }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.exchange-load-all-variants
|
||||
%div
|
||||
{{ 'js.admin.panels.exchange_products.products_loaded' | t:{ num_of_products_loaded: enterprises[exchange.enterprise_id].supplied_products.length, total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
{{ 'js.admin.panels.exchange_products.variants_loaded' | t:{ num_of_variants_loaded: enterprises[exchange.enterprise_id].loaded_variants, total_number_of_variants: exchangeTotalVariants(exchange) } }}
|
||||
%a{ 'ng-click' => 'loadAllExchangeProducts(exchange)', 'ng-show' => 'enterprises[exchange.enterprise_id].last_page_loaded < enterprises[exchange.enterprise_id].num_of_pages' }
|
||||
{{ 'js.admin.panels.exchange_products.load_all_products' | t }}
|
||||
{{ 'js.admin.panels.exchange_products.load_all_variants' | t }}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
'ng-model' => 'exchange.select_all_variants',
|
||||
'ng-change' => 'selectAllVariants(exchange, exchange.select_all_variants)',
|
||||
'id' => 'order_cycle_incoming_exchange_{{ $index }}_select_all_variants' }
|
||||
{{ 'js.admin.panels.exchange_products.select_all_products' | t:{ total_number_of_products: enterprises[exchange.enterprise_id].num_of_products } }}
|
||||
{{ 'js.admin.panels.exchange_products.select_all_variants' | t:{ total_number_of_variants: exchangeTotalVariants(exchange) } }}
|
||||
|
||||
%div{ 'ng-include' => "'admin/panels/exchange_products_supplied_list.html'" }
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
*
|
||||
|
||||
*= require admin/spree_backend
|
||||
*= require jquery.powertip
|
||||
*= require responsive-tables
|
||||
*= require normalize
|
||||
*= require skeleton
|
||||
*= require responsive-tables
|
||||
*= require jquery.powertip
|
||||
*= require jquery.ui.datepicker
|
||||
*= require jquery-ui-timepicker-addon
|
||||
*= require shared/textAngular
|
||||
*= require shared/ng-tags-input.min
|
||||
*= require select2
|
||||
|
||||
*= require_self
|
||||
*/
|
||||
@@ -33,6 +33,11 @@
|
||||
@import 'plugins/powertip';
|
||||
@import 'plugins/jstree';
|
||||
@import 'plugins/font-awesome';
|
||||
@import 'plugins/select2';
|
||||
|
||||
@import 'sections/image_settings';
|
||||
@import 'sections/orders';
|
||||
@import 'sections/products';
|
||||
|
||||
@import 'hacks/mozilla';
|
||||
@import 'hacks/opera';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.destroy_style, .destroy_header {
|
||||
float: right;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -48,16 +48,22 @@ module Api
|
||||
end
|
||||
|
||||
def bulk_products
|
||||
product_query = OpenFoodNetwork::Permissions.new(current_api_user).
|
||||
editable_products.merge(product_scope)
|
||||
product_query = OpenFoodNetwork::Permissions.
|
||||
new(current_api_user).
|
||||
editable_products.
|
||||
merge(product_scope)
|
||||
|
||||
if params[:import_date].present?
|
||||
product_query = product_query.imported_on(params[:import_date]).group_by_products_id
|
||||
product_query = product_query.
|
||||
imported_on(params[:import_date]).
|
||||
group_by_products_id
|
||||
end
|
||||
|
||||
@products = product_query.order('created_at DESC').
|
||||
ransack(params[:q]).result.
|
||||
page(params[:page] || DEFAULT_PAGE).per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
@products = product_query.
|
||||
ransack(query_params_with_defaults).
|
||||
result.
|
||||
page(params[:page] || DEFAULT_PAGE).
|
||||
per(params[:per_page] || DEFAULT_PER_PAGE)
|
||||
|
||||
render_paged_products @products
|
||||
end
|
||||
@@ -136,6 +142,10 @@ module Api
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def query_params_with_defaults
|
||||
params[:q].to_h.reverse_merge(s: 'created_at desc')
|
||||
end
|
||||
|
||||
def pagination_data(results)
|
||||
{
|
||||
results: results.total_count,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Api
|
||||
class PromoImagesController < EnterpriseAttachmentController
|
||||
class PromoImagesController < Api::EnterpriseAttachmentController
|
||||
private
|
||||
|
||||
def attachment_name
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/address_finder'
|
||||
|
||||
class CheckoutController < Spree::CheckoutController
|
||||
class CheckoutController < Spree::StoreController
|
||||
layout 'darkswarm'
|
||||
|
||||
include CheckoutHelper
|
||||
include OrderCyclesHelper
|
||||
include EnterprisesHelper
|
||||
|
||||
ssl_required
|
||||
|
||||
# We need pessimistic locking to avoid race conditions.
|
||||
# Otherwise we fail on duplicate indexes or end up with negative stock.
|
||||
prepend_around_filter CurrentOrderLocker, only: :update
|
||||
@@ -12,12 +20,23 @@ class CheckoutController < Spree::CheckoutController
|
||||
prepend_before_filter :require_order_cycle
|
||||
prepend_before_filter :require_distributor_chosen
|
||||
|
||||
before_filter :load_order
|
||||
|
||||
before_filter :ensure_order_not_completed
|
||||
before_filter :ensure_checkout_allowed
|
||||
before_filter :ensure_sufficient_stock_lines
|
||||
|
||||
before_filter :associate_user
|
||||
before_filter :check_authorization
|
||||
before_filter :enable_embedded_shopfront
|
||||
|
||||
include OrderCyclesHelper
|
||||
include EnterprisesHelper
|
||||
helper 'spree/orders'
|
||||
|
||||
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
|
||||
@@ -25,54 +44,16 @@ class CheckoutController < Spree::CheckoutController
|
||||
end
|
||||
|
||||
def update
|
||||
shipping_method_id = object_params.delete(:shipping_method_id)
|
||||
params_adapter = Checkout::FormDataAdapter.new(params, @order, spree_current_user)
|
||||
return update_failed unless @order.update_attributes(params_adapter.order_params)
|
||||
|
||||
return update_failed unless @order.update_attributes(object_params)
|
||||
|
||||
check_order_for_phantom_fees
|
||||
fire_event('spree.checkout.update')
|
||||
|
||||
while @order.state != "complete"
|
||||
if @order.state == "payment"
|
||||
return if redirect_to_paypal_express_form_if_needed
|
||||
end
|
||||
|
||||
if @order.state == "delivery"
|
||||
@order.select_shipping_method(shipping_method_id)
|
||||
end
|
||||
|
||||
next if advance_order_state(@order)
|
||||
|
||||
flash[:error] = if @order.errors.present?
|
||||
@order.errors.full_messages.to_sentence
|
||||
else
|
||||
t(:payment_processing_failed)
|
||||
end
|
||||
update_failed
|
||||
return
|
||||
end
|
||||
return update_failed unless @order.state == "complete" || @order.completed?
|
||||
|
||||
set_default_bill_address
|
||||
set_default_ship_address
|
||||
|
||||
ResetOrderService.new(self, current_order).call
|
||||
session[:access_token] = current_order.token
|
||||
|
||||
flash[:notice] = t(:order_processed_successfully)
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
respond_with(@order, location: order_path(@order))
|
||||
end
|
||||
format.json do
|
||||
render json: { path: order_path(@order) }, status: :ok
|
||||
end
|
||||
end
|
||||
rescue Spree::Core::GatewayError => error
|
||||
# This is done for all actions in the Spree::CheckoutController.
|
||||
rescue_from_spree_gateway_error(error)
|
||||
rescue StandardError => error
|
||||
Bugsnag.notify(error)
|
||||
checkout_workflow(params_adapter.shipping_method_id)
|
||||
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
|
||||
end
|
||||
@@ -87,111 +68,38 @@ class CheckoutController < Spree::CheckoutController
|
||||
|
||||
private
|
||||
|
||||
def set_default_bill_address
|
||||
if params[:order][:default_bill_address]
|
||||
new_bill_address = @order.bill_address.clone.attributes
|
||||
|
||||
user_bill_address_id = spree_current_user.bill_address.andand.id
|
||||
spree_current_user.update_attributes(
|
||||
bill_address_attributes: new_bill_address.merge('id' => user_bill_address_id)
|
||||
)
|
||||
|
||||
customer_bill_address_id = @order.customer.bill_address.andand.id
|
||||
@order.customer.update_attributes(
|
||||
bill_address_attributes: new_bill_address.merge('id' => customer_bill_address_id)
|
||||
)
|
||||
end
|
||||
def check_authorization
|
||||
authorize!(:edit, current_order, session[:access_token])
|
||||
end
|
||||
|
||||
def set_default_ship_address
|
||||
if params[:order][:default_ship_address]
|
||||
new_ship_address = @order.ship_address.clone.attributes
|
||||
|
||||
user_ship_address_id = spree_current_user.ship_address.andand.id
|
||||
spree_current_user.update_attributes(
|
||||
ship_address_attributes: new_ship_address.merge('id' => user_ship_address_id)
|
||||
)
|
||||
|
||||
customer_ship_address_id = @order.customer.ship_address.andand.id
|
||||
@order.customer.update_attributes(
|
||||
ship_address_attributes: new_ship_address.merge('id' => customer_ship_address_id)
|
||||
)
|
||||
end
|
||||
def ensure_checkout_allowed
|
||||
redirect_to main_app.cart_path unless @order.checkout_allowed?
|
||||
end
|
||||
|
||||
def check_order_for_phantom_fees
|
||||
phantom_fees = @order.adjustments.
|
||||
joins("LEFT OUTER JOIN spree_line_items"\
|
||||
" ON spree_line_items.id = spree_adjustments.source_id").
|
||||
where("originator_type = 'EnterpriseFee'"\
|
||||
" AND source_type = 'Spree::LineItem' AND spree_line_items.id IS NULL")
|
||||
|
||||
if phantom_fees.any?
|
||||
Bugsnag.notify(RuntimeError.new("Phantom Fees"),
|
||||
phantom_fees: {
|
||||
phantom_total: phantom_fees.sum(&:amount).to_s,
|
||||
phantom_fees: phantom_fees.as_json
|
||||
})
|
||||
end
|
||||
def ensure_order_not_completed
|
||||
redirect_to main_app.cart_path if @order.completed?
|
||||
end
|
||||
|
||||
# Copied and modified from spree. Remove check for order state, since the state machine is
|
||||
# progressed all the way in one go with the one page checkout.
|
||||
def object_params
|
||||
# For payment step, filter order parameters to produce the expected
|
||||
# nested attributes for a single payment and its source,
|
||||
# discarding attributes for payment methods other than the one selected
|
||||
if params[:payment_source].present? && source_params = params.delete(:payment_source)[params[:order][:payments_attributes].first[:payment_method_id].underscore]
|
||||
params[:order][:payments_attributes].first[:source_attributes] = source_params
|
||||
end
|
||||
if params[:order][:payments_attributes]
|
||||
params[:order][:payments_attributes].first[:amount] = @order.total
|
||||
end
|
||||
if params[:order][:existing_card_id]
|
||||
construct_saved_card_attributes
|
||||
end
|
||||
params[:order]
|
||||
end
|
||||
|
||||
# Perform order.next, guarding against StaleObjectErrors
|
||||
def advance_order_state(order)
|
||||
tries ||= 3
|
||||
order.next
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
retry unless (tries -= 1).zero?
|
||||
false
|
||||
end
|
||||
|
||||
def update_failed
|
||||
current_order.updater.shipping_address_from_distributor
|
||||
RestartCheckout.new(@order).call
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render :edit
|
||||
end
|
||||
format.json do
|
||||
render json: { errors: @order.errors, flash: flash.to_hash }.to_json, status: :bad_request
|
||||
end
|
||||
def ensure_sufficient_stock_lines
|
||||
if @order.insufficient_stock_lines.present?
|
||||
flash[:error] = Spree.t(:inventory_error_flash_for_insufficient_quantity)
|
||||
redirect_to main_app.cart_path
|
||||
end
|
||||
end
|
||||
|
||||
def load_order
|
||||
@order = current_order
|
||||
redirect_to(main_app.shop_path) && return unless @order && @order.checkout_allowed?
|
||||
|
||||
redirect_to(main_app.shop_path) && return if redirect_to_shop?
|
||||
redirect_to_cart_path && return unless valid_order_line_items?
|
||||
redirect_to(main_app.shop_path) && return if @order.completed?
|
||||
before_address
|
||||
setup_for_current_state
|
||||
end
|
||||
|
||||
def before_address
|
||||
associate_user
|
||||
|
||||
finder = OpenFoodNetwork::AddressFinder.new(@order.email, @order.customer, spree_current_user)
|
||||
|
||||
@order.bill_address = finder.bill_address
|
||||
@order.ship_address = finder.ship_address
|
||||
def redirect_to_shop?
|
||||
!@order ||
|
||||
!@order.checkout_allowed? ||
|
||||
@order.completed?
|
||||
end
|
||||
|
||||
def valid_order_line_items?
|
||||
@@ -212,32 +120,29 @@ class CheckoutController < Spree::CheckoutController
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_to_paypal_express_form_if_needed
|
||||
return unless params[:order][:payments_attributes]
|
||||
|
||||
payment_method_id = params[:order][:payments_attributes].first[:payment_method_id]
|
||||
payment_method = Spree::PaymentMethod.find(payment_method_id)
|
||||
return unless payment_method.is_a?(Spree::Gateway::PayPalExpress)
|
||||
|
||||
render json: { path: spree.paypal_express_path(payment_method_id: payment_method.id) },
|
||||
status: :ok
|
||||
true
|
||||
def setup_for_current_state
|
||||
method_name = :"before_#{@order.state}"
|
||||
__send__(method_name) if respond_to?(method_name, true)
|
||||
end
|
||||
|
||||
def construct_saved_card_attributes
|
||||
existing_card_id = params[:order].delete(:existing_card_id)
|
||||
return if existing_card_id.blank?
|
||||
def before_address
|
||||
associate_user
|
||||
|
||||
credit_card = Spree::CreditCard.find(existing_card_id)
|
||||
if credit_card.try(:user_id).blank? || credit_card.user_id != spree_current_user.try(:id)
|
||||
raise Spree::Core::GatewayError, I18n.t(:invalid_credit_card)
|
||||
end
|
||||
finder = OpenFoodNetwork::AddressFinder.new(@order.email, @order.customer, spree_current_user)
|
||||
|
||||
# Not currently supported but maybe we should add it...?
|
||||
credit_card.verification_value = params[:cvc_confirm] if params[:cvc_confirm].present?
|
||||
@order.bill_address = finder.bill_address
|
||||
@order.ship_address = finder.ship_address
|
||||
end
|
||||
|
||||
params[:order][:payments_attributes].first[:source] = credit_card
|
||||
params[:order][:payments_attributes].first.delete :source_attributes
|
||||
def before_delivery
|
||||
return if params[:order].present?
|
||||
|
||||
packages = @order.shipments.map(&:to_package)
|
||||
@differentiator = Spree::Stock::Differentiator.new(@order, packages)
|
||||
end
|
||||
|
||||
def before_payment
|
||||
current_order.payments.destroy_all if request.put?
|
||||
end
|
||||
|
||||
def rescue_from_spree_gateway_error(error)
|
||||
@@ -247,4 +152,115 @@ class CheckoutController < Spree::CheckoutController
|
||||
format.json { render json: { flash: flash.to_hash }, status: :bad_request }
|
||||
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_workflow_error
|
||||
checkout_failed
|
||||
end
|
||||
end
|
||||
|
||||
def checkout_workflow(shipping_method_id)
|
||||
while @order.state != "complete"
|
||||
if @order.state == "payment"
|
||||
return if redirect_to_payment_gateway
|
||||
end
|
||||
|
||||
@order.select_shipping_method(shipping_method_id) if @order.state == "delivery"
|
||||
|
||||
next if advance_order_state(@order)
|
||||
|
||||
flash[:error] = order_workflow_error
|
||||
return update_failed
|
||||
end
|
||||
|
||||
update_response
|
||||
end
|
||||
|
||||
def redirect_to_payment_gateway
|
||||
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
|
||||
true
|
||||
end
|
||||
|
||||
# Perform order.next, guarding against StaleObjectErrors
|
||||
def advance_order_state(order)
|
||||
tries ||= 3
|
||||
order.next
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
retry unless (tries -= 1).zero?
|
||||
false
|
||||
end
|
||||
|
||||
def order_workflow_error
|
||||
if @order.errors.present?
|
||||
@order.errors.full_messages.to_sentence
|
||||
else
|
||||
t(:payment_processing_failed)
|
||||
end
|
||||
end
|
||||
|
||||
def update_response
|
||||
if order_complete?
|
||||
checkout_succeeded
|
||||
update_succeeded_response
|
||||
else
|
||||
update_failed
|
||||
end
|
||||
end
|
||||
|
||||
def order_complete?
|
||||
@order.state == "complete" || @order.completed?
|
||||
end
|
||||
|
||||
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))
|
||||
end
|
||||
format.json do
|
||||
render json: { path: order_path(@order) }, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_failed
|
||||
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
|
||||
end
|
||||
format.json do
|
||||
render json: { errors: @order.errors, flash: flash.to_hash }.to_json, status: :bad_request
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@ class ShopsController < BaseController
|
||||
.activated
|
||||
.visible
|
||||
.is_distributor
|
||||
.includes(address: :state)
|
||||
.includes(address: [:state, :country])
|
||||
.includes(:properties)
|
||||
.includes(supplied_products: :properties)
|
||||
.all
|
||||
|
||||
@@ -70,10 +70,6 @@ module Spree
|
||||
Spree.t(event_sym, resource: resource_desc)
|
||||
end
|
||||
|
||||
def render_js_for_destroy
|
||||
render partial: '/spree/admin/shared/destroy'
|
||||
end
|
||||
|
||||
# Index request for JSON needs to pass a CSRF token in order to prevent JSON Hijacking
|
||||
def check_json_authenticity
|
||||
return unless request.format.js? || request.format.json?
|
||||
@@ -86,10 +82,6 @@ module Spree
|
||||
raise(ActionController::InvalidAuthenticityToken)
|
||||
end
|
||||
|
||||
def config_locale
|
||||
Spree::Backend::Config[:locale]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def html_request?
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,93 +1,16 @@
|
||||
require 'open_food_network/address_finder'
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This controller (and respective route in the Spree engine)
|
||||
# is only needed for the spree_paypal_express gem that redirects here explicitly.
|
||||
#
|
||||
# According to the rails docs it would be possible to redirect
|
||||
# to CheckoutController directly in the routes
|
||||
# with a slash like "to: '/checkout#edit'", but it does not work in this case.
|
||||
module Spree
|
||||
class CheckoutController < Spree::StoreController
|
||||
include CheckoutHelper
|
||||
|
||||
ssl_required
|
||||
|
||||
before_filter :load_order
|
||||
|
||||
before_filter :ensure_order_not_completed
|
||||
before_filter :ensure_checkout_allowed
|
||||
before_filter :ensure_sufficient_stock_lines
|
||||
|
||||
before_filter :associate_user
|
||||
before_filter :check_authorization
|
||||
before_filter :enable_embedded_shopfront
|
||||
|
||||
helper 'spree/orders'
|
||||
|
||||
rescue_from Spree::Core::GatewayError, with: :rescue_from_spree_gateway_error
|
||||
|
||||
def edit
|
||||
flash.keep
|
||||
redirect_to main_app.checkout_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_order
|
||||
@order = current_order
|
||||
redirect_to(main_app.cart_path) && return unless @order
|
||||
|
||||
if params[:state]
|
||||
redirect_to checkout_state_path(@order.state) if @order.can_go_to_state?(params[:state])
|
||||
@order.state = params[:state]
|
||||
end
|
||||
setup_for_current_state
|
||||
end
|
||||
|
||||
def ensure_checkout_allowed
|
||||
redirect_to main_app.cart_path unless @order.checkout_allowed?
|
||||
end
|
||||
|
||||
def ensure_order_not_completed
|
||||
redirect_to main_app.cart_path if @order.completed?
|
||||
end
|
||||
|
||||
def ensure_sufficient_stock_lines
|
||||
if @order.insufficient_stock_lines.present?
|
||||
flash[:error] = Spree.t(:inventory_error_flash_for_insufficient_quantity)
|
||||
redirect_to main_app.cart_path
|
||||
end
|
||||
end
|
||||
|
||||
def setup_for_current_state
|
||||
method_name = :"before_#{@order.state}"
|
||||
send(method_name) if respond_to?(method_name, true)
|
||||
end
|
||||
|
||||
# Adapted from spree_last_address gem: https://github.com/TylerRick/spree_last_address
|
||||
# Originally, we used a forked version of this gem, but encountered strange errors where
|
||||
# it worked in dev but only intermittently in staging/prod.
|
||||
def before_address
|
||||
associate_user
|
||||
|
||||
finder = OpenFoodNetwork::AddressFinder.new(@order.email)
|
||||
|
||||
@order.bill_address = finder.bill_address
|
||||
@order.ship_address = finder.ship_address
|
||||
end
|
||||
|
||||
def before_delivery
|
||||
return if params[:order].present?
|
||||
|
||||
packages = @order.shipments.map(&:to_package)
|
||||
@differentiator = Spree::Stock::Differentiator.new(@order, packages)
|
||||
end
|
||||
|
||||
def before_payment
|
||||
current_order.payments.destroy_all if request.put?
|
||||
end
|
||||
|
||||
def rescue_from_spree_gateway_error
|
||||
flash[:error] = Spree.t(:spree_gateway_error_flash_for_checkout)
|
||||
render :edit
|
||||
end
|
||||
|
||||
def check_authorization
|
||||
authorize!(:edit, current_order, session[:access_token])
|
||||
end
|
||||
end
|
||||
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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
module NavigationHelper
|
||||
@@ -34,7 +36,9 @@ module Spree
|
||||
end
|
||||
|
||||
selected = if options[:match_path]
|
||||
request.fullpath.starts_with?("#{spree.root_path}admin#{options[:match_path]}")
|
||||
request.
|
||||
fullpath.
|
||||
starts_with?("#{main_app.root_path}admin#{options[:match_path]}")
|
||||
else
|
||||
args.include?(controller.controller_name.to_sym)
|
||||
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
|
||||
|
||||
@@ -6,6 +6,7 @@ class SubscriptionConfirmJob
|
||||
ids = proxy_orders.pluck(:id)
|
||||
proxy_orders.update_all(confirmed_at: Time.zone.now)
|
||||
ProxyOrder.where(id: ids).each do |proxy_order|
|
||||
Rails.logger.info "Confirming Order for Proxy Order #{proxy_order.id}"
|
||||
@order = proxy_order.order
|
||||
process!
|
||||
end
|
||||
|
||||
@@ -5,8 +5,7 @@ class SubscriptionPlacementJob
|
||||
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
|
||||
@@ -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: :schedule_ids }
|
||||
|
||||
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: :order_cycle_ids }
|
||||
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Spree::ProductSet < ModelSet
|
||||
def initialize(attributes = {})
|
||||
super(Spree::Product, [], attributes, proc { |attrs| attrs[:product_id].blank? })
|
||||
super(Spree::Product, [], attributes)
|
||||
end
|
||||
|
||||
def save
|
||||
@@ -34,11 +34,9 @@ class Spree::ProductSet < ModelSet
|
||||
split_taxon_ids!(attributes)
|
||||
|
||||
product = find_model(@collection, attributes[:id])
|
||||
if product.nil?
|
||||
@klass.new(attributes).save unless @reject_if.andand.call(attributes)
|
||||
else
|
||||
update_product(product, attributes)
|
||||
end
|
||||
return if product.nil?
|
||||
|
||||
update_product(product, attributes)
|
||||
end
|
||||
|
||||
def split_taxon_ids!(attributes)
|
||||
|
||||
@@ -15,7 +15,7 @@ Spree::ShippingMethod.class_eval do
|
||||
scoped
|
||||
else
|
||||
joins(:distributors).
|
||||
where('distributors_shipping_methods.distributor_id IN (?)', user.enterprises).
|
||||
where('distributors_shipping_methods.distributor_id IN (?)', user.enterprises.select(&:id)).
|
||||
select('DISTINCT spree_shipping_methods.*')
|
||||
end
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
module Spree
|
||||
TaxRate.class_eval do
|
||||
class << self
|
||||
def match_with_sales_tax_registration(order)
|
||||
def match(order)
|
||||
return [] if order.distributor && !order.distributor.charges_sales_tax
|
||||
return [] unless order.tax_zone
|
||||
|
||||
match_without_sales_tax_registration(order)
|
||||
all.select do |rate|
|
||||
rate.zone == order.tax_zone || rate.zone.contains?(order.tax_zone) || rate.zone.default_tax
|
||||
end
|
||||
end
|
||||
alias_method_chain :match, :sales_tax_registration
|
||||
end
|
||||
|
||||
def adjust_with_included_tax(order)
|
||||
|
||||
@@ -49,7 +49,7 @@ Spree::Variant.class_eval do
|
||||
}
|
||||
|
||||
scope :for_distribution, lambda { |order_cycle, distributor|
|
||||
where('spree_variants.id IN (?)', order_cycle.variants_distributed_by(distributor))
|
||||
where('spree_variants.id IN (?)', order_cycle.variants_distributed_by(distributor).select(&:id))
|
||||
}
|
||||
|
||||
scope :visible_for, lambda { |enterprise|
|
||||
|
||||
@@ -24,8 +24,10 @@ module Stock
|
||||
#
|
||||
# @return [Array<Spree::ShippingMethod>]
|
||||
def shipping_methods
|
||||
super.delete_if do |shipping_method|
|
||||
!ships_with?(order.distributor, shipping_method)
|
||||
available_shipping_methods = super.to_a
|
||||
|
||||
available_shipping_methods.keep_if do |shipping_method|
|
||||
ships_with?(order.distributor.shipping_methods.to_a, shipping_method)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,11 +35,11 @@ module Stock
|
||||
|
||||
# Checks whether the given distributor provides the specified shipping method
|
||||
#
|
||||
# @param distributor [Spree::Enterprise]
|
||||
# @param shipping_methods [Array<Spree::ShippingMethod>]
|
||||
# @param shipping_method [Spree::ShippingMethod]
|
||||
# @return [Boolean]
|
||||
def ships_with?(distributor, shipping_method)
|
||||
distributor.shipping_methods.include?(shipping_method)
|
||||
def ships_with?(shipping_methods, shipping_method)
|
||||
shipping_methods.include?(shipping_method)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
class Subscription < ActiveRecord::Base
|
||||
ALLOWED_PAYMENT_METHOD_TYPES = ["Spree::PaymentMethod::Check", "Spree::Gateway::StripeConnect"].freeze
|
||||
ALLOWED_PAYMENT_METHOD_TYPES = ["Spree::PaymentMethod::Check",
|
||||
"Spree::Gateway::StripeConnect",
|
||||
"Spree::Gateway::StripeSCA"].freeze
|
||||
|
||||
belongs_to :shop, class_name: 'Enterprise'
|
||||
belongs_to :customer
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user