Compare commits

..

216 Commits
1.1.0 ... v1.2

Author SHA1 Message Date
Maikel Linke
51064f31a3 Merge remote-tracking branch 'origin/master' into uk-variable-weights-improvements 2015-09-10 16:05:28 +10:00
Maikel Linke
e25805aa78 Restoring all changes of the last combined branch
This reverts commit 1e55e8a907.
2015-09-10 15:03:13 +10:00
Maikel Linke
1e55e8a907 Reverting all changes of the last combined branch
The production server hangs on certain requests. To rule out a code bug,
we revert to the last deployed version.
2015-09-10 12:50:40 +10:00
Maikel Linke
47d239ee3a fix for intermittent capybara click failure
This fix might have to be applied at more places. We will see what CI
says...
2015-09-09 17:08:42 +10:00
Maikel Linke
2e2e767564 always displaying group_buy_unit_size 2015-09-09 12:22:28 +10:00
Maikel Linke
f0709d5e46 Merge branch 'show-only-visible-shops' into combined/shopfront_group-buy_customer-contact 2015-09-04 21:49:12 +10:00
Maikel Linke
e8ce9feb52 Merge branch 'customers-link' into combined/shopfront_group-buy_customer-contact 2015-09-04 21:48:58 +10:00
Maikel Linke
a3074fa51e Merge branch 'customer-contact-in-order-email' into combined/shopfront_group-buy_customer-contact 2015-09-04 21:47:35 +10:00
Maikel Linke
c1cbf9a35c Merge branch 'group-by-unit-size-display' into combined/shopfront_group-buy_customer-contact 2015-09-04 21:47:22 +10:00
Maikel Linke
8dffb772dc Merge branch 'shopfron-validation' into combined/shopfront_group-buy_customer-contact 2015-09-04 21:47:08 +10:00
Maikel Linke
60b2596774 disable max quantity unless min quantity is set 2015-09-04 16:56:51 +10:00
Maikel Linke
6a2af09006 rounding decimal quantities in shopfront 2015-09-04 16:15:02 +10:00
Maikel Linke
d9b1215a01 Update clicking checkout in spec 2015-09-04 10:54:50 +10:00
Maikel Linke
0895bd8647 Non-integer values cannot be entered into shop front quantities 2015-09-03 17:42:26 +10:00
Maikel Linke
2e3ff8f5c6 doubling cart buttons 2015-09-03 15:52:54 +10:00
Maikel Linke
93f6667c4b Update products group by spec 2015-09-03 15:01:28 +10:00
Maikel Linke
23355d1a25 include order email and phone number in email for shop 2015-09-03 14:46:08 +10:00
Maikel Linke
0fdd000589 Showing Bulk Unit Size in BulkCoopReport
The group_buy_unit_size field is now shown even if 'group buy' is not
enabled. And "Group By Unit Size" is now displayed as "Bulk Unit Size".
2015-09-03 12:26:40 +10:00
Maikel Linke
57caeb64c5 Remove unused product form partial 2015-09-03 12:24:57 +10:00
Continuous Integration
d3e66bcfa5 Auto-merge from CI [skip ci] 2015-09-02 14:44:05 +10:00
Maikel Linke
1a96ef88bc Hiding invisible hubs in producer modal 2015-08-28 10:49:36 +10:00
Maikel Linke
d682a29a65 Admin menu contains link to /admin/customers 2015-08-28 10:31:53 +10:00
Maikel Linke
ced254919a Updating JS spec 2015-08-27 14:54:46 +10:00
Maikel Linke
64834dd50a Fixing display of cloned products
The producer and the unit is displayed again.
2015-08-27 13:05:09 +10:00
Maikel Linke
5d34b711e9 Handling undefined customer tags 2015-08-26 15:01:20 +10:00
Maikel Linke
4f7f2e2035 Displaying customer tags 2015-08-26 10:14:03 +10:00
Continuous Integration
d595e1558a Auto-merge from CI [skip ci] 2015-08-26 10:03:13 +10:00
Maikel Linke
5ccae9cfab Redirecting /products/:id to / 2015-08-21 15:18:31 +10:00
Maikel Linke
adc20e9e4e Handling tax categories without tax rates 2015-08-21 15:16:00 +10:00
Continuous Integration
03627e2ef2 Auto-merge from CI [skip ci] 2015-08-21 13:41:02 +10:00
Rohan Mitchell
3e5028b6b9 Revise lettuceshare report - combine grower and cert cols, add column headings 2015-08-16 11:06:52 +08:00
Rohan Mitchell
e843beaf18 Remove enterprise address from order confirmation and mailer, since in these contexts the pickup location will be specified elsewhere. #636 2015-08-07 14:19:48 +10:00
Rohan Mitchell
170799f65f Add permission for hub enterprise users to use xero report #628 2015-08-07 13:37:37 +10:00
Rohan Mitchell
9e1788a6b0 Apply migrations to db/schema.rb 2015-08-07 12:03:43 +10:00
Rohan Mitchell
719ddea789 Merge branch 'variable-weights-improvements' of https://github.com/lin-d-hop/openfoodnetwork into lin-d-hop-variable-weights-improvements 2015-08-07 11:10:07 +10:00
Rohan Mitchell
3eea002a0c Put rspec-retry on flaky specs 2015-08-07 11:06:16 +10:00
Rohan Mitchell
6f4dc6943e Add first cut of LettuceShare report 2015-08-07 10:37:59 +10:00
Rohan Mitchell
e75b595b97 Tidy syntax 2015-08-07 10:37:59 +10:00
Rohan Mitchell
af04c61528 Add rspec-retry for intermittently failing spec 2015-08-05 13:35:53 +10:00
Continuous Integration
14900655df Auto-merge from CI [skip ci] 2015-08-05 12:18:31 +10:00
Rohan Mitchell
be66769999 SELECT DISTINCT results in inconsistent #count value. Work around this with to_a. 2015-08-05 11:05:29 +10:00
Lynne Davis
8639109372 Variable weights improvements additions 2015-08-04 20:13:28 +01:00
Rohan Mitchell
eca25a2564 Make coordinator fees apply to all variants, not just those with exchange fees 2015-07-31 16:57:47 +10:00
Rohan Mitchell
012b0517a5 Fetch order cycle variants in SQL 2015-07-31 16:57:47 +10:00
Rohan Mitchell
faa1d0d1c5 When cart clear is declined, revert order cycle selection change 2015-07-31 16:56:08 +10:00
Rohan Mitchell
b97bbae00e Remove old selenium-related test helper, remove unused param on handle_js_confirm 2015-07-31 16:56:08 +10:00
Rohan Mitchell
679531fe2a Prompt for clear cart when order cycle changes 2015-07-31 16:56:08 +10:00
Rohan Mitchell
eb6dcba396 Clear localstorage in Cart.clear() 2015-07-31 16:56:08 +10:00
Rohan Mitchell
9c9051498b Rename ofnEmptiesCart to ofnChangeHub 2015-07-31 16:56:08 +10:00
Rohan Mitchell
5688cfd1dd WIP: Rename ofnEmptiesCart to ofnChangeHub 2015-07-31 16:56:08 +10:00
Rohan Mitchell
fc409c97bf Decouple ofn-empties-cart directive from hub-choice navigation 2015-07-31 16:56:08 +10:00
Rohan Mitchell
09b4b3e659 When order cycle is changed, clear the cart 2015-07-31 16:56:08 +10:00
Rohan Mitchell
bf2bd403a4 Clear variants when order cycle changed, not when products are initially loaded 2015-07-31 16:56:08 +10:00
Rohan Mitchell
012a210782 Add method to clear cart 2015-07-31 16:56:08 +10:00
Rohan Mitchell
7caebb11e2 Clear variants registry before reloading products - fixes fees not updating when changing order cycle 2015-07-31 16:56:08 +10:00
Rohan Mitchell
ee65452de3 Add method to clear variants registry 2015-07-31 16:56:08 +10:00
Rohan Mitchell
3a9b13b55e Reorder methods 2015-07-31 16:56:07 +10:00
Rohan Mitchell
734b2fc766 Merge branch 'master' into redesign-cms 2015-07-31 14:59:33 +10:00
Rohan Mitchell
5559231245 Fix date spec error 2015-07-31 14:59:05 +10:00
Rohan Mitchell
3345f54e0a Merge branch 'master' into redesign-cms 2015-07-31 14:55:01 +10:00
Rohan Mitchell
ec908fce92 Make TOS URL configurable 2015-07-31 14:51:17 +10:00
Rohan Mitchell
d8f4061030 Tidy link 2015-07-30 12:09:20 +10:00
Rohan Mitchell
759c53ec34 Add note about provisioning to README 2015-07-30 11:45:50 +10:00
Continuous Integration
8d041f5e7a Auto-merge from CI [skip ci] 2015-07-29 12:03:03 +10:00
Continuous Integration
32d3d0f912 Auto-merge from CI [skip ci] 2015-07-28 14:03:29 +10:00
Rohan Mitchell
917c17af59 Clarify quantities used for calculations, show excess available via max_quantity 2015-07-24 18:31:49 +10:00
Rohan Mitchell
21a9681205 Do not show negative remainder when there are no purchases 2015-07-24 17:42:23 +10:00
Rohan Mitchell
54fd298e3a Reinstate variant full name column - needed to identify variants differing only by description 2015-07-24 17:42:00 +10:00
Rohan Mitchell
9ffc97f8b6 Fix controller spec for extraction of report controller logic 2015-07-24 14:10:04 +10:00
Rohan Mitchell
211171aed4 Rename column for clarity 2015-07-24 12:49:43 +10:00
Rohan Mitchell
f43eacb75d Revert "Do not show non-group buy products on bulk coop reports"
This reverts commit f5c2386296.
2015-07-24 12:40:50 +10:00
Rohan Mitchell
fffc35d9ed Revert "Add a blank row after summary row in all reports"
This reverts commit b0dd765181.
2015-07-24 12:40:42 +10:00
Rohan Mitchell
17fb4a7247 DRY by extracting calculation to method 2015-07-24 07:17:20 +10:00
Rohan Mitchell
3abce54115 Split variant name into number and unit columns 2015-07-24 07:07:49 +10:00
Rohan Mitchell
28de125b59 Bulk coop allocation report uses unit value rather than variant weight 2015-07-24 07:00:49 +10:00
Rohan Mitchell
4d71a56e38 Update available/unallocated calculations 2015-07-23 16:40:50 +10:00
Rohan Mitchell
ae968cd0eb Simple refactoring of original total_allocated code 2015-07-23 16:21:37 +10:00
Rohan Mitchell
1a89c3caf4 Translate bulk coop allocation report to reports DSL 2015-07-23 15:07:39 +10:00
Rohan Mitchell
bdd792a3ea Extract helpers to new superclass 2015-07-23 15:07:15 +10:00
Rohan Mitchell
f5c2386296 Do not show non-group buy products on bulk coop reports 2015-07-23 14:17:00 +10:00
Rohan Mitchell
8665b35f1d Units required shows amount to fulfil order, not complete units required. Remainder works in opposite direction to suit. 2015-07-23 14:16:40 +10:00
Rohan Mitchell
d68cfbff18 Use scaled value for group_buy_unit_size 2015-07-23 14:06:04 +10:00
Rohan Mitchell
e8f7ab5425 Do bulk coop report calculations using unit values rather than variant weight field 2015-07-23 14:06:04 +10:00
Rohan Mitchell
b0dd765181 Add a blank row after summary row in all reports 2015-07-23 14:05:59 +10:00
Rohan Mitchell
8ee467d2b9 Split variant name into number and unit columns 2015-07-23 12:22:30 +10:00
Rohan Mitchell
5874ecbbef Small tweaks 2015-07-23 12:02:31 +10:00
Rohan Mitchell
dcae584673 Extract bulk coop supplier report into Report DSL 2015-07-23 12:02:24 +10:00
Rohan Mitchell
a90666467a Add helper support to Reports DSL 2015-07-23 11:45:19 +10:00
Rohan Mitchell
cc9d0defca Use class attributes instead of class variables to avoid leakage between subclasses 2015-07-23 11:04:36 +10:00
Rohan Mitchell
142e1d6d9a Report outputs rules as array for use by OrderGrouper 2015-07-23 10:46:20 +10:00
Rohan Mitchell
1d39fb4438 Report rules can define a summary row 2015-07-23 10:37:47 +10:00
Rohan Mitchell
07eb857a8d Report can define nested rules 2015-07-23 10:24:10 +10:00
Rohan Mitchell
66f64fc413 Report can define basic rules 2015-07-23 10:08:38 +10:00
Rohan Mitchell
c7a1ca29f4 Report can define and retrieve columns 2015-07-23 09:52:59 +10:00
Rohan Mitchell
0a5e8fe629 Report class can define and retrieve header 2015-07-23 09:36:09 +10:00
Rohan Mitchell
61a39ea82f Cherry-pick 00927f7: Refactoring to move bulk out of reports_controller_decorator 2015-07-22 15:25:12 +10:00
Rohan Mitchell
eabf792238 Amend changes to readme 2015-07-22 13:25:55 +10:00
Myriam
c15e281286 Add command cd openfoodnetwork
Just added the command to go into the openfoodnetwork folder before bundle install
2015-07-19 12:39:51 +02:00
Myriam
5f0766cb75 When installing the gems we had only 1.9.2, there were an imprecision with >=1.9.3 or 1.9.x 2015-07-19 12:37:23 +02:00
Myriam
3a0c5bf0c3 I changed the "Get it" link as the git@ one was reserved to people with access rights 2015-07-19 12:09:20 +02:00
Rohan Mitchell
61435b2dea Add brand guide colours next to definition 2015-07-17 17:05:44 +10:00
Rohan Mitchell
b6892c82f1 Rename australia-orange sass var to country-agnostic brand-colour 2015-07-17 16:27:17 +10:00
Rohan Mitchell
5fd3dc1d28 Customise footer logo 2015-07-17 16:24:07 +10:00
Rohan Mitchell
bb3a4170f5 Add dimensions to image upload 2015-07-17 16:06:50 +10:00
Rohan Mitchell
74f90dada3 Allow overriding mobile logo (raster and SVG) and home hero image 2015-07-17 16:06:50 +10:00
Rohan Mitchell
e4a4cdd915 Rewrite alias_method_chain to use super. Ahhhhh :) 2015-07-17 16:06:50 +10:00
Rohan Mitchell
66b4eb4c5d Move Spree::Preferences::Configuration patches into superclass FileConfiguration 2015-07-17 16:06:50 +10:00
Rohan Mitchell
1d67e3b2b8 Fix image update, display logo on frontend 2015-07-17 16:06:50 +10:00
Rohan Mitchell
0e67a116b6 Admin can upload logo for ContentConfig 2015-07-17 16:06:49 +10:00
Rohan Mitchell
1234b35199 Add file handling to Spree::Preferences::Configuration 2015-07-17 16:06:49 +10:00
Rohan Mitchell
1b17a7fb35 Add logo field to ContentConfig 2015-07-17 16:06:49 +10:00
Rohan Mitchell
4887871474 Remove SVG TODOs - we'll ask admins to upload double-sized images for retina instead 2015-07-17 16:06:49 +10:00
Rohan Mitchell
372b17703a Remove unused content fields and partial 2015-07-17 16:06:49 +10:00
Rohan Mitchell
5de9a5eb54 Add config setting to enable/disable stats on home page 2015-07-17 16:06:49 +10:00
Rohan Mitchell
1f72e4001c Use site name config for hero title 2015-07-17 16:06:49 +10:00
Rohan Mitchell
bd83dde89d Content manage group signup page 2015-07-17 16:06:49 +10:00
Rohan Mitchell
a490d9696b Content manage hub signup page 2015-07-17 16:06:49 +10:00
Rohan Mitchell
d353906bb8 Content manage producer signup page 2015-07-17 16:06:49 +10:00
Continuous Integration
858a613ba2 Auto-merge from CI [skip ci] 2015-07-17 11:48:16 +10:00
Rohan Mitchell
2ef0196200 Merge branch 'add-to-cart-robustness' into combined/vo-on-demand-stock-control_add-to-cart-robustness 2015-07-16 10:06:34 +10:00
Rohan Mitchell
f3ae812f2b Update cart when max_quantity value changes, not just quantity 2015-07-16 10:05:19 +10:00
Rohan Mitchell
904a3a5bd4 When add to cart fails, retry every 3 seconds, with no limit of the number of retries 2015-07-16 10:05:19 +10:00
Rohan Mitchell
de7f3a9e5c When an on-demand variant has overridden stock levels, make it not on-demand 2015-07-16 10:05:07 +10:00
Rohan Mitchell
7424e93133 Merge branch 'proximity-search' 2015-07-15 17:31:22 +10:00
Lynne Davis
f21f57a42c Additions for packing reports, sans refactoring 2015-07-15 14:44:49 +10:00
Lynne Davis
dc95167f15 Adding bits missed from the last commit.
Conflicts:
	app/controllers/spree/admin/reports_controller_decorator.rb
2015-07-15 14:44:49 +10:00
Lynne Davis
c168dec14b Adding packing reports as required by the UK contingent 2015-07-15 14:44:49 +10:00
Rohan Mitchell
28b8e0b0c8 Update distance matches when filter settings change 2015-07-10 17:45:17 +10:00
Rohan Mitchell
0ab75fe2ea Take the first name match from the post-filter results to avoid matching off a result the user can't see 2015-07-10 17:45:17 +10:00
Rohan Mitchell
669c9911fe Fix profile display 2015-07-10 17:45:17 +10:00
Rohan Mitchell
6e6d2566d9 Do not show distance when zero 2015-07-10 17:45:16 +10:00
Rohan Mitchell
6816df5f72 Do not show 'Closest to...' heading when no query entered 2015-07-10 17:45:16 +10:00
Rohan Mitchell
5b93ac2ae1 Remove unneeded injections, consistent spacing 2015-07-10 17:45:16 +10:00
Rohan Mitchell
dcf98ee29f Fix enterprises no results for producers 2015-07-10 17:45:16 +10:00
Rohan Mitchell
7d340d5084 Update taxon search to only display taxons from enterprises on the page 2015-07-10 17:45:16 +10:00
Rohan Mitchell
0eb6d9aaed Disable animate-repeat on shops listing entirely - previous fix was not 100% successful 2015-07-10 17:45:16 +10:00
Rohan Mitchell
d8dfb5b5ee Move most filters back out into view - they now respond to filter preference changes 2015-07-10 17:45:16 +10:00
Rohan Mitchell
07e6a204f3 Do not bindonce ng-repeat, remove translateZ(0) optimisation - address dangling DOM nodes after ng-repeat finishes.
https://github.com/angular/angular.js/issues/4490
2015-07-10 17:45:16 +10:00
Rohan Mitchell
06c5ffb427 Wrap geocode callback in ., fixing inconsistent updates 2015-07-10 17:45:16 +10:00
Rohan Mitchell
673635fdcb Show no results message at correct times 2015-07-10 17:45:16 +10:00
Rohan Mitchell
64bc7404dc Region-bias geocoder results 2015-07-10 17:45:16 +10:00
Rohan Mitchell
651afc34cb Notify enterprise change by broadcast 2015-07-10 17:45:16 +10:00
Rohan Mitchell
e5c42c0e54 Tidyup 2015-07-10 17:45:16 +10:00
Rohan Mitchell
5f8826533d Limit distance matches to those within 50 km 2015-07-10 17:45:16 +10:00
Rohan Mitchell
f154a02c86 Add filter for enterprises within a radius 2015-07-10 17:45:16 +10:00
Rohan Mitchell
46792a4111 Style results tables 2015-07-10 17:45:16 +10:00
Rohan Mitchell
5b5c56064e Hide distance matches when there are name matches. Show with a link. 2015-07-10 17:45:16 +10:00
Rohan Mitchell
794c9558bb Show name matches only when at least one is present. Show accurate closest-to target. 2015-07-10 17:45:15 +10:00
Rohan Mitchell
eba0a12d29 Extract filtering into controller 2015-07-10 17:45:15 +10:00
Rohan Mitchell
7fd4815904 Split results into name matches and distance matches 2015-07-10 17:45:15 +10:00
Rohan Mitchell
97c9504344 Extract hubs table into partial 2015-07-10 17:45:15 +10:00
Rohan Mitchell
7f2508eeaa When a name match is found, calculate distances from that enterprise, rather than from the geocoded lookup 2015-07-10 17:45:15 +10:00
Rohan Mitchell
6e3ca3f90f Make Geo.distanceBetween take either a LatLng or a locatable at either parameter 2015-07-10 17:45:15 +10:00
Rohan Mitchell
744beaa26a Perform shops search by proximity instead of plain string match 2015-07-10 17:45:15 +10:00
Rohan Mitchell
d0d9e9e367 Flag enterprises whose name matches a query 2015-07-10 17:45:15 +10:00
Rohan Mitchell
befcc37456 Fix bug in distanceBetween, update docs, deal with null queries 2015-07-10 17:45:15 +10:00
Rohan Mitchell
9fb7c47c73 Enterprises can calculate their distances from a location via geocode lookup 2015-07-10 17:45:15 +10:00
Rohan Mitchell
80bb6c36e3 Rename file 2015-07-10 17:45:15 +10:00
Rohan Mitchell
a61e96c316 Generalise Geocoder service to Geo, add distanceBetween method 2015-07-10 17:45:15 +10:00
Rohan Mitchell
ddf1bb90ea Add geocoder service 2015-07-10 17:45:15 +10:00
Rohan Mitchell
a509747ba7 Do not show double-ups of places to shop 2015-07-10 15:51:57 +10:00
Rohan Mitchell
3527ae6ea2 Update comment 2015-07-10 15:51:57 +10:00
Rohan Mitchell
e679f1175c Add shop-for-products-at to map producer modals 2015-07-10 15:51:57 +10:00
Rohan Mitchell
197c99349d Add error checking to client-side permalink service 2015-07-10 09:42:09 +10:00
Rohan Mitchell
73b90dba10 Shift legacy redirect further down routes to avoid stomping /enterprises/check_permalink 2015-07-10 09:42:09 +10:00
Rohan Mitchell
4cc8eb90fc Refactor code for brevity 2015-07-10 09:42:09 +10:00
Rohan Mitchell
8deb4ef9d4 When encountering StaleObjectError when checking out, retry 2015-07-10 09:42:09 +10:00
Rohan Mitchell
d0b7a0795d Fix 500 when bulk updating order cycles with no data 2015-07-10 09:42:09 +10:00
Rohan Mitchell
d67b34c2bd Remove deprecated about_us page and add redirect 2015-07-10 09:42:09 +10:00
Rohan Mitchell
3f8235593a When enterprise is deleted, delete relationships 2015-07-10 09:42:09 +10:00
Rohan Mitchell
c5c3051f98 Re-enable future spec 2015-07-10 09:42:09 +10:00
Rohan Mitchell
4f2389e257 Remove legacy enterprises views - index, suppliers, distributors, show 2015-07-10 09:42:09 +10:00
Rohan Mitchell
fc4cd517fd Redirect legacy enterprise path to home #571 2015-07-10 09:42:09 +10:00
Rohan Mitchell
361f7e3432 Remove double-output on push to production. Add script to archive old branches. 2015-07-02 09:39:49 +10:00
Rohan Mitchell
213242627e Merge branch 'master' into redesign-phase-2
Conflicts:
	app/views/home/_hubs.html.haml
2015-07-01 15:44:36 +10:00
Rohan Mitchell
960e4d3015 Scale logo down when display is very short (ie. iPhone 4) 2015-07-01 15:38:50 +10:00
Continuous Integration
c3097cac70 Auto-merge from CI [skip ci] 2015-07-01 10:57:01 +10:00
Rohan Mitchell
b991f6c228 Add angular-slideables to js test include 2015-06-26 16:07:23 +10:00
Rohan Mitchell
918889a572 Make subject more friendly for groups enquiry email 2015-06-26 15:54:26 +10:00
Rohan Mitchell
7e685b646f Fine-tune header sizes on home page and cutover point for page_alert responsiveness 2015-06-26 15:54:26 +10:00
Rohan Mitchell
eef302635b Fix alert overflow on mobile 2015-06-26 15:54:26 +10:00
Rohan Mitchell
6105d008df Show CTA above the fold on iPhone 5 2015-06-26 15:54:26 +10:00
Rohan Mitchell
dd04afe8f8 Make alert box more legible 2015-06-26 15:54:25 +10:00
Rohan Mitchell
5a9101e303 Display HTML special chars correctly in title 2015-06-26 15:54:25 +10:00
Rohan Mitchell
c97ed026d0 Make room in menu for shopping@ text 2015-06-26 15:54:25 +10:00
Rohan Mitchell
ce46cb0956 Add subject to group enquiries 2015-06-26 15:54:25 +10:00
Rohan Mitchell
9f8fa575b3 Scroll past header on shops page if user is logged in 2015-06-26 15:54:25 +10:00
Rohan Mitchell
4da367a94b Put OFN logo in top bar of mobile site 2015-06-26 15:54:25 +10:00
Rohan Mitchell
6197dfe403 Make brand story animate slide down/up 2015-06-26 15:54:25 +10:00
Rohan Mitchell
1363daae3c When fetching ofn commit, if it is not found, return a sentinal value to avoid a wayward git push 2015-06-26 11:22:19 +10:00
Rohan Mitchell
d9f4a92648 Do not use with_products_require_tax_category in around filters - Spree::Config has caching which isn't shared between contexts 2015-06-24 14:52:17 +10:00
Rohan Mitchell
bc11140a40 Make config reset for all tests more robust. Add cache isolation for parallel specs. 2015-06-24 14:02:45 +10:00
Rohan Mitchell
7a36e92592 Merge branch 'unique-enterprise-names' into combined/unique-enterprise-names_parallel-fuubars_rob-github-fixes_optimise-shopfront 2015-06-24 10:09:35 +10:00
Rohan Mitchell
2d79177bb5 When a registering an enterprise with a non-unique name, display an error message. 2015-06-24 10:08:34 +10:00
Rohan Mitchell
f51a9679f2 Merge branch 'optimise-shopfront' into combined/unique-enterprise-names_parallel-fuubars_rob-github-fixes 2015-06-19 11:34:41 +10:00
Rohan Mitchell
173f4c0f03 Merge branch 'rob-github-fixes' into combined/unique-enterprise-names_parallel-fuubars_rob-github-fixes 2015-06-19 11:32:55 +10:00
Rohan Mitchell
116565fa0f Merge branch 'parallel-fuubars' into combined/unique-enterprise-names_parallel-fuubars_rob-github-fixes 2015-06-19 11:32:44 +10:00
Rohan Mitchell
52887dc699 Merge branch 'unique-enterprise-names' into combined/unique-enterprise-names_parallel-fuubars_rob-github-fixes 2015-06-19 11:32:25 +10:00
Rohan Mitchell
89a571d497 Fix spec coupled to variant overrides 2015-06-18 16:05:35 +10:00
Rohan Mitchell
ff4ee16f06 Fix currency issues, changed logo path 2015-06-18 15:38:46 +10:00
Rohan Mitchell
ae3a69c1af Put in debugging for failing push-to-prod step (sigh) 2015-06-18 15:29:05 +10:00
Continuous Integration
6d423ac990 Auto-merge from CI [skip ci] 2015-06-18 15:20:12 +10:00
Rohan Mitchell
aa0a031fa0 Fetch indexed variant overrides in one go 2015-06-18 15:03:24 +10:00
Rohan Mitchell
dd2f6d6430 Extract VariantOverride fetching into ScopeVariantToHub#scope 2015-06-18 14:45:40 +10:00
Rohan Mitchell
ce0de6e1dc Fix push to production - needs includes for get_ofn_commit 2015-06-18 14:25:48 +10:00
Rohan Mitchell
7cc2bc4fde Perform variant override scoping on product/variant by external class. Centralise this so we can load everything in one go. 2015-06-18 13:11:11 +10:00
Rohan Mitchell
6ed9a2620c Use indexed_fees_for for product price 2015-06-18 13:07:52 +10:00
Rohan Mitchell
860183e675 Do not double-display deployment output 2015-06-18 12:41:47 +10:00
Rohan Mitchell
1cd9ee399f Cloak shops page 2015-06-18 10:19:18 +10:00
Rohan Mitchell
b5d841562d Getting OFN commit should retrieve from meta-data 2015-06-18 10:02:15 +10:00
Rohan Mitchell
9f883db25b Really show deploy output in real time 2015-06-18 09:57:21 +10:00
Rohan Mitchell
389ac07bd7 Store commit at start of build process and use it throughout 2015-06-17 16:25:34 +10:00
Rohan Mitchell
4da69f78cb Use fuubar with parallel specs for advance notice of failures 2015-06-17 15:22:08 +10:00
Rohan Mitchell
c5526c78d9 Make enterprise name unique 2015-06-17 15:01:46 +10:00
Rohan Mitchell
470f10a828 Merge branch 'master' into rob-github-fixes 2015-06-16 19:00:54 +10:00
Rohan Mitchell
0dbecce65d Optimise relatives including self - minor improvement 2015-06-16 17:43:13 +10:00
200 changed files with 3266 additions and 1661 deletions

View File

@@ -1,4 +1,4 @@
--format progress
--format Fuubar
--format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log
--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
--tag ~performance

View File

@@ -50,6 +50,7 @@ gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg'
gem 'angularjs-file-upload-rails', '~> 1.1.0'
gem 'roadie-rails', '~> 1.0.3'
gem 'figaro'
gem 'blockenspiel'
gem 'acts-as-taggable-on', '~> 3.4'
gem 'foreigner'
@@ -95,6 +96,7 @@ group :test, :development do
gem 'letter_opener'
gem 'timecop'
gem 'poltergeist'
gem 'rspec-retry'
gem 'json_spec'
gem 'unicorn-rails'
end

View File

@@ -165,6 +165,7 @@ GEM
bcrypt (3.1.7)
bcrypt-ruby (3.1.5)
bcrypt (>= 3.1.3)
blockenspiel (0.4.5)
bugsnag (1.5.2)
httparty (>= 0.6, < 1.0)
multi_json (~> 1.0)
@@ -461,6 +462,8 @@ GEM
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-retry (0.4.2)
rspec-core
ruby-hmac (0.4.0)
ruby-progressbar (1.7.1)
safe_yaml (0.9.5)
@@ -546,6 +549,7 @@ DEPENDENCIES
angularjs-rails (= 1.2.13)
awesome_print
aws-sdk
blockenspiel
bugsnag
capybara
coffee-rails (~> 3.2.1)
@@ -597,6 +601,7 @@ DEPENDENCIES
representative_view
roadie-rails (~> 1.0.3)
rspec-rails
rspec-retry
sass (~> 3.3)
sass-rails (~> 3.2.3)
shoulda-matchers

View File

@@ -12,10 +12,15 @@ We're part of global movement - get involved!
* Find out more and join in the conversation - http://openfoodnetwork.org
## Getting started
Below are instructions for setting up a development environment for Open Food Network. If you're interested in provisioning a server, see [the project's Ansible playbooks](https://github.com/openfoodfoundation/ofn_deployment).
## Dependencies
* Rails 3.2.x
* Ruby >= 1.9.3
* Ruby 1.9.3
* PostgreSQL database
* PhantomJS (for testing)
* See Gemfile for a list of gems required
@@ -32,19 +37,20 @@ You can view the code at:
You can download the source with the command:
git clone git@github.com:openfoodfoundation/openfoodnetwork
git clone https://github.com/openfoodfoundation/openfoodnetwork.git
## Get it running
For those new to Rails, the following tutorial will help get you up to speed with configuring a Rails environment: http://guides.rubyonrails.org/getting_started.html .
First, check your dependencies: Ensure that you have Ruby 1.9.x installed:
First, check your dependencies: Ensure that you have Ruby >= 1.9.3 installed:
ruby --version
Install the project's gem dependencies:
cd openfoodnetwork
bundle install
Configure the site:

View File

@@ -30,7 +30,7 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [
variant: { name: "Variant", visible: true }
quantity: { name: "Quantity", visible: true }
max: { name: "Max", visible: true }
unit_value: { name: "Weight/Volume", visible: false }
final_weight_volume: { name: "Weight/Volume", visible: false }
price: { name: "Price", visible: false }
$scope.initialise = ->
$scope.initialiseVariables()
@@ -166,10 +166,10 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [
$scope.weightAdjustedPrice = (lineItem, oldValue) ->
if oldValue <= 0
oldValue = lineItem.units_variant.unit_value
if lineItem.unit_value <= 0
lineItem.unit_value = lineItem.units_variant.unit_value
lineItem.price = lineItem.price * lineItem.unit_value / oldValue
oldValue = lineItem.units_variant.unit_value * line_item.quantity
if lineItem.final_weight_volume <= 0
lineItem.final_weight_volume = lineItem.units_variant.unit_value * lineItem.quantity
lineItem.price = lineItem.price * lineItem.final_weight_volume / oldValue
#$scope.bulk_order_form.line_item.price.$setViewValue($scope.bulk_order_form.line_item.price.$viewValue)
$scope.unitValueLessThanZero = (lineItem) ->
@@ -178,6 +178,13 @@ angular.module("ofn.admin").controller "AdminOrderMgmtCtrl", [
else
false
$scope.updateOnQuantity = (lineItem, oldQuantity) ->
if lineItem.quantity <= 0
lineItem.quantity = 1
# reset price to original unit value
lineItem.price = lineItem.price * (oldQuantity * lineItem.units_variant.unit_value) / lineItem.final_weight_volume
lineItem.final_weight_volume = lineItem.units_variant.unit_value * lineItem.quantity
$scope.$watch "orderCycleFilter", (newVal, oldVal) ->
unless $scope.orderCycleFilter == "0" || angular.equals(newVal, oldVal)
$scope.startDate = $scope.orderCyclesByID[$scope.orderCycleFilter].first_order

View File

@@ -2,6 +2,7 @@ angular.module("admin.enterprises").factory 'PermalinkChecker', ($q, $http) ->
new class PermalinkChecker
deferredRequest: null
deferredAbort: null
MAX_PERMALINK_LENGTH: 255
check: (permalink) =>
@abort(@deferredAbort) if @deferredRequest && @deferredRequest.promise
@@ -15,9 +16,14 @@ angular.module("admin.enterprises").factory 'PermalinkChecker', ($q, $http) ->
timeout: deferredAbort.promise
)
.success( (data) =>
deferredRequest.resolve
permalink: data
available: "Available"
if data.length > @MAX_PERMALINK_LENGTH || !data.match(/^[\w-]+$/)
deferredRequest.resolve
permalink: permalink
available: "Error"
else
deferredRequest.resolve
permalink: data
available: "Available"
).error (data,status) =>
if status == 409
deferredRequest.resolve

View File

@@ -19,6 +19,7 @@ angular.module("ofn.admin").factory "BulkProducts", (PagedFetcher, dataFetcher)
# when a respond_overrride for the clone action is used.
id = data.product.id
dataFetcher("/api/products/" + id + "?template=bulk_show").then (newProduct) =>
@unpackProduct newProduct
@insertProductAfter(product, newProduct)
updateVariantLists: (serverProducts, productsWithUnsavedVariants) ->

View File

@@ -15,6 +15,7 @@
#= require ../shared/bindonce.min.js
#= require ../shared/ng-infinite-scroll.min.js
#= require ../shared/angular-local-storage.js
#= require ../shared/angular-slideables.js
#= require angularjs-file-upload

View File

@@ -1,18 +1,69 @@
Darkswarm.controller "EnterprisesCtrl", ($scope, Enterprises, Search, $document, $rootScope, HashNavigation, FilterSelectorsService, EnterpriseModal) ->
Darkswarm.controller "EnterprisesCtrl", ($scope, $rootScope, $timeout, Enterprises, Search, $document, HashNavigation, FilterSelectorsService, EnterpriseModal, enterpriseMatchesNameQueryFilter, distanceWithinKmFilter) ->
$scope.Enterprises = Enterprises
$scope.totalActive = FilterSelectorsService.totalActive
$scope.clearAll = FilterSelectorsService.clearAll
$scope.filterText = FilterSelectorsService.filterText
$scope.FilterSelectorsService = FilterSelectorsService
$scope.totalActive = FilterSelectorsService.totalActive
$scope.clearAll = FilterSelectorsService.clearAll
$scope.filterText = FilterSelectorsService.filterText
$scope.FilterSelectorsService = FilterSelectorsService
$scope.query = Search.search()
$scope.openModal = EnterpriseModal.open
$scope.activeTaxons = []
$scope.show_profiles = false
$scope.filtersActive = false
$scope.distanceMatchesShown = false
$scope.$watch "query", (query)->
Enterprises.flagMatching query
Search.search query
$rootScope.$broadcast 'enterprisesChanged'
$scope.distanceMatchesShown = false
$timeout ->
Enterprises.calculateDistance query, $scope.firstNameMatch()
$rootScope.$broadcast 'enterprisesChanged'
$rootScope.$on "enterprisesChanged", ->
$scope.filterEnterprises()
$scope.updateVisibleMatches()
# When filter settings change, this could change which name match is at the top, or even
# result in no matches. This affects the reference point that the distance matches are
# calculated from, so we need to recalculate distances.
$scope.$watch '[activeTaxons, shippingTypes, show_profiles]', ->
$timeout ->
Enterprises.calculateDistance $scope.query, $scope.firstNameMatch()
$rootScope.$broadcast 'enterprisesChanged'
, true
$rootScope.$on "$locationChangeSuccess", (newRoute, oldRoute) ->
if HashNavigation.active "hubs"
$document.scrollTo $("#hubs"), 100, 200
$scope.filterEnterprises = ->
es = Enterprises.hubs
$scope.nameMatches = enterpriseMatchesNameQueryFilter(es, true)
$scope.distanceMatches = enterpriseMatchesNameQueryFilter(es, false)
$scope.distanceMatches = distanceWithinKmFilter($scope.distanceMatches, 50)
$scope.updateVisibleMatches = ->
$scope.visibleMatches = if $scope.nameMatches.length == 0 || $scope.distanceMatchesShown
$scope.nameMatches.concat $scope.distanceMatches
else
$scope.nameMatches
$scope.showDistanceMatches = ->
$scope.distanceMatchesShown = true
$scope.updateVisibleMatches()
$scope.firstNameMatch = ->
if $scope.nameMatchesFiltered?
$scope.nameMatchesFiltered[0]
else
undefined

View File

@@ -1,4 +1,5 @@
Darkswarm.controller "LineItemCtrl", ($scope)->
$scope.$watch "line_item.quantity", (newValue, oldValue)->
$scope.$watch '[line_item.quantity, line_item.max_quantity]', (newValue, oldValue)->
if newValue != oldValue
$scope.Cart.orderChanged()
, true

View File

@@ -1,6 +1,4 @@
# TODO this SUCKS. Fix it
Darkswarm.controller "OrderCycleCtrl", ($scope, OrderCycle, $timeout) ->
Darkswarm.controller "OrderCycleCtrl", ($scope, $timeout, OrderCycle) ->
$scope.order_cycle = OrderCycle.order_cycle
$scope.OrderCycle = OrderCycle
@@ -9,11 +7,26 @@ Darkswarm.controller "OrderCycleCtrl", ($scope, OrderCycle, $timeout) ->
# That takes an expression instead of a trigger, and binds to that
$timeout =>
if !$scope.OrderCycle.selected()
$("#order_cycle_id").trigger("openTrigger")
$("#order_cycle_id").trigger("openTrigger")
Darkswarm.controller "OrderCycleChangeCtrl", ($scope, OrderCycle, Products, $timeout) ->
Darkswarm.controller "OrderCycleChangeCtrl", ($scope, $timeout, OrderCycle, Products, Variants, Cart) ->
# Track previous order cycle id for use with revertOrderCycle()
$scope.previous_order_cycle_id = OrderCycle.order_cycle.order_cycle_id
$scope.$watch 'order_cycle.order_cycle_id', (newValue, oldValue)->
$scope.previous_order_cycle_id = oldValue
$scope.changeOrderCycle = ->
OrderCycle.push_order_cycle Products.update
OrderCycle.push_order_cycle $scope.orderCycleChanged
$timeout ->
$("#order_cycle_id").trigger("closeTrigger")
$("#order_cycle_id").trigger("closeTrigger")
$scope.revertOrderCycle = ->
$scope.order_cycle.order_cycle_id = $scope.previous_order_cycle_id
$scope.orderCycleChanged = ->
# push_order_cycle clears the cart server-side. Here we call Cart.clear() to clear the
# client-side cart.
Variants.clear()
Cart.clear()
Products.update()

View File

@@ -10,6 +10,7 @@ window.Darkswarm = angular.module("Darkswarm", ["ngResource",
'google-maps',
'duScroll',
'angularFileUpload',
'angularSlideables'
]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
$httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
$httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')

View File

@@ -0,0 +1,16 @@
Darkswarm.directive "ofnChangeHub", (CurrentHub, Cart) ->
# Compares scope.hub with CurrentHub. Will trigger an confirmation if they are different,
# and Cart isn't empty
restrict: "A"
scope:
hub: "=ofnChangeHub"
link: (scope, elm, attr)->
cart_will_need_emptying = ->
CurrentHub.hub?.id and CurrentHub.hub.id isnt scope.hub.id and !Cart.empty()
if cart_will_need_emptying()
elm.bind 'click', (ev)->
if confirm "Are you sure? This will change your selected hub and remove any items in your shopping cart."
Cart.clear()
else
ev.preventDefault()

View File

@@ -0,0 +1,22 @@
Darkswarm.directive "ofnChangeOrderCycle", (OrderCycle, Cart, storage) ->
# Compares chosen order cycle with pre-set OrderCycle. Will trigger
# a confirmation if they are different, and Cart isn't empty
restrict: "A"
scope: true
link: (scope, elm, attr)->
order_cycle_id = ->
parseInt elm.val()
cart_needs_emptying = ->
OrderCycle.order_cycle?.order_cycle_id && OrderCycle.order_cycle.order_cycle_id != order_cycle_id() && !Cart.empty()
elm.bind 'change', (ev)->
if cart_needs_emptying()
if confirm "Are you sure? This will change your selected order cycle and remove any items in your shopping cart."
Cart.clear()
scope.changeOrderCycle()
else
scope.$apply ->
scope.revertOrderCycle()
else
scope.changeOrderCycle()

View File

@@ -1,13 +0,0 @@
Darkswarm.directive "ofnEmptiesCart", (CurrentHub, Cart, Navigation, storage) ->
# Compares scope.hub with CurrentHub. Will trigger an confirmation if they are different,
# and Cart isn't empty
restrict: "A"
scope:
hub: "=ofnEmptiesCart"
link: (scope, elm, attr)->
if CurrentHub.hub?.id and CurrentHub.hub.id isnt scope.hub.id and !Cart.empty()
elm.bind 'click', (ev)->
ev.preventDefault()
if confirm "Are you sure? This will change your selected Hub and remove any items in your shopping cart."
storage.clearAll() # One day this will have to be moar GRANULAR
Navigation.go scope.hub.path

View File

@@ -0,0 +1,5 @@
Darkswarm.directive "integer", ->
restrict: 'A'
link: (scope, elem, attr) ->
elem.bind 'input', ->
elem.val Math.round(elem.val())

View File

@@ -2,10 +2,9 @@ Darkswarm.directive 'scrollAfterLoad', ($timeout, $location, $document)->
# Scroll to an element on page load
restrict: "A"
link: (scope, element, attr) ->
if scope.$last is true
$(window).load ->
$timeout ->
elem = $("##{$location.hash()}")
if elem.length > 0
$document.scrollTo elem , 100, 200, (x)->
x * (2 - x)
elem = element
$(window).load ->
$timeout ->
if elem?
$document.scrollTo elem, 100, 200, (x) ->
x * (2 - x)

View File

@@ -0,0 +1,5 @@
Darkswarm.filter 'distanceWithinKm', ->
(enterprises, range) ->
enterprises ||= []
enterprises.filter (enterprise) ->
enterprise.distance / 1000 <= range

View File

@@ -0,0 +1,4 @@
Darkswarm.filter 'enterpriseMatchesNameQuery', ->
(enterprises, matches_name_query) ->
enterprises.filter (enterprise) ->
enterprise.matches_name_query == matches_name_query

View File

@@ -1,4 +1,4 @@
Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http, storage)->
# Handles syncing of current cart/order state to server
new class Cart
dirty: false
@@ -20,7 +20,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
$http.post('/orders/populate', @data()).success (data, status)=>
@saved()
.error (response, status)=>
# TODO what shall we do here?
@scheduleRetry()
data: =>
variants = {}
@@ -30,6 +30,12 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
max_quantity: li.max_quantity
{variants: variants}
scheduleRetry: =>
console.log "Error updating cart: #{status}. Retrying in 3 seconds..."
$timeout =>
console.log "Retrying cart update"
@orderChanged()
, 3000
saved: =>
@dirty = false
@@ -63,6 +69,10 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
exists = @line_items.some (li)-> li.variant == variant
@create_line_item(variant) unless exists
clear: ->
@line_items = []
storage.clearAll() # One day this will have to be moar GRANULAR
create_line_item: (variant)->
variant.extended_name = @extendedVariantName(variant)
variant.line_item =

View File

@@ -26,7 +26,11 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
RegistrationService.select('about')
).error((data) =>
Loading.clear()
alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.')
if data?.errors?
errors = ("#{k.capitalize()} #{v[0]}" for k, v of data.errors when v.length > 0)
alert "Failed to create your enterprise.\n" + errors.join('\n')
else
alert('Failed to create your enterprise.\nPlease ensure all fields are completely filled out.')
)
# RegistrationService.select('about')

View File

@@ -1,4 +1,4 @@
Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter)->
Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter, Matcher, Geo, $rootScope)->
new class Enterprises
enterprises_by_id: {}
constructor: ->
@@ -28,3 +28,36 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer,
Dereferencer.dereference enterprise.taxons, Taxons.taxons_by_id
Dereferencer.dereference enterprise.supplied_taxons, Taxons.taxons_by_id
flagMatching: (query) ->
for enterprise in @enterprises
enterprise.matches_name_query = if query? && query.length > 0
Matcher.match([enterprise.name], query)
else
false
calculateDistance: (query, firstMatching) ->
if query?.length > 0
if firstMatching?
@setDistanceFrom firstMatching
else
@calculateDistanceGeo query
else
@resetDistance()
calculateDistanceGeo: (query) ->
Geo.geocode query, (results, status) =>
$rootScope.$apply =>
if status == Geo.OK
#console.log "Geocoded #{query} -> #{results[0].geometry.location}."
@setDistanceFrom results[0].geometry.location
else
console.log "Geocoding failed for the following reason: #{status}"
@resetDistance()
setDistanceFrom: (locatable) ->
for enterprise in @enterprises
enterprise.distance = Geo.distanceBetween enterprise, locatable
$rootScope.$broadcast 'enterprisesChanged'
resetDistance: ->
enterprise.distance = null for enterprise in @enterprises

View File

@@ -0,0 +1,23 @@
Darkswarm.service "Geo", ->
new class Geo
OK: google.maps.GeocoderStatus.OK
# Usage:
# Geo.geocode address, (results, status) ->
# if status == Geo.OK
# console.log results[0].geometry.location
# else
# console.log "Error: #{status}"
geocode: (address, callback) ->
geocoder = new google.maps.Geocoder()
geocoder.geocode {'address': address, 'region': "<%= Spree::Country.find_by_id(Spree::Config[:default_country_id]).iso %>"}, callback
distanceBetween: (src, dst) ->
google.maps.geometry.spherical.computeDistanceBetween @toLatLng(src), @toLatLng(dst)
# Wrap an object in a google.maps.LatLng if it has not been already
toLatLng: (locatable) ->
if locatable.lat?
locatable
else
new google.maps.LatLng locatable.latitude, locatable.longitude

View File

@@ -9,8 +9,8 @@ Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter) ->
# Adding methods to each enterprise
extend: (enterprise) ->
new class MapMarker
# We're whitelisting attributes because GMaps tries to crawl
# our data, and our data is recursive, so it breaks
# We cherry-pick attributes because GMaps tries to crawl
# our data, and our data is cyclic, so it breaks
latitude: enterprise.latitude
longitude: enterprise.longitude
icon: enterprise.icon

View File

@@ -10,12 +10,26 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro
update: =>
@loading = true
@products = $resource("/shop/products").query (products)=>
@extend() && @dereference()
@products = []
$resource("/shop/products").query (products)=>
@products = products
@extend()
@dereference()
@registerVariants()
@registerVariantsWithCart()
@loading = false
@
extend: ->
for product in @products
if product.variants?.length > 0
prices = (v.price for v in product.variants)
product.price = Math.min.apply(null, prices)
product.hasVariants = product.variants?.length > 0
product.primaryImage = product.images[0]?.small_url if product.images
product.primaryImageOrMissing = product.primaryImage || "/assets/noimage/small.png"
product.largeImage = product.images[0]?.large_url if product.images
dereference: ->
for product in @products
@@ -42,14 +56,3 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Pro
for variant in product.variants
Cart.register_variant variant
Cart.register_variant product.master if product.master
extend: ->
for product in @products
if product.variants?.length > 0
prices = (v.price for v in product.variants)
product.price = Math.min.apply(null, prices)
product.hasVariants = product.variants?.length > 0
product.primaryImage = product.images[0]?.small_url if product.images
product.primaryImageOrMissing = product.primaryImage || "/assets/noimage/small.png"
product.largeImage = product.images[0]?.large_url if product.images

View File

@@ -1,6 +1,10 @@
Darkswarm.factory 'Variants', ->
new class Variants
variants: {}
clear: ->
@variants = {}
register: (variant)->
@variants[variant.id] ||= @extend variant

View File

@@ -0,0 +1,2 @@
String.prototype.capitalize = ->
this.charAt(0).toUpperCase() + this.slice(1)

View File

@@ -0,0 +1,55 @@
/*
* Angular Slideables - A "pure" Angular implementation of jQuery-style slideToggle()
* Source: https://github.com/EricWVGG/AngularSlideables
* By Eric Jacobsen, used under MIT licence
*/
angular.module('angularSlideables', [])
.directive('slideable', function () {
return {
restrict:'C',
compile: function (element, attr) {
// wrap tag
var contents = element.html();
element.html('<div class="slideable_content" style="margin:0 !important; padding:0 !important" >' + contents + '</div>');
return function postLink(scope, element, attrs) {
// default properties
attrs.duration = (!attrs.duration) ? '1s' : attrs.duration;
attrs.easing = (!attrs.easing) ? 'ease-in-out' : attrs.easing;
element.css({
'overflow': 'hidden',
'height': '0px',
'transitionProperty': 'height',
'transitionDuration': attrs.duration,
'transitionTimingFunction': attrs.easing
});
};
}
};
})
.directive('slideToggle', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var target, content;
attrs.expanded = false;
element.bind('click', function() {
if (!target) target = document.querySelector(attrs.slideToggle);
if (!content) content = target.querySelector('.slideable_content');
if(!attrs.expanded) {
content.style.border = '1px solid rgba(0,0,0,0)';
var y = content.clientHeight;
content.style.border = 0;
target.style.height = y + 'px';
} else {
target.style.height = '0px';
}
attrs.expanded = !attrs.expanded;
});
}
}
});

0
app/assets/javascripts/shared/ng-tags-input.min.js vendored Executable file → Normal file
View File

View File

@@ -1,4 +1,5 @@
%ng-include{src: "'partials/enterprise_header.html'"}
%ng-include{src: "'partials/enterprise_details.html'"}
%ng-include{src: "'partials/hub_details.html'"}
%ng-include{src: "'partials/producer_details.html'"}
%ng-include{src: "'partials/close.html'"}

View File

@@ -2,7 +2,7 @@
.highlight-top.row
.small-12.medium-7.large-8.columns
%h3{"ng-if" => "enterprise.is_distributor"}
%a{"bo-href" => "enterprise.path", "ofn-empties-cart" => "enterprise"}
%a{"bo-href" => "enterprise.path", "ofn-change-hub" => "enterprise"}
%i{"ng-class" => "enterprise.icon_font"}
%span{"bo-text" => "enterprise.name"}
%h3{"ng-if" => "!enterprise.is_distributor", "ng-class" => "{'is_producer' : enterprise.is_primary_producer}"}
@@ -10,4 +10,4 @@
%span{"bo-text" => "enterprise.name"}
.small-12.medium-5.large-4.columns.text-right.small-only-text-left
%p{"bo-bind" => "[enterprise.address.city, enterprise.address.state_name] | printArray"}
%img.hero-img{"bo-src" => "enterprise.promo_image"}
%img.hero-img{"bo-src" => "enterprise.promo_image"}

View File

@@ -1,16 +1,15 @@
.row.pad-top{bindonce: true, "ng-if" => "enterprise.hubs.length > 0 && enterprise.is_distributor"}
.cta-container.small-12.columns
%label
Shop for
%strong{"bo-text" => "enterprise.name"}
%label
Shop for
%strong{"bo-text" => "enterprise.name"}
products at:
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs",
"bo-href" => "hub.path",
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs",
"bo-href" => "hub.path",
"bo-class" => "{primary: hub.active, secondary: !hub.active}",
"ofn-empties-cart" => "hub"}
"ofn-change-hub" => "hub"}
%i.ofn-i_033-open-sign{"bo-if" => "hub.active"}
%i.ofn-i_032-closed-sign{"bo-if" => "!hub.active"}
.hub-name{"bo-text" => "hub.name"}
.hub-name{"bo-text" => "hub.name"}
.button-address{"bo-bind" => "[hub.address.city, hub.address.state_name] | printArray"}
/ %i.ofn-i_007-caret-right

View File

@@ -5,18 +5,18 @@
%label{"active-table-hub-link" => "enterprise", change: "Change shop to:", shop: "Shop now at:"}
.small-8.columns.right
%label.right{"bo-if" => "enterprise.pickup || enterprise.delivery"}
Delivery options:
%span{"bo-if" => "enterprise.pickup"}
Delivery options:
%span{"bo-if" => "enterprise.pickup"}
%i.ofn-i_038-takeaway
Pickup
%span{"bo-if" => "enterprise.delivery"}
%span{"bo-if" => "enterprise.delivery"}
%i.ofn-i_039-delivery
Delivery
.row
.columns.small-12
%a.cta-hub{"bo-href" => "enterprise.path",
%a.cta-hub{"bo-href" => "enterprise.path",
"ng-class" => "{primary: enterprise.active, secondary: !enterprise.active}",
"ofn-empties-cart" => "enterprise"}
"ofn-change-hub" => "enterprise"}
%i.ofn-i_033-open-sign{"bo-if" => "enterprise.active"}
%i.ofn-i_032-closed-sign{"bo-if" => "!enterprise.active"}
.hub-name{"bo-text" => "enterprise.name"}

View File

@@ -0,0 +1,23 @@
-# Show places to buy products from this producer, when there are any
-# Do not show this for producer shops selling only their own produce,
-# Since a shopping link will already have been displayed in hub_details.html.haml
.row.active_table_row.pad-top{bindonce: true, "ng-if" => "enterprise.is_primary_producer && enterprise.hubs.length > 0 && !(enterprise.hubs.length == 1 && enterprise.hubs[0] == enterprise)"}
.columns.small-12
.row
.columns.small-12.fat
%div{"bo-if" => "enterprise.name"}
%label
Shop for
%span.turquoise{"bo-text" => "enterprise.name"}
products at:
%div.show-for-medium-up{"bo-if" => "!enterprise.name"}
&nbsp;
.row.cta-container
.columns.small-12
%a.cta-hub{"ng-repeat" => "hub in enterprise.hubs | filter:{id: '!'+enterprise.id} | orderBy:'-active'",
"bo-href" => "hub.path", "ofn-empties-cart" => "hub",
"bo-class" => "{primary: hub.active, secondary: !hub.active}"}
%i.ofn-i_033-open-sign{"bo-if" => "hub.active"}
%i.ofn-i_032-closed-sign{"bo-if" => "!hub.active"}
.hub-name{"bo-text" => "hub.name"}
.button-address{"bo-bind" => "[hub.address.city, hub.address.state_name] | printArray"}

View File

@@ -11,6 +11,7 @@
.small-5.medium-3.large-3.columns.text-right{"bo-if" => "!variant.product.group_buy"}
%input{type: :number,
integer: true,
value: nil,
min: 0,
placeholder: "0",
@@ -26,14 +27,17 @@
%span.bulk-input
%input.bulk.first{type: :number,
value: nil,
integer: true,
min: 0,
"ng-model" => "variant.line_item.quantity",
placeholder: "min",
"ofn-disable-scroll" => true,
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"}
%span.bulk-input{"bo-if" => "variant.product.group_buy"}
%span.bulk-input
%input.bulk.second{type: :number,
"ng-disabled" => "!variant.line_item.quantity",
integer: true,
min: 0,
"ng-model" => "variant.line_item.max_quantity",
placeholder: "max",

View File

@@ -55,7 +55,7 @@
100%
opacity: 1
@-webkit-keyframes spin
@-webkit-keyframes spin
0%
-webkit-transform: rotate(0deg)
transform: rotate(0deg)
@@ -104,12 +104,10 @@
.animate-repeat
-webkit-transform: translateZ(0)
transform: translateZ(0)
&.ng-move, &.ng-enter, &.ng-leave
-webkit-transition: all 300ms linear
transition: all 300ms linear
-webkit-transition: all 300ms linear
transition: all 300ms linear
&.ng-leave
opacity: 1
&.ng-leave-active
@@ -178,7 +176,7 @@ product.animate-repeat
overflow: hidden
max-height: 0
opacity: 0 !important
// &.ng-hide-add-active, &.ng-hide-remove-active
&.ng-hide-add, &.ng-hide-remove
@@ -197,7 +195,7 @@ product.animate-repeat
&.ng-hide
opacity: 0 !important
// &.ng-hide-add-active, &.ng-hide-remove-active
&.ng-hide-add, &.ng-hide-remove
@@ -206,8 +204,8 @@ product.animate-repeat
it as hidden. */
display: block !important
@mixin csstrans
@@ -217,7 +215,3 @@ product.animate-repeat
-o-transition: all 300ms ease
transition: all 300ms ease
-webkit-transform-style: preserve-3d

View File

@@ -36,11 +36,14 @@ footer
border: 1px solid rgba($dark-grey, 0.35)
background-image: url("/assets/tile-wide.png")
background-position: center center
background-color: #bbb
padding: 12px 0 8px 0
display: block
&, & *
@include csstrans
color: #333
strong
letter-spacing: 0.5px
&:hover, &:active, &:focus
text-decoration: none
border-color: white

View File

@@ -43,6 +43,11 @@
@include bodyFont
font-size: 1.5rem
font-weight: 300
@media all and (max-width: 768px)
h2
font-size: 52px
p
font-size: 1.3rem
a.text-vbig i
font-size: 75px
@@ -71,7 +76,7 @@
width: 64px
height: 64px
margin: 0 auto
background-color: $australia-orange
background-color: $brand-colour
background-position: center center
background-repeat: no-repeat
background-size: auto 100%
@@ -85,10 +90,13 @@
h2
font-size: 70px
font-weight: 300
color: $australia-orange
color: $brand-colour
@media all and (max-width: 640px)
font-size: 45px
a
color: $australia-orange
color: $brand-colour
.home-icon-box-bottom
margin-top: 1rem
@@ -102,8 +110,8 @@
padding-left: 1rem
padding-right: 1rem
h4
color: $australia-orange
border-bottom: 2px solid lighten($australia-orange, 20%)
color: $brand-colour
border-bottom: 2px solid lighten($brand-colour, 20%)
text-align: center
padding: 1rem 0
margin: 1.5rem 0
@@ -113,6 +121,8 @@
font-weight: 300
font-size: 45px
margin-bottom: 2rem
@media all and (max-width: 830px)
font-size: 35px
#stats.pane
.row.header
@@ -136,4 +146,4 @@
font-weight: 300
#shops-signup.pane
background-color: $australia-orange
background-color: $brand-colour

View File

@@ -24,5 +24,10 @@
@media all and (min-width: 768px)
margin-top: 10rem
img
max-width: 80%
margin-bottom: 5rem
max-width: 45%
@media all and (min-height: 500px)
max-width: 80%
margin-bottom: 2rem
@media all and (min-width: 768px)
margin-bottom: 5rem

View File

@@ -5,3 +5,6 @@
background-color: lighten($ofn-grey, 43%)
@include panepadding
@include sidepaddingSm
.name-matches, .distance-matches
margin-top: 4em

View File

@@ -28,7 +28,7 @@ nav
// Default overrides - big menu
.top-bar-section .has-dropdown > a
padding-right: $topbar-height / 2 !important
padding-right: $topbar-height / 3 !important
i.ofn-i_022-cog
font-size: 24px
@@ -51,7 +51,7 @@ nav
opacity: 1
.nav-branded
color: $australia-orange
color: $brand-colour
span
font-size: 13px
.nav-primary
@@ -84,6 +84,9 @@ nav
.tab-bar .menu-icon span::after
box-shadow: 0 0 0 1px black, 0 7px 0 1px black, 0 14px 0 1px black
.tab-bar .ofn-logo
padding: 9px 0 0 9px
.left-off-canvas-menu
background-color: white
@@ -99,7 +102,7 @@ nav
color: rgba(0, 0, 0, 0.9)
&:hover
background-color: transparent
color: $australia-orange
color: $brand-colour
@include transition(all 0.3s ease-in-out)
.off-canvas-wrap.move-right ul.off-canvas-list i
@@ -110,10 +113,10 @@ nav
// Responsive
@media screen and (max-width: 1350px)
@media screen and (max-width: 1450px)
nav .top-bar-section
ul li a, .has-dropdown > a
padding: 0 $topbar-height / 4 !important
padding: 0 $topbar-height / 8 !important
ul.center
margin-left: -24px

View File

@@ -7,7 +7,7 @@
@mixin tiledPane
background-image: url("/assets/tile-wide.png")
background-color: $australia-orange
background-color: $brand-colour
background-position: center center
@include paneWhiteText

View File

@@ -10,15 +10,23 @@
border: 1px solid rgba($dark-grey, 0.35)
border-left: none
border-right: none
background-color: lighten($ofn-grey, 15%)
background-color: #bbb
background-image: url("/assets/tile-wide.png")
background-position: center center
padding: 12px 0 8px 0
margin: 0
h6
@media all and (max-width: 480px)
font-size: 10px
line-height: 24px
a.alert-cta
&, & *
@include csstrans
color: #333
strong
letter-spacing: 0.5px
&:hover, &:active, &:focus
&, & *
text-decoration: none

View File

@@ -38,6 +38,7 @@
padding: 4px 12px
color: #fff
.buttons
margin-bottom: 0.1em
.button
height: auto
top: 0px

View File

@@ -20,13 +20,13 @@
#producer-case-studies, #shops-case-studies
padding-top: 100px
padding-bottom: 100px
background-color: $australia-orange
background-color: $brand-colour
background-image: url("/assets/hubs-bg.jpg")
background-position: center center
-webkit-filter: brightness(1.1)
filter: brightness(1.1)
h2
color: $australia-orange
color: $brand-colour
font-size: 3rem
.case-study
background-color: rgba(255, 255, 255, 0.5)
@@ -42,7 +42,7 @@
@media all and (min-width: 640px)
text-align: left
h4, a
color: $australia-orange
color: $brand-colour
a
&, & *
@include csstrans
@@ -68,9 +68,9 @@ table.signup-table.hubs-table, table.signup-table.producers-table
background-color: lighten($ofn-grey, 41%)
td:last-child
&, & i
color: $australia-orange
border-bottom: 1px solid rgba($australia-orange, 0.3)
background-color: lighten($australia-orange, 48%)
color: $brand-colour
border-bottom: 1px solid rgba($brand-colour, 0.3)
background-color: lighten($brand-colour, 48%)
thead
background-color: transparent
tr
@@ -85,7 +85,7 @@ table.signup-table.hubs-table, table.signup-table.producers-table
td:last-child
&, & *
color: white
background: $australia-orange
background: $brand-colour
h5
text-transform: uppercase
color: $ofn-grey
@@ -107,7 +107,7 @@ table.signup-table.hubs-table, table.signup-table.producers-table
td:last-child
&, & *
color: white
background: $australia-orange
background: $brand-colour
h2
.text-small
text-transform: uppercase

View File

@@ -1,17 +1,27 @@
@import "foundation/functions"
@import "foundation/components/global"
$australia-orange: rgba(242, 112, 82, 1)
// Brand guide colours:
// International: #81c26e
// Australia: #f35746
// Africa: #f35e32
// South Africa: #f9a72b
// Norway: #4b83cc
// Scandanavia: #0c8bbc
// UK: #e6373f
$brand-colour: #f27052
// Topbar
$topbar-height: rem-calc(75)
$topbar-link-padding: $topbar-height / 2
$topbar-link-padding: $topbar-height / 3
$topbar-bg: $white
$topbar-bg-color: $topbar-bg
$topbar-link-color: $black
$topbar-link-color-hover: $australia-orange
$topbar-link-color-hover: $brand-colour
$topbar-link-color-active: $black
$topbar-link-color-active-hover: $white
$topbar-link-bg-hover: $white

View File

@@ -1,15 +1,26 @@
module Admin
class ContentsController < Spree::Admin::BaseController
def edit
@preferences_home = [:home_tagline_cta, :home_whats_happening]
@preferences_footer = [:footer_facebook_url, :footer_twitter_url, :footer_instagram_url, :footer_linkedin_url, :footer_googleplus_url, :footer_pinterest_url, :footer_email, :footer_links_md, :footer_about_url]
@preference_sections = [{name: 'Header', preferences: [:logo, :logo_mobile, :logo_mobile_svg]},
{name: 'Home page', preferences: [:home_hero, :home_show_stats]},
{name: 'Producer signup page', preferences: [:producer_signup_pricing_table_html, :producer_signup_case_studies_html, :producer_signup_detail_html]},
{name: 'Hub signup page', preferences: [:hub_signup_pricing_table_html, :hub_signup_case_studies_html, :hub_signup_detail_html]},
{name: 'Group signup page', preferences: [:group_signup_pricing_table_html, :group_signup_case_studies_html, :group_signup_detail_html]},
{name: 'Footer', preferences: [:footer_logo,
:footer_facebook_url, :footer_twitter_url, :footer_instagram_url, :footer_linkedin_url, :footer_googleplus_url, :footer_pinterest_url,
:footer_email, :footer_links_md, :footer_about_url, :footer_tos_url]}]
end
def update
params.each do |name, value|
next unless ContentConfig.has_preference? name
ContentConfig[name] = value
if ContentConfig.has_preference?(name) || ContentConfig.has_attachment?(name)
ContentConfig.send("#{name}=", value)
end
end
# Save any uploaded images
ContentConfig.save
flash[:success] = t(:successfully_updated, :resource => "Your content")
redirect_to main_app.edit_admin_content_path

View File

@@ -241,7 +241,7 @@ module Admin
# Overriding method on Spree's resource controller
def location_after_save
referer_path = OpenFoodNetwork::RefererParser::path(request.referer)
refered_from_edit = referer_path == main_app.edit_admin_enterprise_path(@enterprise)
refered_from_edit = referer_path =~ /\/edit$/
if params[:enterprise].key?(:producer_properties_attributes) && !refered_from_edit
main_app.admin_enterprises_path
else

View File

@@ -65,8 +65,8 @@ module Admin
end
def bulk_update
@order_cycle_set = OrderCycleSet.new(params[:order_cycle_set])
if @order_cycle_set.save
@order_cycle_set = params[:order_cycle_set] && OrderCycleSet.new(params[:order_cycle_set])
if @order_cycle_set.andand.save
redirect_to main_app.admin_order_cycles_path, :notice => 'Order cycles have been updated.'
else
render :index
@@ -132,10 +132,12 @@ module Admin
end
def remove_unauthorized_bulk_attrs
params[:order_cycle_set][:collection_attributes].each do |i, hash|
order_cycle = OrderCycle.find(hash[:id])
unless Enterprise.managed_by(spree_current_user).include?(order_cycle.andand.coordinator)
params[:order_cycle_set][:collection_attributes].delete i
if params.key? :order_cycle_set
params[:order_cycle_set][:collection_attributes].each do |i, hash|
order_cycle = OrderCycle.find(hash[:id])
unless Enterprise.managed_by(spree_current_user).include?(order_cycle.andand.coordinator)
params[:order_cycle_set][:collection_attributes].delete i
end
end
end
end

View File

@@ -23,7 +23,7 @@ class CheckoutController < Spree::CheckoutController
return if redirect_to_paypal_express_form_if_needed
end
if @order.next
if advance_order_state(@order)
state_callback(:after)
else
if @order.errors.present?
@@ -83,6 +83,16 @@ class CheckoutController < Spree::CheckoutController
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
clear_ship_address

View File

@@ -9,55 +9,6 @@ class EnterprisesController < BaseController
respond_to :js, only: :permalink_checker
def index
@enterprises = Enterprise.all
end
def suppliers
@suppliers = Enterprise.is_primary_producer
end
def distributors
@distributors = Enterprise.is_distributor
respond_to do |format|
format.js do
@distributor_details = Hash[@distributors.map { |d| [d.id, render_to_string(:partial => 'enterprises/distributor_details', :locals => {:distributor => d})] }]
end
format.html do
@distributors
end
end
end
def show
@enterprise = Enterprise.find_by_permalink(params[:id]) || Enterprise.find(params[:id])
# User can view this page if they've already chosen their distributor, or if this page
# is for a supplier, they may use it to select a distributor that sells this supplier's
# products.
unless current_distributor || @enterprise.is_primary_producer
redirect_to spree.root_path and return
end
options = {:enterprise_id => params[:id]}
options.merge(params.reject { |k,v| k == :id })
@products = []
if @enterprise.is_primary_producer
@distributors = Enterprise.distributing_any_product_of(@enterprise.supplied_products).by_name.all
end
if current_order_cycle
@searcher = Spree::Config.searcher_class.new(options)
@products = @searcher.retrieve_products
order_cycle_products = current_order_cycle.products_distributed_by(current_distributor)
@products = @products & order_cycle_products
end
end
def check_permalink
return render text: params[:permalink], status: 409 if Enterprise.find_by_permalink params[:permalink]

View File

@@ -2,12 +2,11 @@ class HomeController < BaseController
layout 'darkswarm'
def index
@num_distributors = Enterprise.is_distributor.activated.visible.count
@num_producers = Enterprise.is_primary_producer.activated.visible.count
@num_users = Spree::User.joins(:orders).merge(Spree::Order.complete).count('DISTINCT spree_users.*')
@num_orders = Spree::Order.complete.count
end
def about_us
if ContentConfig.home_show_stats
@num_distributors = Enterprise.is_distributor.activated.visible.count
@num_producers = Enterprise.is_primary_producer.activated.visible.count
@num_users = Spree::User.joins(:orders).merge(Spree::Order.complete).count('DISTINCT spree_users.*')
@num_orders = Spree::Order.complete.count
end
end
end

View File

@@ -46,10 +46,12 @@ class ShopController < BaseController
def products_for_shop
if current_order_cycle
scoper = OpenFoodNetwork::ScopeProductToHub.new(current_distributor)
current_order_cycle.
valid_products_distributed_by(current_distributor).
order(taxon_order).
each { |p| p.scope_to_hub current_distributor }.
each { |p| scoper.scope(p) }.
select { |p| !p.deleted? && p.has_stock_for_distribution?(current_order_cycle, current_distributor) }
end
end
@@ -69,9 +71,10 @@ class ShopController < BaseController
# We use the in_stock? method here instead of the in_stock scope because we need to
# look up the stock as overridden by VariantOverrides, and the scope method is not affected
# by them.
scoper = OpenFoodNetwork::ScopeVariantToHub.new(current_distributor)
Spree::Variant.
for_distribution(current_order_cycle, current_distributor).
each { |v| v.scope_to_hub current_distributor }.
each { |v| scoper.scope(v) }.
select(&:in_stock?)
end

View File

@@ -1,13 +1,18 @@
require 'csv'
require 'open_food_network/order_and_distributor_report'
require 'open_food_network/products_and_inventory_report'
require 'open_food_network/lettuce_share_report'
require 'open_food_network/group_buy_report'
require 'open_food_network/order_grouper'
require 'open_food_network/customers_report'
require 'open_food_network/users_and_enterprises_report'
require 'open_food_network/order_cycle_management_report'
require 'open_food_network/packing_report'
require 'open_food_network/sales_tax_report'
require 'open_food_network/xero_invoices_report'
require 'open_food_network/bulk_coop_report'
require 'open_food_network/payments_report'
require 'open_food_network/orders_and_fulfillments_report'
Spree::Admin::ReportsController.class_eval do
@@ -22,7 +27,8 @@ Spree::Admin::ReportsController.class_eval do
],
products_and_inventory: [
['All products', :all_products],
['Inventory (on hand)', :inventory]
['Inventory (on hand)', :inventory],
['LettuceShare', :lettuce_share]
],
customers: [
["Mailing List", :mailing_list],
@@ -31,11 +37,15 @@ Spree::Admin::ReportsController.class_eval do
order_cycle_management: [
["Payment Methods Report", :payment_methods],
["Delivery Report", :delivery]
],
packing: [
["Pack By Customer", :pack_by_customer],
["Pack By Supplier", :pack_by_supplier]
]
}
# Fetches user's distributors, suppliers and order_cycles
before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management]
before_filter :load_data, only: [:customers, :products_and_inventory, :order_cycle_management, :packing]
# Render a partial for orders and fulfillment description
respond_override :index => { :html => { :success => lambda {
@@ -47,7 +57,9 @@ Spree::Admin::ReportsController.class_eval do
render_to_string(partial: 'customers_description', layout: false, locals: {report_types: REPORT_TYPES[:customers]}).html_safe
@reports[:order_cycle_management][:description] =
render_to_string(partial: 'order_cycle_management_description', layout: false, locals: {report_types: REPORT_TYPES[:order_cycle_management]}).html_safe
} } }
@reports[:packing][:description] =
render_to_string(partial: 'packing_description', layout: false, locals: {report_types: REPORT_TYPES[:packing]}).html_safe
} } }
# Overide spree reports list.
@@ -75,19 +87,33 @@ Spree::Admin::ReportsController.class_eval do
render_report(@report.header, @report.table, params[:csv], "order_cycle_management_#{timestamp}.csv")
end
def packing
# -- Prepare date parameters
prepare_date_params params
# -- Prepare form options
my_distributors = Enterprise.is_distributor.managed_by(spree_current_user)
my_suppliers = Enterprise.is_primary_producer.managed_by(spree_current_user)
# My distributors and any distributors distributing products I supply
@distributors = my_distributors | Enterprise.with_distributed_products_outer.merge(Spree::Product.in_any_supplier(my_suppliers))
# My suppliers and any suppliers supplying products I distribute
@suppliers = my_suppliers | my_distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq
@order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC')
@report_types = REPORT_TYPES[:packing]
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::PackingReport.new spree_current_user, params
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
@table = order_grouper.table(@report.table_items)
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], "packing_#{timestamp}.csv")
end
def orders_and_distributors
params[:q] ||= {}
if params[:q][:completed_at_gt].blank?
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:completed_at_lt].blank?
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue ""
end
params[:q][:meta_sort] ||= "completed_at.desc"
prepare_date_params params
@search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q])
orders = @search.result
@@ -105,18 +131,7 @@ Spree::Admin::ReportsController.class_eval do
end
def sales_tax
params[:q] ||= {}
if params[:q][:completed_at_gt].blank?
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:completed_at_lt].blank?
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue ""
end
params[:q][:meta_sort] ||= "completed_at.desc"
prepare_date_params params
@search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q])
orders = @search.result
@@ -135,300 +150,47 @@ Spree::Admin::ReportsController.class_eval do
end
def bulk_coop
params[:q] ||= {}
if params[:q][:completed_at_gt].blank?
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:completed_at_lt].blank?
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue ""
end
params[:q][:meta_sort] ||= "completed_at.desc"
@search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q])
orders = @search.result
@line_items = orders.map { |o| o.line_items.managed_by(spree_current_user) }.flatten
# -- Prepare date parameters
prepare_date_params params
# -- Prepare form options
@distributors = Enterprise.is_distributor.managed_by(spree_current_user)
@report_type = params[:report_type]
case params[:report_type]
when "bulk_coop_supplier_report"
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::BulkCoopReport.new spree_current_user, params
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
@table = order_grouper.table(@report.table_items)
csv_file_name = "bulk_coop_#{params[:report_type]}_#{timestamp}.csv"
header = ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"]
columns = [ proc { |lis| lis.first.variant.product.supplier.name },
proc { |lis| lis.first.variant.product.name },
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
proc { |lis| lis.first.variant.full_name },
proc { |lis| lis.first.variant.weight || 0 },
proc { |lis| lis.sum { |li| li.quantity } },
proc { |lis| lis.sum { |li| li.max_quantity || 0 } },
proc { |lis| "" },
proc { |lis| "" } ]
rules = [ { group_by: proc { |li| li.variant.product.supplier },
sort_by: proc { |supplier| supplier.name } },
{ group_by: proc { |li| li.variant.product },
sort_by: proc { |product| product.name },
summary_columns: [ proc { |lis| lis.first.variant.product.supplier.name },
proc { |lis| lis.first.variant.product.name },
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
proc { |lis| "" },
proc { |lis| "" },
proc { |lis| lis.sum { |li| (li.quantity || 0) * (li.variant.weight || 0) } },
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } },
proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor },
proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] },
{ group_by: proc { |li| li.variant },
sort_by: proc { |variant| variant.full_name } } ]
when "bulk_coop_allocation"
header = ["Customer", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Total Allocated", "Remainder"]
columns = [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname },
proc { |lis| lis.first.variant.product.name },
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
proc { |lis| lis.first.variant.full_name },
proc { |lis| lis.first.variant.weight || 0 },
proc { |lis| lis.sum { |li| li.quantity } },
proc { |lis| lis.sum { |li| li.max_quantity || 0 } },
proc { |lis| "" },
proc { |lis| "" } ]
rules = [ { group_by: proc { |li| li.variant.product },
sort_by: proc { |product| product.name },
summary_columns: [ proc { |lis| "TOTAL" },
proc { |lis| lis.first.variant.product.name },
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
proc { |lis| "" },
proc { |lis| "" },
proc { |lis| lis.sum { |li| li.quantity * (li.variant.weight || 0) } },
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } },
proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) },
proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] },
{ group_by: proc { |li| li.variant },
sort_by: proc { |variant| variant.full_name } },
{ group_by: proc { |li| li.order },
sort_by: proc { |order| order.to_s } } ]
when "bulk_coop_packing_sheets"
header = ["Customer", "Product", "Variant", "Sum Total"]
columns = [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname },
proc { |lis| lis.first.variant.product.name },
proc { |lis| lis.first.variant.full_name },
proc { |lis| lis.sum { |li| li.quantity } } ]
rules = [ { group_by: proc { |li| li.variant.product },
sort_by: proc { |product| product.name } },
{ group_by: proc { |li| li.variant },
sort_by: proc { |variant| variant.full_name } },
{ group_by: proc { |li| li.order },
sort_by: proc { |order| order.to_s } } ]
when "bulk_coop_customer_payments"
header = ["Customer", "Date of Order", "Total Cost", "Amount Owing", "Amount Paid"]
columns = [ proc { |lis| lis.first.order.bill_address.firstname + " " + lis.first.order.bill_address.lastname },
proc { |lis| lis.first.order.completed_at.to_s },
proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.total } },
proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.outstanding_balance } },
proc { |lis| lis.map { |li| li.order }.uniq.sum { |o| o.payment_total } } ]
rules = [ { group_by: proc { |li| li.order },
sort_by: proc { |order| order.completed_at } } ]
else # List all line items
header = ["Supplier", "Product", "Unit Size", "Variant", "Weight", "Sum Total", "Sum Max Total", "Units Required", "Remainder"]
columns = [ proc { |lis| lis.first.variant.product.supplier.name },
proc { |lis| lis.first.variant.product.name },
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
proc { |lis| lis.first.variant.full_name },
proc { |lis| lis.first.variant.weight || 0 },
proc { |lis| lis.sum { |li| li.quantity } },
proc { |lis| lis.sum { |li| li.max_quantity || 0 } },
proc { |lis| "" },
proc { |lis| "" } ]
rules = [ { group_by: proc { |li| li.variant.product.supplier },
sort_by: proc { |supplier| supplier.name } },
{ group_by: proc { |li| li.variant.product },
sort_by: proc { |product| product.name },
summary_columns: [ proc { |lis| lis.first.variant.product.supplier.name },
proc { |lis| lis.first.variant.product.name },
proc { |lis| lis.first.variant.product.group_buy ? (lis.first.variant.product.group_buy_unit_size || 0.0) : "" },
proc { |lis| "" },
proc { |lis| "" },
proc { |lis| lis.sum { |li| li.quantity * (li.variant.weight || 0) } },
proc { |lis| lis.sum { |li| (li.max_quantity || 0) * (li.variant.weight || 0) } },
proc { |lis| ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor },
proc { |lis| lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } - ( ( (lis.first.variant.product.group_buy_unit_size || 0).zero? ? 0 : ( lis.sum { |li| ( [li.max_quantity || 0, li.quantity || 0].max ) * (li.variant.weight || 0) } / lis.first.variant.product.group_buy_unit_size ) ).floor * (lis.first.variant.product.group_buy_unit_size || 0) ) } ] },
{ group_by: proc { |li| li.variant },
sort_by: proc { |variant| variant.full_name } } ]
end
order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns
@header = header
@table = order_grouper.table(@line_items)
csv_file_name = "bulk_coop_#{timestamp}.csv"
render_report(@header, @table, params[:csv], csv_file_name)
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def payments
params[:q] ||= {}
if params[:q][:completed_at_gt].blank?
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]).beginning_of_day rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:completed_at_lt].blank?
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]).end_of_day rescue ""
end
params[:q][:meta_sort] ||= "completed_at.desc"
@search = Spree::Order.complete.not_state(:canceled).managed_by(spree_current_user).search(params[:q])
orders = @search.result
payments = orders.map { |o| o.payments.select { |payment| payment.completed? } }.flatten # Only select completed payments
# -- Prepare Date Params
prepare_date_params params
# -- Prepare Form Options
@distributors = Enterprise.is_distributor.managed_by(spree_current_user)
@report_type = params[:report_type]
case params[:report_type]
when "payments_by_payment_type"
table_items = payments
header = ["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"]
columns = [ proc { |payments| payments.first.order.payment_state },
proc { |payments| payments.first.order.distributor.name },
proc { |payments| payments.first.payment_method.name },
proc { |payments| payments.sum { |payment| payment.amount } } ]
rules = [ { group_by: proc { |payment| payment.order.payment_state },
sort_by: proc { |payment_state| payment_state } },
{ group_by: proc { |payment| payment.order.distributor },
sort_by: proc { |distributor| distributor.name } },
{ group_by: proc { |payment| Spree::PaymentMethod.unscoped { payment.payment_method } },
sort_by: proc { |method| method.name } } ]
when "itemised_payment_totals"
table_items = orders
header = ["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})", "Total (#{currency_symbol})"]
columns = [ proc { |orders| orders.first.payment_state },
proc { |orders| orders.first.distributor.name },
proc { |orders| orders.sum { |o| o.item_total } },
proc { |orders| orders.sum { |o| o.ship_total } },
proc { |orders| orders.sum { |o| o.outstanding_balance } },
proc { |orders| orders.sum { |o| o.total } } ]
rules = [ { group_by: proc { |order| order.payment_state },
sort_by: proc { |payment_state| payment_state } },
{ group_by: proc { |order| order.distributor },
sort_by: proc { |distributor| distributor.name } } ]
when "payment_totals"
table_items = orders
header = ["Payment State", "Distributor", "Product Total (#{currency_symbol})", "Shipping Total (#{currency_symbol})", "Total (#{currency_symbol})", "EFT (#{currency_symbol})", "PayPal (#{currency_symbol})", "Outstanding Balance (#{currency_symbol})"]
columns = [ proc { |orders| orders.first.payment_state },
proc { |orders| orders.first.distributor.name },
proc { |orders| orders.sum { |o| o.item_total } },
proc { |orders| orders.sum { |o| o.ship_total } },
proc { |orders| orders.sum { |o| o.total } },
proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "EFT") }.sum { |payment| payment.amount } } },
proc { |orders| orders.sum { |o| o.payments.select { |payment| payment.completed? && (payment.payment_method.name.to_s.include? "PayPal") }.sum{ |payment| payment.amount } } },
proc { |orders| orders.sum { |o| o.outstanding_balance } } ]
rules = [ { group_by: proc { |order| order.payment_state },
sort_by: proc { |payment_state| payment_state } },
{ group_by: proc { |order| order.distributor },
sort_by: proc { |distributor| distributor.name } } ]
else
table_items = payments
header = ["Payment State", "Distributor", "Payment Type", "Total (#{currency_symbol})"]
columns = [ proc { |payments| payments.first.order.payment_state },
proc { |payments| payments.first.order.distributor.name },
proc { |payments| payments.first.payment_method.name },
proc { |payments| payments.sum { |payment| payment.amount } } ]
rules = [ { group_by: proc { |payment| payment.order.payment_state },
sort_by: proc { |payment_state| payment_state } },
{ group_by: proc { |payment| payment.order.distributor },
sort_by: proc { |distributor| distributor.name } },
{ group_by: proc { |payment| payment.payment_method },
sort_by: proc { |method| method.name } } ]
end
order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns
@header = header
@table = order_grouper.table(table_items)
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::PaymentsReport.new spree_current_user, params
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
@table = order_grouper.table(@report.table_items)
csv_file_name = "payments_#{timestamp}.csv"
render_report(@header, @table, params[:csv], csv_file_name)
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def orders_and_fulfillment
# -- Prepare parameters
params[:q] ||= {}
if params[:q][:completed_at_gt].blank?
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]) rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:completed_at_lt].blank?
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue ""
end
params[:q][:meta_sort] ||= "completed_at.desc"
# -- Prepare Date Params
prepare_date_params params
# -- Prepare Form Options
permissions = OpenFoodNetwork::Permissions.new(spree_current_user)
# -- Search
@search = Spree::Order.complete.not_state(:canceled).search(params[:q])
orders = permissions.visible_orders.merge(@search.result)
@line_items = permissions.visible_line_items.merge(Spree::LineItem.where(order_id: orders))
@line_items = @line_items.supplied_by_any(params[:supplier_id_in]) if params[:supplier_id_in].present?
line_items_with_hidden_details = @line_items.where('"spree_line_items"."id" NOT IN (?)', permissions.editable_line_items)
@line_items.select{ |li| line_items_with_hidden_details.include? li }.each do |line_item|
# TODO We should really be hiding customer code here too, but until we
# have an actual association between order and customer, it's a bit tricky
line_item.order.bill_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil)
line_item.order.ship_address.assign_attributes(firstname: "HIDDEN", lastname: "", phone: "", address1: "", address2: "", city: "", zipcode: "", state: nil)
line_item.order.assign_attributes(email: "HIDDEN")
end
# My distributors and any distributors distributing products I supply
@distributors = permissions.visible_enterprises_for_order_reports.is_distributor
# My suppliers and any suppliers supplying products I distribute
@suppliers = permissions.visible_enterprises_for_order_reports.is_primary_producer
@@ -438,239 +200,25 @@ Spree::Admin::ReportsController.class_eval do
@report_types = REPORT_TYPES[:orders_and_fulfillment]
@report_type = params[:report_type]
# -- Format according to report type
case params[:report_type]
when "order_cycle_supplier_totals"
table_items = @line_items
@include_blank = 'All'
@include_blank = 'All'
header = ["Producer", "Product", "Variant", "Amount", "Total Units", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"]
columns = [ proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| total_units(line_items) },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum { |li| li.amount } },
proc { |line_items| "" },
proc { |line_items| "incoming transport" } ]
rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name } },
{ group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name } },
{ group_by: proc { |line_item| line_item.variant },
sort_by: proc { |variant| variant.full_name } } ]
when "order_cycle_supplier_totals_by_distributor"
table_items = @line_items
@include_blank = 'All'
header = ["Producer", "Product", "Variant", "To Hub", "Amount", "Curr. Cost per Unit", "Total Cost", "Shipping Method"]
columns = [ proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum { |li| li.amount } },
proc { |line_items| "shipping method" } ]
rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name } },
{ group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name } },
{ group_by: proc { |line_item| line_item.variant },
sort_by: proc { |variant| variant.full_name },
summary_columns: [ proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "TOTAL" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| line_items.sum { |li| li.amount } },
proc { |line_items| "" } ] },
{ group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name } } ]
when "order_cycle_distributor_totals_by_supplier"
table_items = @line_items
@include_blank = 'All'
header = ["Hub", "Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Total Shipping Cost", "Shipping Method"]
columns = [ proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum { |li| li.amount } },
proc { |line_items| "" },
proc { |line_items| "shipping method" } ]
rules = [ { group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name },
summary_columns: [ proc { |line_items| "" },
proc { |line_items| "TOTAL" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| line_items.sum { |li| li.amount } },
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } },
proc { |line_items| "" } ] },
{ group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name } },
{ group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name } },
{ group_by: proc { |line_item| line_item.variant },
sort_by: proc { |variant| variant.full_name } } ]
when "order_cycle_customer_totals"
table_items = @line_items
@include_blank = 'All'
header = ["Hub", "Customer", "Email", "Phone", "Producer", "Product", "Variant",
"Amount", "Item (#{currency_symbol})", "Item + Fees (#{currency_symbol})", "Admin & Handling (#{currency_symbol})", "Ship (#{currency_symbol})", "Total (#{currency_symbol})", "Paid?",
"Shipping", "Delivery?",
"Ship Street", "Ship Street 2", "Ship City", "Ship Postcode", "Ship State",
"Comments", "SKU",
"Order Cycle", "Payment Method", "Customer Code", "Tags",
"Billing Street 1", "Billing Street 2", "Billing City", "Billing Postcode", "Billing State"
]
rsa = proc { |line_items| line_items.first.order.shipping_method.andand.require_ship_address }
columns = [
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname },
proc { |line_items| line_items.first.order.email },
proc { |line_items| line_items.first.order.bill_address.phone },
proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.sum { |li| li.amount } },
proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| line_items.first.order.shipping_method.andand.name },
proc { |line_items| rsa.call(line_items) ? 'Y' : 'N' },
proc { |line_items| line_items.first.order.ship_address.andand.address1 if rsa.call(line_items) },
proc { |line_items| line_items.first.order.ship_address.andand.address2 if rsa.call(line_items) },
proc { |line_items| line_items.first.order.ship_address.andand.city if rsa.call(line_items) },
proc { |line_items| line_items.first.order.ship_address.andand.zipcode if rsa.call(line_items) },
proc { |line_items| line_items.first.order.ship_address.andand.state if rsa.call(line_items) },
proc { |line_items| "" },
proc { |line_items| line_items.first.variant.product.sku },
proc { |line_items| line_items.first.order.order_cycle.andand.name },
proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name },
proc { |line_items| line_items.first.order.user.andand.customer_of(line_items.first.order.distributor).andand.code },
proc { |line_items| "" },
proc { |line_items| line_items.first.order.bill_address.andand.address1 },
proc { |line_items| line_items.first.order.bill_address.andand.address2 },
proc { |line_items| line_items.first.order.bill_address.andand.city },
proc { |line_items| line_items.first.order.bill_address.andand.zipcode },
proc { |line_items| line_items.first.order.bill_address.andand.state } ]
rules = [ { group_by: proc { |line_item| line_item.order.distributor },
sort_by: proc { |distributor| distributor.name } },
{ group_by: proc { |line_item| line_item.order },
sort_by: proc { |order| order.bill_address.lastname + " " + order.bill_address.firstname },
summary_columns: [
proc { |line_items| line_items.first.order.distributor.name },
proc { |line_items| line_items.first.order.bill_address.firstname + " " + line_items.first.order.bill_address.lastname },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "TOTAL" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| line_items.sum { |li| li.amount } },
proc { |line_items| line_items.sum { |li| li.amount_with_adjustments } },
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.admin_and_handling_total } },
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.ship_total } },
proc { |line_items| line_items.map { |li| li.order }.uniq.sum { |o| o.total } },
proc { |line_items| line_items.all? { |li| li.order.paid? } ? "Yes" : "No" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| line_items.first.order.special_instructions } ,
proc { |line_items| "" },
proc { |line_items| line_items.first.order.order_cycle.andand.name },
proc { |line_items| line_items.first.order.payments.first.andand.payment_method.andand.name },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" },
proc { |line_items| "" }
] },
{ group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name } },
{ group_by: proc { |line_item| line_item.variant },
sort_by: proc { |variant| variant.full_name } } ]
else
table_items = @line_items
@include_blank = 'All'
header = ["Producer", "Product", "Variant", "Amount", "Curr. Cost per Unit", "Total Cost", "Status", "Incoming Transport"]
columns = [ proc { |line_items| line_items.first.variant.product.supplier.name },
proc { |line_items| line_items.first.variant.product.name },
proc { |line_items| line_items.first.variant.full_name },
proc { |line_items| line_items.sum { |li| li.quantity } },
proc { |line_items| line_items.first.price },
proc { |line_items| line_items.sum { |li| li.quantity * li.price } },
proc { |line_items| "" },
proc { |line_items| "incoming transport" } ]
rules = [ { group_by: proc { |line_item| line_item.variant.product.supplier },
sort_by: proc { |supplier| supplier.name } },
{ group_by: proc { |line_item| line_item.variant.product },
sort_by: proc { |product| product.name } },
{ group_by: proc { |line_item| line_item.variant },
sort_by: proc { |variant| variant.full_name } } ]
end
order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns
@header = header
@table = order_grouper.table(table_items)
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user, params
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns
@table = order_grouper.table(@report.table_items)
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
render_report(@header, @table, params[:csv], csv_file_name)
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def products_and_inventory
@report_types = REPORT_TYPES[:products_and_inventory]
@report = OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user, params
if params[:report_type] != 'lettuce_share'
@report = OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user, params
else
@report = OpenFoodNetwork::LettuceShareReport.new spree_current_user, params
end
render_report(@report.header, @report.table, params[:csv], "products_and_inventory_#{timestamp}.csv")
end
@@ -709,6 +257,20 @@ Spree::Admin::ReportsController.class_eval do
private
def prepare_date_params(params)
# -- Prepare parameters
params[:q] ||= {}
if params[:q][:completed_at_gt].blank?
params[:q][:completed_at_gt] = Time.zone.now.beginning_of_month
else
params[:q][:completed_at_gt] = Time.zone.parse(params[:q][:completed_at_gt]) rescue Time.zone.now.beginning_of_month
end
if params[:q] && !params[:q][:completed_at_lt].blank?
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue ""
end
params[:q][:meta_sort] ||= "completed_at.desc"
end
def load_data
# Load distributors either owned by the user or selling their enterprises products.
my_distributors = Enterprise.is_distributor.managed_by(spree_current_user)
@@ -732,23 +294,14 @@ Spree::Admin::ReportsController.class_eval do
:sales_total => { :name => "Sales Total", :description => "Sales Total For All Orders" },
:users_and_enterprises => { :name => "Users & Enterprises", :description => "Enterprise Ownership & Status" },
:order_cycle_management => {:name => "Order Cycle Management", :description => ''},
:packing => {:name => "Packing Reports", :description => ''},
:sales_tax => { :name => "Sales Tax", :description => "Sales Tax For Orders" },
:xero_invoices => { :name => "Xero Invoices", :description => 'Invoices for import into Xero' }
}
# Return only reports the user is authorized to view.
reports.select { |action| can? action, :report }
end
def total_units(line_items)
return " " if line_items.map{ |li| li.variant.unit_value.nil? }.any?
total_units = line_items.sum do |li|
scale_factor = ( li.product.variant_unit == 'weight' ? 1000 : 1 )
li.quantity * li.variant.unit_value / scale_factor
end
total_units.round(3)
end
def timestamp
Time.now.strftime("%Y%m%d")
end

View File

@@ -14,9 +14,10 @@ Spree::Admin::VariantsController.class_eval do
if params[:distributor_id].present?
distributor = Enterprise.find params[:distributor_id]
@variants = @variants.in_distributor(distributor)
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
# Perform scoping after all filtering is done.
# Filtering could be a problem on scoped variants.
@variants.each { |v| v.scope_to_hub(distributor) }
@variants.each { |v| scoper.scope(v) }
end
end

View File

@@ -15,6 +15,16 @@ module Spree
link_to_with_icon('icon-trash', name, url, :class => "remove_fields #{options[:class]}", :data => {:action => 'remove'}, :title => t(:remove)) + f.hidden_field(:_destroy)
end
def preference_field_tag_with_files(name, value, options)
if options[:type] == :file
file_field_tag name, preference_field_options(options)
else
preference_field_tag_without_files name, value, options
end
end
alias_method_chain :preference_field_tag, :files
end
end
end

View File

@@ -1,7 +1,39 @@
class ContentConfiguration < Spree::Preferences::Configuration
preference :home_tagline_cta, :string, default: "Browse Open Food Network Australia"
preference :home_whats_happening, :string, default: "Thanks for making the Open Food Network possible. Our vision is a better food system, and we're proud of what we're achieving together."
require 'open_food_network/paperclippable'
class ContentConfiguration < Spree::Preferences::FileConfiguration
include OpenFoodNetwork::Paperclippable
# Header
preference :logo, :file
preference :logo_mobile, :file
preference :logo_mobile_svg, :file
has_attached_file :logo
has_attached_file :logo_mobile
has_attached_file :logo_mobile_svg
# Home page
preference :home_hero, :file
preference :home_show_stats, :boolean, default: true
has_attached_file :home_hero
# Producer sign-up page
preference :producer_signup_pricing_table_html, :text, default: "(TODO: Pricing table)"
preference :producer_signup_case_studies_html, :text, default: "(TODO: Case studies)"
preference :producer_signup_detail_html, :text, default: "(TODO: Detail)"
# Hubs sign-up page
preference :hub_signup_pricing_table_html, :text, default: "(TODO: Pricing table)"
preference :hub_signup_case_studies_html, :text, default: "(TODO: Case studies)"
preference :hub_signup_detail_html, :text, default: "(TODO: Detail)"
# Groups sign-up page
preference :group_signup_pricing_table_html, :text, default: "(TODO: Pricing table)"
preference :group_signup_case_studies_html, :text, default: "(TODO: Case studies)"
preference :group_signup_detail_html, :text, default: "(TODO: Detail)"
# Footer
preference :footer_logo, :file
has_attached_file :footer_logo
preference :footer_facebook_url, :string, default: "https://www.facebook.com/OpenFoodNet"
preference :footer_twitter_url, :string, default: "https://twitter.com/OpenFoodNet"
preference :footer_instagram_url, :string, default: ""
@@ -18,4 +50,5 @@ class ContentConfiguration < Spree::Preferences::Configuration
EOS
preference :footer_about_url, :string, default: "http://www.openfoodnetwork.org/ofn-local/open-food-network-australia/"
preference :footer_tos_url, :string, default: "/Terms-of-service.pdf"
end

View File

@@ -15,6 +15,8 @@ class Enterprise < ActiveRecord::Base
acts_as_gmappable :process_geocoding => false
has_many :relationships_as_parent, class_name: 'EnterpriseRelationship', foreign_key: 'parent_id', dependent: :destroy
has_many :relationships_as_child, class_name: 'EnterpriseRelationship', foreign_key: 'child_id', dependent: :destroy
has_and_belongs_to_many :groups, class_name: 'EnterpriseGroup'
has_many :producer_properties, foreign_key: 'producer_id'
has_many :properties, through: :producer_properties
@@ -56,6 +58,7 @@ class Enterprise < ActiveRecord::Base
validates :name, presence: true
validate :name_is_unique
validates :sells, presence: true, inclusion: {in: SELLS}
validates :address, presence: true, associated: true
validates :email, presence: true
@@ -333,6 +336,15 @@ class Enterprise < ActiveRecord::Base
private
def name_is_unique
dups = Enterprise.where(name: name)
dups = dups.where('id != ?', id) unless new_record?
if dups.any?
errors.add :name, "has already been taken. If this is your enterprise and you would like to claim ownership, please contact the current manager of this profile at #{dups.first.owner.email}."
end
end
def email_is_known?
owner.enterprises.confirmed.map(&:email).include?(email)
end

View File

@@ -32,10 +32,13 @@ class EnterpriseRelationship < ActiveRecord::Base
relationships = EnterpriseRelationship.includes(:child, :parent)
relatives = {}
Enterprise.all.each do |e|
relatives[e.id] ||= { distributors: Set.new, producers: Set.new }
relatives[e.id][:producers] << e.id if e.is_primary_producer
relatives[e.id][:distributors] << e.id if e.is_distributor
Enterprise.is_primary_producer.pluck(:id).each do |enterprise_id|
relatives[enterprise_id] ||= { distributors: Set.new, producers: Set.new }
relatives[enterprise_id][:producers] << enterprise_id
end
Enterprise.is_distributor.pluck(:id).each do |enterprise_id|
relatives[enterprise_id] ||= { distributors: Set.new, producers: Set.new }
relatives[enterprise_id][:distributors] << enterprise_id
end
relationships.each do |r|

View File

@@ -132,7 +132,12 @@ class OrderCycle < ActiveRecord::Base
end
def variants
self.exchanges.map(&:variants).flatten.uniq.reject(&:deleted?)
Spree::Variant.
joins(:exchanges).
merge(Exchange.in_order_cycle(self)).
not_deleted.
select('DISTINCT spree_variants.*').
to_a # http://stackoverflow.com/q/15110166
end
def distributed_variants

View File

@@ -123,7 +123,7 @@ class AbilityDecorator
can [:admin, :index, :read, :create, :edit], Spree::Classification
# Reports page
can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], :report
can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :packing], :report
end
def add_order_cycle_management_abilities(user)
@@ -186,7 +186,7 @@ class AbilityDecorator
end
# Reports page
can [:admin, :index, :customers, :group_buys, :bulk_coop, :sales_tax, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management], :report
can [:admin, :index, :customers, :group_buys, :bulk_coop, :sales_tax, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :xero_invoices], :report
can [:admin, :index, :update], Customer, enterprise_id: Enterprise.managed_by(user).pluck(:id)
end

View File

@@ -4,9 +4,10 @@ module Spree
return [] unless order.completed?
#increase inventory to meet initial requirements
scoper = OpenFoodNetwork::ScopeVariantToHub.new(order.distributor)
order.line_items.each do |line_item|
# Scope variant to hub so that stock levels may be subtracted from VariantOverride.
line_item.variant.scope_to_hub order.distributor
scoper.scope(line_item.variant)
increase(order, line_item.variant, line_item.quantity)
end

View File

@@ -1,6 +1,6 @@
Spree::LineItem.class_eval do
attr_accessible :max_quantity, :unit_value
attr_accessible :unit_value, :price, :as => :api
attr_accessible :max_quantity, :final_weight_volume
attr_accessible :final_weight_volume, :price, :as => :api
# -- Scopes
scope :managed_by, lambda { |user|

View File

@@ -130,7 +130,7 @@ Spree::Order.class_eval do
else
current_item = Spree::LineItem.new(:quantity => quantity, max_quantity: max_quantity)
current_item.variant = variant
current_item.unit_value = variant.unit_value
current_item.final_weight_volume = variant.unit_value * quantity
if currency
current_item.currency = currency unless currency.nil?
current_item.price = variant.price_in(currency).amount

View File

@@ -9,7 +9,7 @@ Spree::OrderPopulator.class_eval do
errors.add(:base, "That distributor or order cycle can't supply all the products in your cart. Please choose another.")
end
if valid?
if valid?
@order.with_lock do
@order.empty! if overwrite
@@ -33,7 +33,7 @@ Spree::OrderPopulator.class_eval do
def attempt_cart_add(variant_id, quantity, max_quantity = nil)
quantity = quantity.to_i
variant = Spree::Variant.find(variant_id)
variant.scope_to_hub @distributor
OpenFoodNetwork::ScopeVariantToHub.new(@distributor).scope(variant)
if quantity > 0
if check_stock_levels(variant, quantity) &&
check_order_cycle_provided_for(variant) &&

View File

@@ -0,0 +1,50 @@
module Spree::Preferences
class FileConfiguration < Configuration
def self.preference(name, type, *args)
if type == :file
super "#{name}_file_name", :string, *args
super "#{name}_content_type", :string, *args
super "#{name}_file_size", :integer, *args
super "#{name}_updated_at", :string, *args
else
super name, type, *args
end
end
def get_preference(key)
if !has_preference?(key) && has_attachment?(key)
send key
else
super key
end
end
alias :[] :get_preference
def preference_type(name)
if has_attachment? name
:file
else
super name
end
end
# Spree's Configuration responds to preference methods via method_missing, but doesn't
# override respond_to?, which consequently reports those methods as unavailable. Paperclip
# errors if respond_to? isn't correct, so we override it here.
def respond_to?(method, include_all=false)
name = method.to_s.gsub('=', '')
super(self.class.preference_getter_method(name), include_all) || super(method, include_all)
end
def has_attachment?(name)
self.class.respond_to?(:attachment_definitions) &&
self.class.attachment_definitions.keys.include?(name.to_sym)
end
end
end

View File

@@ -1,8 +1,4 @@
require 'open_food_network/scope_product_to_hub'
Spree::Product.class_eval do
include OpenFoodNetwork::ProductScopableToHub
# We have an after_destroy callback on Spree::ProductOptionType. However, if we
# don't specify dependent => destroy on this association, it is not called. See:
# https://github.com/rails/rails/issues/7618

View File

@@ -1,10 +1,7 @@
require 'open_food_network/scope_variant_to_hub'
require 'open_food_network/enterprise_fee_calculator'
require 'open_food_network/option_value_namer'
Spree::Variant.class_eval do
include OpenFoodNetwork::VariantScopableToHub
has_many :exchange_variants, dependent: :destroy
has_many :exchanges, through: :exchange_variants
has_many :variant_overrides

View File

@@ -8,6 +8,12 @@ class VariantOverride < ActiveRecord::Base
where(hub_id: hubs)
}
def self.indexed(hub)
Hash[
for_hubs(hub).map { |vo| [vo.variant, vo] }
]
end
def self.price_for(hub, variant)
self.for(hub, variant).andand.price
end
@@ -25,12 +31,21 @@ class VariantOverride < ActiveRecord::Base
if vo.nil?
Bugsnag.notify RuntimeError.new "Attempting to decrement stock level for a variant without a VariantOverride."
elsif vo.count_on_hand.blank?
Bugsnag.notify RuntimeError.new "Attempting to decrement stock level on a VariantOverride without a count_on_hand specified."
else
vo.decrement! :count_on_hand, quantity
vo.decrement_stock! quantity
end
end
def stock_overridden?
count_on_hand.present?
end
def decrement_stock!(quantity)
if stock_overridden?
decrement! :count_on_hand, quantity
else
Bugsnag.notify RuntimeError.new "Attempting to decrement stock level on a VariantOverride without a count_on_hand specified."
end
end

View File

@@ -0,0 +1,2 @@
/ insert_bottom "[data-hook='admin_tabs'], #admin_tabs[data-hook]"
= tab :customers, :url => main_app.admin_customers_path

View File

@@ -1,5 +1,5 @@
class Api::Admin::LineItemSerializer < ActiveModel::Serializer
attributes :id, :quantity, :max_quantity, :supplier, :price, :unit_value, :units_product, :units_variant
attributes :id, :quantity, :max_quantity, :supplier, :price, :final_weight_volume, :units_product, :units_variant
def supplier
Api::Admin::IdNameSerializer.new(object.product.supplier).serializable_hash
@@ -13,7 +13,7 @@ class Api::Admin::LineItemSerializer < ActiveModel::Serializer
Api::Admin::UnitsVariantSerializer.new(object.variant).serializable_hash
end
def unit_value
object.unit_value.to_f
def final_weight_volume
object.final_weight_volume.to_f
end
end

View File

@@ -22,7 +22,12 @@ class Api::UncachedProductSerializer < ActiveModel::Serializer
attributes :price
def price
object.master.price_with_fees(options[:current_distributor], options[:current_order_cycle])
if options[:enterprise_fee_calculator]
object.master.price + options[:enterprise_fee_calculator].indexed_fees_for(object.master)
else
object.master.price_with_fees(options[:current_distributor], options[:current_order_cycle])
end
end
end

View File

@@ -4,10 +4,10 @@
Content
= form_tag main_app.admin_content_path, method: :put do
= form_tag main_app.admin_content_path, method: :put, multipart: true do
#preferences
= render 'fieldset', name: 'Home page', preferences: @preferences_home
= render 'fieldset', name: 'Footer', preferences: @preferences_footer
- @preference_sections.each do |preference_section|
= render 'fieldset', name: preference_section[:name], preferences: preference_section[:preferences]
.form-buttons.filter-actions.actions{"data-hook" => "buttons"}
= button t(:update), 'icon-refresh'

View File

@@ -1,24 +0,0 @@
.distributor-details{'data-hook' => 'distributor-details'}
%h2= distributor.name
%p
%strong Address:
%br/
= render 'spree/shared/address', :address => distributor.address
%p
%strong Next collection time:
%br/
= distributor.next_collection_at
%p
%strong Regular collection times:
%br/
= distributor.pickup_times
%p
%strong Contact:
%br/
= distributor.contact
%br/
= "Phone: #{distributor.phone}"
%br/
= "Email: #{distributor.email}"
%p= distributor.description
%p= link_to distributor.website, distributor.website if distributor.website

View File

@@ -1,12 +0,0 @@
- content_for :sidebar do
%div{'data-hook' => "homepage_sidebar_navigation"}
= render 'spree/sidebar'
%h1 Distributors
= cms_page_content(:content, Cms::Page.find_by_full_path('/enterprises/distributors'))
%ul.enterprises
- @distributors.each do |distributor|
%li= link_to distributor.name, distributor

View File

@@ -1 +0,0 @@
distributors = <%= @distributor_details.to_json.html_safe %>;

View File

@@ -1,12 +0,0 @@
- content_for :sidebar do
%div{'data-hook' => "homepage_sidebar_navigation"}
= render 'spree/sidebar'
%h1 Enterprises
= cms_page_content(:content, Cms::Page.find_by_full_path('/enterprises'))
%ul.enterprises
- @enterprises.each do |enterprise|
%li= link_to enterprise.name, enterprise

View File

@@ -16,7 +16,7 @@
/ Will this label should be a variable to reflect 'Ready for pickup / delivery' as appropriate
%select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id",
"ng-change" => "changeOrderCycle()",
"ofn-change-order-cycle" => true,
"ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}",
"popover-placement" => "left", "popover" => "Choose when you want your order:", "popover-trigger" => "openTrigger"}

View File

@@ -1,15 +0,0 @@
.row
.large-12.columns
%h2= @enterprise.name
.row
.large-12.columns= @enterprise.long_description.andand.html_safe
.row
.large-12.columns
.products
- @products.each_slice(4).to_a.each do |products_row|
.row
- products_row.each do |product|
.large-4.columns.centered
.clearfix= link_to small_image(product), product
= link_to product.name, product

View File

@@ -1,18 +0,0 @@
- if @enterprise != current_distributor
%h2= @enterprise.name
.enterprise-description= @enterprise.long_description.andand.html_safe
- if current_distributor
= render :template => 'spree/products/index'
- else
%h3 Hubs that distribute our products
%p.hint Select a hub to start shopping:
%ul#supplier-distributors
- if @distributors.delete @enterprise
%li= link_to "Buy direct from the farm", enterprise_shop_path(@enterprise), {class: distributor_link_class(@enterprise)}
- @distributors.each do |distributor|
%li= render partial: "shared/distributor", object: distributor

View File

@@ -1,12 +0,0 @@
- content_for :sidebar do
%div{'data-hook' => "homepage_sidebar_navigation"}
= render 'spree/sidebar'
%h1 Suppliers
= cms_page_content(:content, Cms::Page.find_by_full_path('/enterprises/suppliers'))
%ul.enterprises
- @suppliers.each do |supplier|
%li= link_to supplier.name, supplier

View File

@@ -10,7 +10,7 @@
.small-12.medium-6.medium-offset-3.columns.text-center
%p.text-big We're an amazing platform for collaborative marketing, the easiest way for your members and stakeholders to reach new markets. We're non-profit, affordable, and simple.
%br
%a.button.transparent{href: "hello@openfoodnetwork.org".reverse, target: '_blank', mailto: true}
%a.button.transparent{href: "hello@openfoodnetwork.org?subject=I'd%20like%20to%20talk%20to%20you%20about%20groups%20on%20the%20Open%20Food%20Network".reverse, target: '_blank', mailto: true}
Email us
.groups-details.pane
@@ -35,96 +35,27 @@
-# / If there is a time-sensitive offer you can write it here, e.g.
-# Time-sensitive offer goes here!
%br
%table.signup-table.hubs-table{cellpadding: "0", cellspacing: "0"}
%thead
%tr
%td
%h5
%td.text-center{width: "24%"}
%h5 OFN Group
%tr
%td
%p
%strong Promote your members
%br
%span.text-small Promote your local and/or member businesses through a group page
%td.text-center
%i.ofn-i_003-check.text-big
%tr
%td
%p
%strong Unique URL
%br
%span.text-small Your own organisation page/url on the Open Food Network.
%td.text-center
%i.ofn-i_003-check.text-big
%tr
%td
%p
%strong Region map &amp; listings
%br
%span.text-small Easy search from your own list view and your own map - links through to profile pages for your member/local producers and/or food businesses.
%td.text-center
%i.ofn-i_003-check.text-big
%tr
%td
%p
%strong Opt-in extras
%p.text-small Available additional support:
%ul.small
%li Tailored workshop(s) for your stakeholders
%li Tailored training and support package
%td.text-center
%tfoot
%tr
%td
%td.text-center{valign: "top"}
%h2
$5,500
= ContentConfig.group_signup_pricing_table_html.html_safe
#shops-case-studies
.row
.small-12.medium-10.medium-offset-1.columns
%h2.text-center Case studies
%br
.row
.small-12.medium-6.columns
= render 'shared/case_study', img_src: "/assets/case-studies/South_East_Food_Hub.png", title: "South East Food Group", description: "The South East Food Hub lists all its participating producers and hubs (food clubs) on its group page on the Open Food Network.", link: "https://openfoodnetwork.org.au/groups/6"
.small-12.medium-6.columns
= render 'shared/case_study', img_src: "/assets/case-studies/mt-alexander.png", title: "Mt Alexander Local Produce Network", description: "This community organisation promotes local sustainable food production and consumption through its group page. It lists sources of local sustenance &mdash; direct from the grower or through a range local shops and markets.".html_safe, link: "https://openfoodnetwork.org.au/groups/10"
= ContentConfig.group_signup_case_studies_html.html_safe
.pane#cta
.row
.small-12.medium-6.medium-offset-3.columns.text-center
%h2 Ready to discuss?
%p.text-big Get in touch to discover what OFN can do for you:
%a.button.transparent{href: "hello@openfoodnetwork.org".reverse, target: '_blank', mailto: true}
%a.button.transparent{href: "hello@openfoodnetwork.org?subject=I'd%20like%20to%20talk%20to%20you%20about%20groups%20on%20the%20Open%20Food%20Network".reverse, target: '_blank', mailto: true}
Email us
#hub-details.pane.footer-pad
.row
.small-12.medium-10.medium-offset-1.columns
%h2.text-center Here's the detail.
.row
.small-12.medium-6.columns
%h4 A sub-heading goes here
%p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer in odio vel ex efficitur auctor. Nam rhoncus, tortor vel varius varius, magna lorem dignissim libero, at dignissim erat leo id tortor. Donec gravida quis augue sed pellentesque. Morbi laoreet efficitur mi, id tempus diam lacinia eu.
%h5 Smaller sub-heading goes here
%p.text-small Vestibulum eu quam neque. Aenean porta velit sit amet metus mattis, ut pulvinar dui semper. Nunc ornare scelerisque varius. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras a magna nec augue suscipit fermentum at ac urna.
%ul.text-small
%li A bullet point
%li Another point
%li A third point goes here
%p.text-small Quisque urna lacus, tristique sed rutrum a, volutpat eu diam. Nam placerat mi nec enim tincidunt, nec dapibus risus molestie. Praesent mattis eu dolor nec sollicitudin. Cras ut magna sem. Etiam vitae commodo augue, sit amet feugiat diam.
.small-12.medium-6.columns
%h4 A sub-heading goes here
%p Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer in odio vel ex efficitur auctor. Nam rhoncus, tortor vel varius varius, magna lorem dignissim libero, at dignissim erat leo id tortor. Donec gravida quis augue sed pellentesque. Morbi laoreet efficitur mi, id tempus diam lacinia eu.
%h5 Smaller sub-heading goes here
%p.text-small Vestibulum eu quam neque. Aenean porta velit sit amet metus mattis, ut pulvinar dui semper. Nunc ornare scelerisque varius. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras a magna nec augue suscipit fermentum at ac urna.
%ul.text-small
%li A bullet point
%li Another point
%li A third point goes here
%p.text-small Quisque urna lacus, tristique sed rutrum a, volutpat eu diam. Nam placerat mi nec enim tincidunt, nec dapibus risus molestie. Praesent mattis eu dolor nec sollicitudin. Cras ut magna sem. Etiam vitae commodo augue, sit amet feugiat diam.
= ContentConfig.group_signup_detail_html.html_safe
= render partial: "shared/footer"

View File

@@ -4,8 +4,7 @@
%h2 Food, unincorporated.
%p Sometimes the best way to fix the system is to start a new one&hellip;
-# TODO: Make this slide down/up
.hide-show{"ng-show" => "brandStoryExpanded"}
#brand-story-text.hide-show.slideable
%p We begin from the ground up. With farmers and growers ready to tell their stories proudly and truly. With distributors ready to connect people with products fairly and honestly. With buyers who believe that better weekly shopping decisions can seriously change the world.
%p Then we need a way to make it real. A way to empower everyone who grows, sells and buys food. A way to tell all the stories, to handle all the logistics. A way to turn transaction into transformation every day.
%p So we build an online marketplace that levels the playing field. Its transparent, so it creates real relationships. Its open source, so its owned by everyone. It scales to regions and nations, so people start versions across the world.
@@ -14,6 +13,6 @@
%strong We call it Open Food Network.
%p We all love food. Now we can love our food system too.
%a.text-vbig{"ng-click" => "toggleBrandStory()"}
%a.text-vbig{"slide-toggle" => "#brand-story-text", "ng-click" => "toggleBrandStory()"}
%i.ofn-i_005-caret-down{"ng-hide" => "brandStoryExpanded"}
%i.ofn-i_006-caret-up{ "ng-show" => "brandStoryExpanded"}

View File

@@ -1,21 +1,21 @@
.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"}
.row.active_table_row{"ng-show" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}", bindonce: true}
.columns.small-12.medium-6.large-5.fat
%div{"bo-if" => "hub.taxons"}
%label Shop for
.trans-sentence
%span.fat-taxons{"ng-repeat" => "taxon in hub.taxons"}
%render-svg{path: "{{taxon.icon}}"}
%span{"bo-text" => "taxon.name"}
%span{"bo-text" => "taxon.name"}
%div.show-for-medium-up{"bo-if" => "hub.taxons.length==0"}
&nbsp;
.columns.small-12.medium-3.large-2.fat
%div{"bo-if" => "hub.pickup || hub.delivery"}
%label Delivery options
%ul.small-block-grid-2.medium-block-grid-1.large-block-grid-1
%li.pickup{"bo-if" => "hub.pickup"}
%li.pickup{"bo-if" => "hub.pickup"}
%i.ofn-i_038-takeaway
Pickup
%li.delivery{"bo-if" => "hub.delivery"}
%li.delivery{"bo-if" => "hub.delivery"}
%i.ofn-i_039-delivery
Delivery
.columns.small-12.medium-3.large-5.fat

View File

@@ -10,7 +10,7 @@
%h5.tdhead
.light Filter by
Type
%filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{ objects: "Enterprises.hubs | searchEnterprises:query | taxonsOf", "active-selectors" => "activeTaxons" }
%filter-selector.small-block-grid-2.medium-block-grid-4.large-block-grid-5{ objects: "visibleMatches | visible | taxonsOf", "active-selectors" => "activeTaxons" }
.small-12.large-3.columns
%h5.tdhead
.light Filter by

View File

@@ -1,22 +1,26 @@
= inject_enterprises
#hubs.hubs{"ng-controller" => "EnterprisesCtrl"}
= inject_enterprises
#hubs.hubs{"ng-controller" => "EnterprisesCtrl", "ng-cloak" => true}
.row
.small-12.columns
%h1 Shop in your local area
%h1{"scroll-after-load" => (spree_current_user ? true : nil)} Shop in your local area
= render partial: "shared/components/enterprise_search"
= render partial: "home/filters"
= render "shared/components/enterprise_search"
= render "home/filters"
.row{bindonce: true}
.row
.small-12.columns
.active_table
%hub.active_table_node.row.animate-repeat{"ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])",
"ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}",
"scroll-after-load" => true,
"ng-controller" => "HubNodeCtrl",
id: "{{hub.hash}}"}
.small-12.columns
= render partial: 'home/skinny'
= render partial: 'home/fat'
.name-matches{"ng-show" => "nameMatchesFiltered.length > 0"}
%h2 Did you mean?
= render "home/hubs_table", enterprises: "nameMatches"
= render partial: 'shared/components/enterprise_no_results'
.distance-matches{"ng-if" => "nameMatchesFiltered.length == 0 || distanceMatchesShown"}
%h2{"ng-show" => "nameMatchesFiltered.length > 0 || query.length > 0"}
Closest to
%span{"ng-show" => "nameMatchesFiltered.length > 0"} {{ nameMatchesFiltered[0].name }}...
%span{"ng-hide" => "nameMatchesFiltered.length > 0"} {{ query }}...
= render "home/hubs_table", enterprises: "distanceMatches"
.show-distance-matches{"ng-show" => "nameMatchesFiltered.length > 0 && !distanceMatchesShown"}
%a{href: "", "ng-click" => "showDistanceMatches()"} Show me shops near {{ nameMatchesFiltered[0].name }}

View File

@@ -0,0 +1,10 @@
.active_table
%hub.active_table_node.row{"ng-repeat" => "hub in #{enterprises}Filtered = (#{enterprises} | visible | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+distance', '+orders_close_at'])",
"ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}",
"ng-controller" => "HubNodeCtrl",
id: "{{hub.hash}}"}
.small-12.columns
= render 'home/skinny'
= render 'home/fat'
= render 'shared/components/enterprise_no_results', enterprises: "#{enterprises}Filtered"

View File

@@ -1,7 +1,6 @@
.row.active_table_row{"ng-if" => "hub.is_distributor", "ng-click" => "toggle($event)", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}", bindonce: true}
.columns.small-12.medium-5.large-5.skinny-head
%a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub", "data-is-link" => "true"}
%a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub", "data-is-link" => "true"}
%i{bo: {class: "hub.icon_font"}}
%span.margin-top.hub-name-listing{"bo-bind" => "hub.name | truncate:40"}
@@ -9,9 +8,10 @@
%span.margin-top{"bo-text" => "hub.address.city"}
.columns.small-2.medium-1.large-1
%span.margin-top{"bo-bind" => "hub.address.state_name | uppercase"}
%span.margin-top{"ng-if" => "hub.distance != null && hub.distance > 0"} ({{ hub.distance / 1000 | number:0 }} km)
.columns.small-4.medium-3.large-3.text-right{"bo-if" => "hub.active"}
%a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"}
%a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub"}
%i.ofn-i_033-open-sign
%span.margin-top{ bo: { if: "current()" } }
%em Shopping here
@@ -19,17 +19,17 @@
%span{"bo-bind" => "hub.orders_close_at | sensible_timeframe"}
.columns.small-4.medium-3.large-3.text-right{"bo-if" => "!hub.active"}
%a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"}
%a.hub.open_closed{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-change-hub" => "hub"}
%i.ofn-i_032-closed-sign
%span.margin-top{ bo: { if: "current()" } }
%em Shopping here
%span.margin-top{ bo: { if: "!current()" } } Orders closed
.columns.small-2.medium-1.large-1.text-right
%span.margin-top
%span.margin-top
%i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"}
.row.active_table_row{"ng-if" => "!hub.is_distributor", "ng-class" => "closed"}
.row.active_table_row{"ng-if" => "!hub.is_distributor", "ng-class" => "closed", bindonce: true}
.columns.small-12.medium-6.large-5.skinny-head
%a.hub{"ng-click" => "openModal(hub)", "ng-class" => "{primary: hub.active, secondary: !hub.active}"}
%i{ng: {class: "hub.icon_font"}}
@@ -43,4 +43,3 @@
.columns.small-6.medium-3.large-4.text-right
%span.margin-top{ bo: { if: "!current()" } }
%em Profile only

View File

@@ -4,19 +4,20 @@
%h2 We're creating a new food system.
.row.content
.small-12.medium-3.columns.text-center
%h4
%strong= number_with_delimiter @num_producers
food producers
.small-12.medium-3.columns.text-center
%h4
%strong= number_with_delimiter @num_distributors
food shops
.small-12.medium-3.columns.text-center
%h4
%strong= number_with_delimiter @num_users
food shoppers
.small-12.medium-3.columns.text-center
%h4
%strong= number_with_delimiter @num_orders
food orders
- if ContentConfig.home_show_stats
.small-12.medium-3.columns.text-center
%h4
%strong= number_with_delimiter @num_producers
food producers
.small-12.medium-3.columns.text-center
%h4
%strong= number_with_delimiter @num_distributors
food shops
.small-12.medium-3.columns.text-center
%h4
%strong= number_with_delimiter @num_users
food shoppers
.small-12.medium-3.columns.text-center
%h4
%strong= number_with_delimiter @num_orders
food orders

View File

@@ -1,3 +1,7 @@
:css
#tagline:before { background-image: url("#{ContentConfig.home_hero.url}") }
%div{"ng-controller" => "HomeCtrl"}
= render partial: "shared/menu/alert"
@@ -6,8 +10,7 @@
.small-12.text-center.columns
%h1
/ TODO: Rohan - logo asset & width is content manageable:
-# TODO: SVGify
%img{src: "/assets/logo-white-notext.png", width: "250", title: "Open Food Network Australia"}
%img{src: "/assets/logo-white-notext.png", width: "250", title: Spree::Config.site_name}
%br/
%a.button.transparent{href: "/shops"}
Shop Now

View File

@@ -3,7 +3,7 @@
%meta{charset: 'utf-8'}/
%meta{name: 'viewport', content: "width=device-width,initial-scale=1.0"}/
%title= content_for?(:title) ? "#{yield(:title)} - Open Food Network" : 'Welcome to Open Food Network'
%title= content_for?(:title) ? "#{yield(:title)} - Open Food Network".html_safe : 'Welcome to Open Food Network'
- if Rails.env.production?
= favicon_link_tag
- else
@@ -11,7 +11,7 @@
%link{href: "https://fonts.googleapis.com/css?family=Roboto:400,300italic,400italic,300,700,700italic|Oswald:300,400,700", rel: "stylesheet", type: "text/css"}
= yield :scripts
%script{src: "//maps.googleapis.com/maps/api/js?libraries=places&sensor=false"}
%script{src: "//maps.googleapis.com/maps/api/js?libraries=places,geometry&sensor=false"}
= split_stylesheet_link_tag "darkswarm/all"
= javascript_include_tag "darkswarm/all"

View File

@@ -1,5 +1,5 @@
.row.active_table_row{"ng-if" => "open()", "ng-click" => "toggle($event)", "ng-class" => "{'open' : !ofn-i_032-closed-sign()}"}
.columns.small-12.medium-7.large-7.fat
/ Will add in long description available once clean up HTML formatting producer.long_description
%div{"bo-if" => "producer.description"}
@@ -10,23 +10,23 @@
%label &nbsp;
.columns.small-12.medium-5.large-5.fat
%div{"bo-if" => "producer.supplied_taxons"}
%label Shop for
%p.trans-sentence
%span.fat-taxons{"ng-repeat" => "taxon in producer.supplied_taxons"}
%render-svg{path: "{{taxon.icon}}"}
%span{"bo-text" => "taxon.name"}
%div.show-for-medium-up{"ng-if" => "producer.supplied_taxons.length==0"}
&nbsp;
%div{"bo-if" => "producer.email || producer.website || producer.phone"}
%label Contact
%p.word-wrap{"bo-if" => "producer.phone"}
Call
%span{"bo-text" => "producer.phone"}
Call
%span{"bo-text" => "producer.phone"}
%p.word-wrap{"bo-if" => "producer.email"}
%a{"bo-href" => "producer.email | stripUrl", target: "_blank", mailto: true}
@@ -39,20 +39,20 @@
%div{"bo-if" => "producer.twitter || producer.facebook || producer.linkedin || producer.instagram"}
%label Follow
.follow-icons{bindonce: true}
%span{"bo-if" => "producer.twitter"}
%span{"bo-if" => "producer.twitter"}
%a{"bo-href-i" => "http://twitter.com/{{producer.twitter}}", target: "_blank"}
%i.ofn-i_041-twitter
%span{"bo-if" => "producer.facebook"}
%a{"bo-href-i" => "http://{{producer.facebook | stripUrl}}", target: "_blank"}
%i.ofn-i_044-facebook
%span{"bo-if" => "producer.linkedin"}
%a{"bo-href-i" => "http://{{producer.linkedin | stripUrl}}", target: "_blank"}
%i.ofn-i_042-linkedin
%span{"bo-if" => "producer.instagram"}
%a{"bo-href-i" => "http://instagram.com/{{producer.instagram}}", target: "_blank"}
%a{"bo-href-i" => "http://instagram.com/{{producer.instagram}}", target: "_blank"}
%i.ofn-i_043-instagram
.row.active_table_row.pad-top{"ng-if" => "open()", "bo-if" => "producer.hubs"}
@@ -60,19 +60,18 @@
.row
.columns.small-12.fat
%div{"bo-if" => "producer.name"}
%label
%label
Shop for
%span.turquoise{"bo-text" => "producer.name"}
%span.turquoise{"bo-text" => "producer.name"}
products at:
%div.show-for-medium-up{"bo-if" => "!producer.name"}
&nbsp;
.row.cta-container
.columns.small-12
%a.cta-hub{"ng-repeat" => "hub in producer.hubs | orderBy:'-active'",
"bo-href" => "hub.path", "ofn-empties-cart" => "hub",
%a.cta-hub{"ng-repeat" => "hub in producer.hubs | visible | orderBy:'-active'",
"bo-href" => "hub.path", "ofn-change-hub" => "hub",
"bo-class" => "{primary: hub.active, secondary: !hub.active}"}
%i.ofn-i_033-open-sign{"bo-if" => "hub.active"}
%i.ofn-i_032-closed-sign{"bo-if" => "!hub.active"}
.hub-name{"bo-text" => "hub.name"}
.button-address{"bo-bind" => "[hub.address.city, hub.address.state_name] | printArray"}

View File

@@ -15,7 +15,6 @@
.small-12.columns
.active_table
%producer.active_table_node.row.animate-repeat{id: "{{producer.path}}",
"scroll-after-load" => true,
"ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)",
"ng-controller" => "ProducerNodeCtrl",
"ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}",

View File

@@ -20,79 +20,14 @@
-# If there is a time-sensitive offer you can write it here, e.g.
-# Sign up before 30th June for an extra month free!
%br
%table.signup-table.producers-table{cellpadding: "0", cellspacing: "0"}
%thead
%tr
%td
%h5
%td.text-center{width: "20%"}
%h5 Basic
%td.text-center{width: "24%"}
%h5 Plus
%tr
%td
%p
%strong Profile
%p
%span.text-small Get a profile on the Open Food Network - including a listing on the Producers page and a pin on the OFN Map. These help people to find and connect with you.
%p
%span.text-small Option to add and manage your products on OFN, enabling you to act as a supplier to other OFN shopfronts.
%td.text-center
%i.ofn-i_003-check.text-big
%td.text-center
%i.ofn-i_003-check.text-big
%tr
%td
%p
%strong Shop
%p.text-small Get a shopfront on OFN to sell your produce direct!
%p.text-small Basic support included:
%ul.small
%li User manual
%li Post queries to discussion forum/via contact form
%li Report bugs/suggestions
%li Regular release notes what's new
%td.text-center
%td.text-center
%i.ofn-i_003-check.text-big
%tfoot
%tr
%td
%td.text-center{valign: "top"}
%h2 Free
%td.text-center{valign: "top"}
%h2
.text-small
first
%br
month
Free
%p.text-small
Then, 2% of total transactions
%br
%em
Capped at
%strong $50
per month
%br
%em (Special offer for 2015)
= ContentConfig.producer_signup_pricing_table_html.html_safe
#producer-case-studies
.row
.small-12.medium-10.medium-offset-1.columns
%h2.text-center Stories from our producers.
%br
.row
.small-12.medium-6.columns
= render 'shared/case_study', img_src: "/assets/case-studies/jonai.png", title: "Jonai Farms", description: "Jonai Farms is an ethical pork and beef “Community Supported Agriculture” farm that delivers to a range of hubs in Melbourne and central Victoria every month.", link: "http://www.jonaifarms.com.au"
.small-12.medium-6.columns
= render 'shared/case_study', img_src: "/assets/case-studies/wandiful.png", title: "Wandiful Produce", description: "Biodynamically grown in NE Victoria, Wandiful Produce supplies hazelnuts, chestnuts and associated products (bliss balls - yum!) shipped to your door.", link: "https://openfoodnetwork.org.au/wandiful-produce/shop"
.row
.small-12.medium-6.columns
= render 'shared/case_study', img_src: "/assets/case-studies/longley.png", title: "Longley Organic Farm", description: "Near Hobart, Longley Organic Farm provides berries and vegetables for local food cooperatives and small shops and through its roadside stall.", link: "https://openfoodnetwork.org.au/longley-organic-farm/shop"
.small-12.medium-6.columns
= render 'shared/case_study', img_src: "/assets/case-studies/jindivick.jpg", title: "Jindivick Hydroponics", description: "Selling through farmers markets and the South East Food Hub, Jindivick Hydroponics grow an ever increasing range of vegetables including tomatoes, cucumbers and beans.", link: "https://openfoodnetwork.org.au/producers#/#jindivick-hydroponics"
= ContentConfig.producer_signup_case_studies_html.html_safe
.pane#cta
.row
@@ -106,17 +41,6 @@
.row
.small-12.medium-10.medium-offset-1.columns
%h2.text-center Here's the detail.
= render 'shared/enterprise_type_flowchart', type: 'producers'
%h4 Join a marketplace of independent online food stores
%p Create an online shop front and join the network of online farmers markets on the Open Food Network. Set up a Producer Shop and sell your products direct to customers, or create a Food Hub and sell products from multiple producers.
%h4 Connect with a new source of customers
%p Gain access to the growing crowd of conscientious buyers on the Open Food Network, all looking to make more ethical and sustainable choices when purchasing food.
%h4 Manage your business online
%p The Open Food Network provides online tools to help with the day-to-day running of your business. Manage your product listing and stock levels. Create, receive and manage orders from your buyers, and organise payments online. Generate invoices for orders and export them to your accounting software, and access a range of reports.
%h4 Start with a simple listing on our directory
%p Create a profile on the Open Food Network and gain exposure to a new marketplace of potential buyers. Tell your story in words and images, provide your contact details, and drive connections to your social and online presence.
%p Access new wholesale opportunities by connecting with food hubs in your region who can sell and distribute your products to buyers on the Open Food Network.
= ContentConfig.producer_signup_detail_html.html_safe
= render partial: "shared/footer"

View File

@@ -1,16 +0,0 @@
.enterprise-type-flowchart
= image_tag "enterprise-type.png"
%br
%br
%br
%br
%br
%p{style: "text-align: center;"}
%a{href: "http://www.openfoodnetwork.org/platform/features/"} More Features
\|
%a{href: "http://www.openfoodnetwork.org/platform/user-guide/"} User Guide
\|
- if type == 'hubs'
%a{href: "http://www.openfoodnetwork.org/platform/user-guide/faqs/hub-faqs/"} Hub FAQs
- elsif type == 'producers'
%a{href: "http://www.openfoodnetwork.org/platform/user-guide/faqs/producer-faqs/"} Producer FAQs

View File

@@ -116,11 +116,11 @@
.row
.small-12.medium-3.medium-offset-2.columns.text-left
%a{href: root_path}
%img{src: "/assets/logo-color.png", srcset: "/assets/logo-color.svg", width: "220px"}
%img{src: ContentConfig.footer_logo.url, width: "220"}
.small-12.medium-5.columns.text-left
%p.text-small
Read our
%a{href: "/Terms-of-service.pdf"} Terms &amp; conditions
%a{href: ContentConfig.footer_tos_url} Terms &amp; conditions
&#124;
Find us on
%a{href:"https://github.com/openfoodfoundation/openfoodnetwork", target: "_blank"} Github

View File

@@ -1,4 +1,5 @@
%producer.row{"ng-show" => "filteredEnterprises.length == 0"}
- enterprises ||= 'filteredEnterprises'
%producer.row{"ng-show" => "#{enterprises}.length == 0"}
%p.no-results
Sorry, no results found for
%strong {{query}}.

View File

@@ -3,5 +3,5 @@
%input{type: :text,
"ng-model" => "query",
placeholder: t('search_by_name'),
"ng-debounce" => "150",
"ng-debounce" => "500",
"ofn-disable-enter" => true}

View File

@@ -3,6 +3,7 @@
%a.alert-cta{href: "http://www.openfoodnetwork.org", target: "_blank"}
%h6
Interested in selling food on the Open Food Network? &nbsp;
%strong Start here
%i.ofn-i_054-point-right
%strong
Start here
%i.ofn-i_054-point-right
%a.close{ ng: { click: "close()" } } &times;

View File

@@ -9,7 +9,11 @@
.joyride-tip-guide{"ng-class" => "{ in: open }", "ng-show" => "open"}
%span.joyride-nub.top
.joyride-content-wrapper
%h5 Your shopping cart
%h5.text-left Your shopping cart
.buttons.text-right
%a.button.secondary.tiny.add_to_cart{ href: cart_path, type: :submit, "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" }
{{ Cart.dirty ? 'Updating cart...' : (Cart.empty() ? 'Cart empty' : 'Edit your cart' ) }}
%a.button.primary.tiny{href: checkout_path, "ng-disabled" => "Cart.dirty || Cart.empty()"} Checkout now
%table
%tr.product-cart{"ng-repeat" => "line_item in Cart.line_items_present()",
"ng-controller" => "LineItemCtrl", "id" => "cart-variant-{{ line_item.variant.id }}"}

Some files were not shown because too many files have changed in this diff Show More