Merge branch 'master' into order-cycle-selection

Conflicts:
	Gemfile
	Gemfile.lock
	app/controllers/spree/orders_controller_decorator.rb
	app/models/enterprise.rb
	app/models/spree/order_decorator.rb
	app/views/spree/checkout/_other_available_distributors.html.erb
	app/views/spree/products/_add_to_cart.html.haml
	lib/open_food_web/distributor_change_validator.rb
	spec/spec_helper.rb
This commit is contained in:
Andrew Spinks
2013-07-23 14:42:57 +10:00
140 changed files with 3209 additions and 304 deletions

5
.gitignore vendored
View File

@@ -1,5 +1,7 @@
.bundle
.rbenv-version
.ruby-version
.ruby-gemset
.swp
*.swo
*.swp
@@ -16,6 +18,7 @@ tmp/
*.~lock.*
.emacs.desktop
.DS_Store
*.sublime-project*
spec/javascripts/generated/*
db/development_structure.sql
db/test_structure.sql
@@ -29,4 +32,6 @@ public/images
public/spree
config/abr.yml
config/heroku_env.rb
config/initializers/feature_toggle.rb
NERD_tree*
coverage

48
.rvmrc Normal file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# This is an RVM Project .rvmrc file, used to automatically load the ruby
# development environment upon cd'ing into the directory
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
# Only full ruby name is supported here, for short names use:
# echo "rvm use 1.9.3" > .rvmrc
environment_id="ruby-1.9.3-p392@openfoodweb"
# Uncomment the following lines if you want to verify rvm version per project
# rvmrc_rvm_version="1.18.21 (stable)" # 1.10.1 seams as a safe start
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
# return 1
# }
# First we attempt to load the desired environment directly from the environment
# file. This is very fast and efficient compared to running through the entire
# CLI and selector. If you want feedback on which environment was used then
# insert the word 'use' after --create as this triggers verbose mode.
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
then
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
else
# If the environment file has not yet been created, use the RVM CLI to select.
rvm --create "$environment_id" || {
echo "Failed to create RVM environment '${environment_id}'."
return 1
}
fi
# If you use bundler, this might be useful to you:
# if [[ -s Gemfile ]] && {
# ! builtin command -v bundle >/dev/null ||
# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
# }
# then
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
# gem install bundler
# fi
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
# then
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
# fi

26
Gemfile
View File

@@ -1,13 +1,13 @@
source 'http://rubygems.org'
source 'https://rubygems.org'
ruby "1.9.3"
gem 'rails', '3.2.11'
gem 'rails', '3.2.13'
gem 'pg'
gem 'spree', :git => 'git://github.com/spree/spree.git', :branch => '1-3-stable'
gem 'spree_i18n', :git => 'git://github.com/spree/spree_i18n.git'
gem 'spree_paypal_express', :git => 'git://github.com/spree/spree_paypal_express.git', :branch => '1-3-stable'
gem 'spree_last_address', :git => 'git://github.com/eaterprises/spree-last-address.git'
gem 'spree_last_address', :git => 'git://github.com/eaterprises/spree-last-address.git', :branch => '1-3-stable'
gem 'spree_auth_devise', :git => 'https://github.com/spree/spree_auth_devise.git', :branch => '1-3-stable'
gem 'comfortable_mexican_sofa'
@@ -18,12 +18,18 @@ gem 'simple_form', :git => 'git://github.com/RohanM/simple_form.git'
gem 'unicorn'
gem 'bugsnag'
gem 'spree_heroku', :git => 'git://github.com/eaterprises/spree-heroku.git'
gem 'newrelic_rpm'
gem 'haml'
gem 'sass'
gem 'aws-sdk'
gem 'db2fog'
gem 'andand'
gem 'truncate_html'
gem 'representative_view'
gem 'rabl'
gem 'oj'
gem 'chili', :github => 'eaterprises/chili'
gem 'deface', :github => 'spree/deface'
# Gems used only for assets and not required
# in production environments by default.
@@ -36,6 +42,8 @@ group :assets do
gem 'therubyracer'
gem 'uglifier', '>= 1.0.3'
gem 'turbo-sprockets-rails3'
end
gem 'jquery-rails'
@@ -51,8 +59,16 @@ group :test, :development do
gem 'faker'
gem 'capybara'
gem 'database_cleaner', '0.7.1', :require => false
gem 'simplecov', :require => false
gem 'awesome_print'
gem "letter_opener"
gem 'letter_opener'
gem 'timecop'
end
group :chili do
gem 'enterprises_distributor_info_rich_text_feature', path: 'lib/chili/enterprises_distributor_info_rich_text_feature'
gem 'eaterprises_feature', path: 'lib/chili/eaterprises_feature'
gem 'local_organics_feature', path: 'lib/chili/local_organics_feature'
end
group :development do

View File

@@ -7,63 +7,71 @@ GIT
activemodel (~> 3.0)
GIT
remote: git://github.com/eaterprises/spree-heroku.git
revision: a1e07bf7a22fc0c07a1be9148f477d20b557dbf6
remote: git://github.com/eaterprises/chili.git
revision: b82608623d504eb39f852115b138a53af5f89505
specs:
spree_heroku (1.0)
aws-sdk (~> 1.3.4)
spree_core (>= 0.70.0)
chili (3.1.0)
deface (~> 1.0.0.rc2)
rails (~> 3.2)
GIT
remote: git://github.com/eaterprises/spree-last-address.git
revision: f5ed71a5482fbc16da559737903bb46fe032c150
revision: f93ea5a6b06e4605ea9c3fc5ef8e71f8e6aa0ffc
branch: 1-3-stable
specs:
spree_last_address (1.1.0)
spree_core (>= 1.1)
GIT
remote: git://github.com/spree/deface.git
revision: 1110a1336252109bce7f98f9182042e0bc2930ae
specs:
deface (1.0.0.rc3)
colorize (>= 0.5.8)
nokogiri (~> 1.6.0)
rails (>= 3.1)
GIT
remote: git://github.com/spree/spree.git
revision: 36a59eb311470a2c24b9aa826ecad114c518c01f
revision: 94566bf1b7d50f474333d10844534e570b6edf5a
branch: 1-3-stable
specs:
spree (1.3.3.beta)
spree_api (= 1.3.3.beta)
spree_cmd (= 1.3.3.beta)
spree_core (= 1.3.3.beta)
spree_dash (= 1.3.3.beta)
spree_promo (= 1.3.3.beta)
spree_sample (= 1.3.3.beta)
spree_api (1.3.3.beta)
spree_core (= 1.3.3.beta)
spree (1.3.3)
spree_api (= 1.3.3)
spree_cmd (= 1.3.3)
spree_core (= 1.3.3)
spree_promo (= 1.3.3)
spree_sample (= 1.3.3)
spree_api (1.3.3)
spree_core (= 1.3.3)
versioncake (= 0.4.0)
spree_cmd (1.3.3.beta)
spree_cmd (1.3.3)
thor (>= 0.14.6)
spree_core (1.3.3.beta)
activemerchant (~> 1.29.3)
spree_core (1.3.3)
activemerchant (~> 1.31)
acts_as_list (= 0.1.4)
awesome_nested_set (= 2.1.5)
aws-sdk (~> 1.3.4)
cancan (= 1.6.8)
deface (>= 0.9.0)
ffaker (~> 1.12.0)
highline (= 1.6.11)
ffaker (~> 1.15.0)
highline (= 1.6.18)
jquery-rails (~> 2.2.0)
json (>= 1.5.5)
kaminari (= 0.13.0)
money (= 5.0.0)
paperclip (~> 2.8)
rabl (= 0.7.2)
rails (~> 3.2.11)
ransack (~> 0.7.2)
select2-rails (~> 3.2)
rails (~> 3.2.13)
ransack (= 0.7.2)
select2-rails (= 3.2.1)
state_machine (= 1.1.2)
stringex (~> 1.3.2)
spree_dash (1.3.3.beta)
httparty (~> 0.8.1)
spree_core (= 1.3.3.beta)
spree_promo (1.3.3.beta)
spree_core (= 1.3.3.beta)
spree_sample (1.3.3.beta)
spree_core (= 1.3.3.beta)
truncate_html (~> 0.5.5)
spree_promo (1.3.3)
spree_core (= 1.3.3)
spree_sample (1.3.3)
spree_core (= 1.3.3)
GIT
remote: git://github.com/spree/spree_i18n.git
@@ -75,7 +83,7 @@ GIT
GIT
remote: git://github.com/spree/spree_paypal_express.git
revision: 483f6ababa9c8a1c574aad0a948c96cc21a591db
revision: 9d70ab6d537420ff41468d05d6d2ac3c8d2ff3bf
branch: 1-3-stable
specs:
spree_paypal_express (1.2.0)
@@ -83,28 +91,49 @@ GIT
GIT
remote: https://github.com/spree/spree_auth_devise.git
revision: ab03a7a22740b52f96bf0311a1e9f18a99ee02f2
revision: b2b82f1bbe4b47a0713bcf70af8b3c17fa39c0d0
branch: 1-3-stable
specs:
spree_auth_devise (1.0.0)
cancan (~> 1.6.7)
devise (~> 2.2.3)
devise-encryptable (= 0.1.1)
devise-encryptable (= 0.1.2)
spree_core
GEM
remote: http://rubygems.org/
PATH
remote: lib/chili/eaterprises_feature
specs:
actionmailer (3.2.11)
actionpack (= 3.2.11)
mail (~> 2.4.4)
actionpack (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
eaterprises_feature (0.0.1)
chili (~> 3.1)
rails (~> 3.2.11)
PATH
remote: lib/chili/enterprises_distributor_info_rich_text_feature
specs:
enterprises_distributor_info_rich_text_feature (0.0.1)
chili (~> 3.1)
rails (~> 3.2.11)
PATH
remote: lib/chili/local_organics_feature
specs:
local_organics_feature (0.0.1)
chili (~> 3.1)
rails (~> 3.2.11)
GEM
remote: https://rubygems.org/
specs:
actionmailer (3.2.13)
actionpack (= 3.2.13)
mail (~> 2.5.3)
actionpack (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
rack (~> 1.4.0)
rack (~> 1.4.5)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
@@ -112,7 +141,7 @@ GEM
active_utils (1.0.5)
activesupport (>= 2.3.11)
i18n
activemerchant (1.29.3)
activemerchant (1.34.0)
active_utils (>= 1.0.2)
activesupport (>= 2.3.14)
builder (>= 2.0.0)
@@ -120,19 +149,19 @@ GEM
json (>= 1.5.1)
money
nokogiri
activemodel (3.2.11)
activesupport (= 3.2.11)
activemodel (3.2.13)
activesupport (= 3.2.13)
builder (~> 3.0.0)
activerecord (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
activerecord (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
activesupport (3.2.11)
i18n (~> 0.6)
activeresource (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
activesupport (3.2.13)
i18n (= 0.6.1)
multi_json (~> 1.0)
acts_as_list (0.1.4)
addressable (2.3.3)
@@ -162,7 +191,7 @@ GEM
xpath (~> 1.0.0)
childprocess (0.3.9)
ffi (~> 1.0, >= 1.0.11)
chunky_png (1.2.7)
chunky_png (1.2.8)
climate_control (0.0.3)
activesupport (>= 3.0)
cocaine (0.5.1)
@@ -175,6 +204,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.3.3)
colorize (0.5.8)
columnize (0.3.6)
comfortable_mexican_sofa (1.6.24)
active_link_to (~> 1.0.0)
@@ -187,24 +217,26 @@ GEM
compass-rails (1.0.3)
compass (>= 0.12.2, < 0.14)
database_cleaner (0.7.1)
debugger (1.5.0)
db2fog (0.8.0)
activerecord (~> 3.0)
fog (~> 1.0)
rails (~> 3.0)
debugger (1.6.1)
columnize (>= 0.3.1)
debugger-linecache (~> 1.2.0)
debugger-ruby_core_source (~> 1.2.0)
debugger-ruby_core_source (~> 1.2.3)
debugger-linecache (1.2.0)
debugger-ruby_core_source (1.2.0)
deface (0.9.1)
nokogiri (~> 1.5.0)
rails (~> 3.1)
devise (2.2.3)
debugger-ruby_core_source (1.2.3)
devise (2.2.4)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
railties (~> 3.1)
warden (~> 1.2.1)
devise-encryptable (0.1.1)
devise (>= 2.1.0.rc)
devise-encryptable (0.1.2)
devise (>= 2.1.0)
diff-lcs (1.1.3)
erubis (2.7.0)
excon (0.25.3)
execjs (1.4.0)
multi_json (~> 1.0)
factory_girl (3.3.0)
@@ -214,21 +246,32 @@ GEM
railties (>= 3.0.0)
faker (1.0.1)
i18n (~> 0.4)
ffaker (1.12.1)
ffaker (1.15.0)
ffi (1.4.0)
fog (1.14.0)
builder
excon (~> 0.25.0)
formatador (~> 0.2.0)
mime-types
multi_json (~> 1.0)
net-scp (~> 1.1)
net-ssh (>= 2.1.3)
nokogiri (~> 1.5)
ruby-hmac
formatador (0.2.4)
fssm (0.2.10)
haml (3.1.6)
highline (1.6.11)
hike (1.2.1)
httparty (0.8.3)
highline (1.6.18)
hike (1.2.3)
httparty (0.11.0)
multi_json (~> 1.0)
multi_xml
multi_xml (>= 0.5.2)
i18n (0.6.1)
journey (1.0.4)
jquery-rails (2.2.1)
jquery-rails (2.2.2)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
json (1.7.7)
json (1.8.0)
kaminari (0.13.0)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
@@ -239,18 +282,24 @@ GEM
letter_opener (1.0.0)
launchy (>= 2.0.4)
libv8 (3.3.10.4)
mail (2.4.4)
i18n (>= 0.4.0)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
method_source (0.8.1)
mime-types (1.21)
mime-types (1.23)
mini_portile (0.5.1)
money (5.0.0)
i18n (~> 0.4)
json
multi_json (1.6.1)
multi_xml (0.5.3)
nokogiri (1.5.6)
multi_json (1.7.7)
multi_xml (0.5.4)
net-scp (1.1.2)
net-ssh (>= 2.6.5)
net-ssh (2.6.8)
newrelic_rpm (3.6.5.130)
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
oj (2.1.2)
orm_adapter (0.4.0)
paperclip (2.8.0)
activerecord (>= 2.3.0)
@@ -261,7 +310,7 @@ GEM
polyamorous (0.5.0)
activerecord (~> 3.0)
polyglot (0.3.3)
pry (0.9.12)
pry (0.9.12.2)
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.4)
@@ -278,28 +327,28 @@ GEM
rack
rack-test (0.6.2)
rack (>= 1.0)
rails (3.2.11)
actionmailer (= 3.2.11)
actionpack (= 3.2.11)
activerecord (= 3.2.11)
activeresource (= 3.2.11)
activesupport (= 3.2.11)
rails (3.2.13)
actionmailer (= 3.2.13)
actionpack (= 3.2.13)
activerecord (= 3.2.13)
activeresource (= 3.2.13)
activesupport (= 3.2.13)
bundler (~> 1.0)
railties (= 3.2.11)
railties (3.2.11)
actionpack (= 3.2.11)
activesupport (= 3.2.11)
railties (= 3.2.13)
railties (3.2.13)
actionpack (= 3.2.13)
activesupport (= 3.2.13)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
raindrops (0.9.0)
rake (10.0.3)
rake (10.1.0)
ransack (0.7.2)
actionpack (~> 3.0)
activerecord (~> 3.0)
polyamorous (~> 0.5.0)
rdoc (3.12.1)
rdoc (3.12.2)
json (~> 1.4)
representative (1.0.5)
activesupport (>= 2.2.2)
@@ -322,14 +371,14 @@ GEM
activesupport (>= 3.0)
railties (>= 3.0)
rspec (~> 2.10.0)
ruby-hmac (0.4.0)
rubyzip (0.9.9)
sass (3.1.19)
sass-rails (3.2.5)
sass (3.2.9)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
select2-rails (3.3.0)
sass-rails (~> 3.2)
select2-rails (3.2.1)
thor (~> 0.14)
selenium-webdriver (2.31.0)
childprocess (>= 0.2.5)
@@ -338,7 +387,11 @@ GEM
websocket (~> 1.0.4)
shoulda-matchers (1.1.0)
activesupport (>= 3.0.0)
slop (3.4.4)
simplecov (0.7.1)
multi_json (~> 1.0)
simplecov-html (~> 0.7.1)
simplecov-html (0.7.1)
slop (3.4.5)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)
@@ -348,15 +401,19 @@ GEM
stringex (1.3.3)
therubyracer (0.10.1)
libv8 (~> 3.3.10)
thor (0.17.0)
tilt (1.3.3)
treetop (1.4.12)
thor (0.18.1)
tilt (1.4.1)
timecop (0.6.2.2)
treetop (1.4.14)
polyglot
polyglot (>= 0.3.1)
truncate_html (0.5.5)
turbo-sprockets-rails3 (0.3.6)
railties (> 3.2.8, < 4.0.0)
sprockets (>= 2.0.0)
turn (0.8.3)
ansi
tzinfo (0.3.35)
tzinfo (0.3.37)
uglifier (1.2.4)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
@@ -364,7 +421,7 @@ GEM
kgio (~> 2.6)
rack
raindrops (~> 0.7)
uuidtools (2.1.3)
uuidtools (2.1.4)
versioncake (0.4.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
@@ -384,32 +441,44 @@ DEPENDENCIES
aws-sdk
bugsnag
capybara
chili!
coffee-rails (~> 3.2.1)
comfortable_mexican_sofa
compass-rails
database_cleaner (= 0.7.1)
db2fog
debugger-linecache
deface!
eaterprises_feature!
enterprises_distributor_info_rich_text_feature!
factory_girl_rails
faker
haml
jquery-rails
letter_opener
local_organics_feature!
newrelic_rpm
oj
pg
pry-debugger
rails (= 3.2.11)
rabl
rails (= 3.2.13)
representative_view
rspec-rails
sass
sass-rails (~> 3.2.3)
shoulda-matchers
simple_form!
simplecov
spree!
spree_auth_devise!
spree_heroku!
spree_i18n!
spree_last_address!
spree_paypal_express!
therubyracer
timecop
truncate_html
turbo-sprockets-rails3
turn (~> 0.8.3)
uglifier (>= 1.0.3)
unicorn

View File

@@ -7,8 +7,8 @@ produce.
## Dependencies
* Rails 3.x
* Ruby >= 1.9.2
* Rails 3.2.x
* Ruby >= 1.9.3
* PostgreSQL database
* See Gemfile for a list of gems required
@@ -75,6 +75,8 @@ speak to Andrew Spinks.
* Andrew Spinks (http://github.com/andrewspinks)
* Rohan Mitchell (http://github.com/rohanm)
* Rob Harrington (http://github.com/oeoeaio)
* Alex Serdyuk (http://github.com/alexs333)
* David Cook (http://github.com/dacook)
## Licence

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
app/assets/images/ofw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,396 @@
var productsApp = angular.module('bulk_product_update', [])
productsApp.config(["$httpProvider", function(provider) {
provider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}]);
productsApp.directive('ngDecimal', function () {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
var numRegExp = /^\d+(\.\d+)?$/;
element.bind('blur', function() {
scope.$apply(ngModel.$setViewValue(ngModel.$modelValue));
ngModel.$render();
});
ngModel.$parsers.push(function(viewValue){
if (angular.isString(viewValue) && numRegExp.test(viewValue)){
if (viewValue.indexOf(".") == -1){
return viewValue+".0";
}
}
return viewValue;
});
}
}
});
productsApp.directive('ngTrackProduct', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
var property_name = attrs.ngTrackProduct;
ngModel.$parsers.push(function(viewValue){
if (ngModel.$dirty) {
addDirtyProperty(scope.dirtyProducts, scope.product.id, property_name, viewValue);
scope.displayDirtyProducts();
}
return viewValue;
});
}
}
});
productsApp.directive('ngTrackVariant', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
var property_name = attrs.ngTrackVariant;
ngModel.$parsers.push(function(viewValue){
var dirtyVariants = {};
if (scope.dirtyProducts.hasOwnProperty(scope.product.id) && scope.dirtyProducts[scope.product.id].hasOwnProperty("variants")) dirtyVariants = scope.dirtyProducts[scope.product.id].variants;
if (ngModel.$dirty) {
addDirtyProperty(dirtyVariants, scope.variant.id, property_name, viewValue);
addDirtyProperty(scope.dirtyProducts, scope.product.id, "variants", dirtyVariants);
scope.displayDirtyProducts();
}
return viewValue;
});
}
}
});
productsApp.directive('ngToggleVariants',function(){
return {
link: function(scope,element,attrs){
if (scope.displayProperties[scope.product.id].showVariants) { element.removeClass('icon-chevron-right'); element.addClass('icon-chevron-down'); }
else { element.removeClass('icon-chevron-down'); element.addClass('icon-chevron-right'); }
element.on('click', function(){
scope.$apply(function(){
if (scope.displayProperties[scope.product.id].showVariants){
scope.displayProperties[scope.product.id].showVariants = false;
element.removeClass('icon-chevron-down');
element.addClass('icon-chevron-right');
}
else {
scope.displayProperties[scope.product.id].showVariants = true;
element.removeClass('icon-chevron-right');
element.addClass('icon-chevron-down');
}
});
});
}
};
});
productsApp.directive('ngToggleColumn',function(){
return {
link: function(scope,element,attrs){
if (!scope.column.visible) { element.addClass("unselected"); }
element.click('click', function(){
scope.$apply(function(){
if (scope.column.visible) { scope.column.visible = false; element.addClass("unselected"); }
else { scope.column.visible = true; element.removeClass("unselected"); }
});
});
}
};
});
productsApp.directive('ngToggleColumnList', function($compile){
return {
link: function(scope,element,attrs){
var dialogDiv = element.next();
element.on('click',function(){
var pos = element.position();
var height = element.outerHeight();
dialogDiv.css({
position: "absolute",
top: (pos.top + height) + "px",
left: pos.left + "px",
}).toggle();
});
}
}
});
productsApp.directive('datetimepicker', function ($parse) {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.datetimepicker({
dateFormat: 'yy-mm-dd',
timeFormat: 'HH:mm:ss',
stepMinute: 15,
onSelect:function (dateText, inst) {
scope.$apply(function(scope){
ngModel.$setViewValue(dateText); // Fires ngModel.$parsers
});
}
});
}
}
});
productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http, dataFetcher) {
$scope.updateStatusMessage = {
text: "",
style: {}
}
$scope.columns = {
name: { name: 'Name', visible: true },
supplier: { name: 'Supplier', visible: true },
price: { name: 'Price', visible: true },
on_hand: { name: 'On Hand', visible: true },
available_on: { name: 'Available On', visible: true }
}
$scope.initialise = function(spree_api_key){
var authorise_api_reponse = "";
dataFetcher('/api/users/authorise_api?token='+spree_api_key).then(function(data){
authorise_api_reponse = data;
$scope.spree_api_key_ok = data.hasOwnProperty("success") && data["success"] == "Use of API Authorised";
if ($scope.spree_api_key_ok){
$http.defaults.headers.common['X-Spree-Token'] = spree_api_key;
dataFetcher('/api/enterprises?template=bulk_index;q[is_primary_producer_eq]=true').then(function(data){
$scope.suppliers = data;
// Need to have suppliers before we get products so we can match suppliers to product.supplier
dataFetcher('/api/products?template=bulk_index').then(function(data){
$scope.resetProducts(data);
});
});
}
else if (authorise_api_reponse.hasOwnProperty("error")){ $scope.api_error_msg = authorise_api_reponse("error"); }
else{ api_error_msg = "You don't have an API key yet. An attempt was made to generate one, but you are currently not authorised, please contact your site administrator for access." }
});
};
$scope.resetProducts = function(data){
$scope.products = data;
$scope.dirtyProducts = {};
$scope.displayProperties = $scope.displayProperties || {};
angular.forEach($scope.products,function(product){
$scope.displayProperties[product.id] = $scope.displayProperties[product.id] || { showVariants: false };
$scope.matchSupplier(product);
});
}
$scope.matchSupplier = function(product){
for (i in $scope.suppliers){
var supplier = $scope.suppliers[i];
if (angular.equals(supplier,product.supplier)){
product.supplier = supplier;
break;
}
}
}
$scope.updateOnHand = function(product){
product.on_hand = onHand(product);
}
$scope.editWarn = function(product,variant){
if ( ( $scope.dirtyProductCount() > 0 && confirm("Unsaved changes will be lost. Continue anyway?") ) || ( $scope.dirtyProductCount() == 0 ) ){
window.location = "/admin/products/"+product.permalink_live+(variant ? "/variants/"+variant.id : "")+"/edit";
}
}
$scope.deleteProduct = function(product){
if (confirm("Are you sure?")){
$http({
method: 'DELETE',
url: '/api/products/'+product.id
})
.success(function(data){
$scope.products.splice($scope.products.indexOf(product),1);
if ($scope.dirtyProducts.hasOwnProperty(product.id)) delete $scope.dirtyProducts[product.id];
$scope.displayDirtyProducts();
})
}
}
$scope.deleteVariant = function(product,variant){
if (confirm("Are you sure?")){
$http({
method: 'DELETE',
url: '/api/products/'+product.id+"/variants/"+variant.id
})
.success(function(data){
product.variants.splice(product.variants.indexOf(variant),1);
if ($scope.dirtyProducts.hasOwnProperty(product.id) && $scope.dirtyProducts[product.id].hasOwnProperty("variants") && $scope.dirtyProducts[product.id].variants.hasOwnProperty(variant.id)) delete $scope.dirtyProducts[product.id].variants[variant.id];
$scope.displayDirtyProducts();
})
}
}
$scope.cloneProduct = function(product){
dataFetcher("/admin/products/"+product.permalink_live+"/clone.json").then(function(data){
// Ideally we would use Spree's built in respond_override helper here to redirect the user after a successful clone with .json in the accept headers
// However, at the time of writing there appears to be an issue which causes the respond_with block in the destroy action of Spree::Admin::Product to break
// when a respond_overrride for the clone action is used.
var id = data.product.id;
dataFetcher("/api/products/"+id+"?template=bulk_show").then(function(data){
var newProduct = data;
$scope.matchSupplier(newProduct);
$scope.products.push(newProduct);
});
});
}
$scope.hasVariants = function(product){
return Object.keys(product.variants).length > 0;
}
$scope.updateProducts = function(productsToSubmit){
$scope.displayUpdating();
$http({
method: 'POST',
url: '/admin/products/bulk_update',
data: productsToSubmit
})
.success(function(data){
if (angular.toJson($scope.products) == angular.toJson(data)){
$scope.resetProducts(data);
$scope.displaySuccess();
}
else{
$scope.displayFailure("Product lists do not match.");
}
})
.error(function(data,status){
$scope.displayFailure("Server returned with error status: "+status);
});
}
$scope.prepareProductsForSubmit = function(){
var productsToSubmit = filterSubmitProducts($scope.dirtyProducts);
$scope.updateProducts(productsToSubmit);
}
$scope.setMessage = function(model,text,style,timeout){
model.text = text;
model.style = style;
if (model.timeout) $timeout.cancel(model.timeout);
if (timeout){
model.timeout = $timeout(function() { $scope.setMessage(model,"",{},false); }, timeout, true);
}
}
$scope.displayUpdating = function(){
$scope.setMessage($scope.updateStatusMessage,"Updating...",{ color: "orange" },false);
}
$scope.displaySuccess = function(){
$scope.setMessage($scope.updateStatusMessage,"Update complete",{ color: "green" },3000);
}
$scope.displayFailure = function(failMessage){
$scope.setMessage($scope.updateStatusMessage,"Updating failed. "+failMessage,{ color: "red" },10000);
}
$scope.displayDirtyProducts = function(){
if ($scope.dirtyProductCount() > 0) $scope.setMessage($scope.updateStatusMessage,"Changes to "+$scope.dirtyProductCount()+" products remain unsaved.",{ color: "gray" },false);
else $scope.setMessage($scope.updateStatusMessage,"",{},false);
}
$scope.dirtyProductCount = function(){
return Object.keys($scope.dirtyProducts).length;
}
});
productsApp.factory('dataFetcher', function($http,$q){
return function(dataLocation){
var deferred = $q.defer();
$http.get(dataLocation).success(function(data) {
deferred.resolve(data);
}).error(function(){
deferred.reject();
});
return deferred.promise;
};
});
function onHand(product){
var onHand = 0;
if(product.hasOwnProperty('variants') && product.variants instanceof Object){
angular.forEach(product.variants, function(variant) {
onHand = parseInt( onHand ) + parseInt( variant.on_hand > 0 ? variant.on_hand : 0 );
});
}
else{
onHand = 'error';
}
return onHand;
}
function filterSubmitProducts(productsToFilter){
var filteredProducts= [];
if (productsToFilter instanceof Object){
angular.forEach(productsToFilter, function(product){
if (product.hasOwnProperty("id")){
var filteredProduct = {};
var filteredVariants = [];
if (product.hasOwnProperty("variants")){
angular.forEach(product.variants, function(variant){
if (variant.deleted_at == null && variant.hasOwnProperty("id")){
var hasUpdateableProperty = false;
var filteredVariant = {};
filteredVariant.id = variant.id;
if (variant.hasOwnProperty("on_hand")) { filteredVariant.on_hand = variant.on_hand; hasUpdatableProperty = true; }
if (variant.hasOwnProperty("price")) { filteredVariant.price = variant.price; hasUpdatableProperty = true; }
if (hasUpdatableProperty) filteredVariants.push(filteredVariant);
}
});
}
var hasUpdatableProperty = false;
filteredProduct.id = product.id;
if (product.hasOwnProperty("name")) { filteredProduct.name = product.name; hasUpdatableProperty = true; }
if (product.hasOwnProperty("supplier")) { filteredProduct.supplier_id = product.supplier.id; hasUpdatableProperty = true; }
if (product.hasOwnProperty("price")) { filteredProduct.price = product.price; hasUpdatableProperty = true; }
if (product.hasOwnProperty("on_hand") && filteredVariants.length == 0) { filteredProduct.on_hand = product.on_hand; hasUpdatableProperty = true; } //only update if no variants present
if (product.hasOwnProperty("available_on")) { filteredProduct.available_on = product.available_on; hasUpdatableProperty = true; }
if (filteredVariants.length > 0) { filteredProduct.variants_attributes = filteredVariants; hasUpdatableProperty = true; } // Note that the name of the property changes to enable mass assignment of variants attributes with rails
if (hasUpdatableProperty) filteredProducts.push(filteredProduct);
}
});
}
return filteredProducts;
}
function addDirtyProperty(dirtyObjects, objectID, propertyName, propertyValue){
if (dirtyObjects.hasOwnProperty(objectID)){
dirtyObjects[objectID][propertyName] = propertyValue;
}
else {
dirtyObjects[objectID] = {};
dirtyObjects[objectID]["id"] = objectID;
dirtyObjects[objectID][propertyName] = propertyValue;
}
}
function removeCleanProperty(dirtyObjects, objectID, propertyName){
if (dirtyObjects.hasOwnProperty(objectID) && dirtyObjects[objectID].hasOwnProperty(propertyName)) delete dirtyObjects[objectID][propertyName];
if (dirtyObjects.hasOwnProperty(objectID) && Object.keys(dirtyObjects[objectID]).length <= 1) delete dirtyObjects[objectID];
}
function toObjectWithIDKeys(array){
var object = {};
//if (array instanceof Array){
for (i in array){
if (array[i] instanceof Object && array[i].hasOwnProperty("id")){
object[array[i].id] = angular.copy(array[i]);
if (array[i].hasOwnProperty("variants") && array[i].variants instanceof Array){
object[array[i].id].variants = toObjectWithIDKeys(array[i].variants);
}
}
}
//}
return object;
}

View File

@@ -1,3 +1,49 @@
#product_distributors_field span {
display: block;
}
tbody.odd {
tr.product { td { background-color: white; } }
tr.variant.odd { td { background-color: lighten(#eff5fc, 3); } }
tr.variant.even { td { background-color: white; } }
}
tbody.even {
tr.product { td { background-color: darken(#eff5fc, 1); } }
tr.variant.odd { td { background-color: lighten(#eff5fc, 2); } }
tr.variant.even { td { background-color: darken(#eff5fc, 1); } }
}
tbody tr.product td.actions { background-color: transparent; }
tbody tr.variant td.actions { background-color: transparent; }
tbody tr.variant td { padding: 5px 10px; }
th.left-actions, td.left-actions {
background-color: transparent !important;
border: none !important;
border-right: 1px solid #cee1f4 !important;
}
li.column-list-item {
border-radius: 3px;
padding: 2px 20px;
margin: 2px 1px;
border: 2px solid darken(#5498da, 3);
background-color: #5498da;
color: white;
font-size: 100%;
cursor: default;
}
li.column-list-item.unselected {
background-color: white;
border: 2px solid lightgray;
color: darkgray;
font-size: 100%;
}
ul.column-list{
padding: 5px 8px;
border-radius: 3px;
border: solid 1px darkgray;
list-style:none
}

View File

@@ -234,6 +234,11 @@ fieldset#product-distributor-details {
}
}
/* Checkout address page */
#checkout .alternative-available-distributors {
padding-top: 30px;
}
/* Delivery fees table on checkout page */
#delivery-fees {

View File

@@ -7,7 +7,7 @@ $c_red: #e45353 !default; /* Error red */
$layout_background_color: #FFFFFF !default;
$title_text_color: #404042 !default;
$body_text_color: #404042 !default;
$link_text_color: #006066 !default;
$link_text_color: #00ADEE !default;
$product_background_color: #FFFFFF !default;
$product_title_text_color: #404042 !default;
@@ -55,6 +55,10 @@ $ff_base: 'Ubuntu', sans-serif !default;
$button_font_size: 12px !default;
$input_box_font_size: 13px !default;
$base_font_size: 12px !default;
$border_color: lighten($body_text_color, 60);
$default_border: 1px solid $border_color;
$border_color: lighten($body_text_color, 60) !default;
$default_border: 1px solid $border_color !default;
$button_border_color: rgba(0, 138, 189, .75) !default;
$table_head_color: lighten($body_text_color, 60) !default;
@import "./variables_changes.css.scss";

View File

@@ -1,6 +1,5 @@
include Spree::ProductsHelper
class EnterprisesController < BaseController
helper Spree::AnalyticsHelper
def index
@enterprises = Enterprise.all

View File

@@ -0,0 +1,36 @@
Spree::Admin::ProductsController.class_eval do
before_filter :load_spree_api_key, :only => :bulk_edit
alias_method :location_after_save_original, :location_after_save
respond_to :json, :only => :clone
#respond_override :clone => { :json => {:success => lambda { redirect_to bulk_index_admin_products_url+"?q[id_eq]=#{@new.id}" } } }
def bulk_update
collection_hash = Hash[params[:_json].each_with_index.map { |p,i| [i,p] }]
product_set = Spree::ProductSet.new({:collection_attributes => collection_hash})
if product_set.save
redirect_to "/api/products?template=bulk_index"
else
render :nothing => true
end
end
protected
def location_after_save
if URI(request.referer).path == '/admin/products/bulk_edit'
bulk_edit_admin_products_url
else
location_after_save_original
end
end
private
def load_spree_api_key
current_user.generate_spree_api_key! unless spree_current_user.spree_api_key
@spree_api_key = spree_current_user.spree_api_key
end
end

View File

@@ -333,11 +333,11 @@ Spree::Admin::ReportsController.class_eval do
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
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]).end_of_day rescue ""
params[:q][:completed_at_lt] = Time.zone.parse(params[:q][:completed_at_lt]) rescue ""
end
params[:q][:meta_sort] ||= "completed_at.desc"

View File

@@ -0,0 +1,17 @@
module Spree
module Api
class EnterprisesController < Spree::Api::BaseController
respond_to :json
def bulk_show
@enterprise = Enterprise.find(params[:id])
respond_with(@enterprise)
end
def bulk_index
@enterprises = Enterprise.ransack(params[:q]).result
respond_with(@enterprises)
end
end
end
end

View File

@@ -19,25 +19,6 @@ Spree::OrdersController.class_eval do
end
end
def update_distribution
@order = current_order(true)
if params[:commit] == 'Choose Hub'
distributor = Enterprise.is_distributor.find params[:order][:distributor_id]
@order.set_distributor! distributor
flash[:notice] = 'Your hub has been selected.'
redirect_to request.referer
elsif params[:commit] == 'Choose Order Cycle'
order_cycle = OrderCycle.active.find params[:order][:order_cycle_id]
@order.set_order_cycle! order_cycle
flash[:notice] = 'Your order cycle has been selected.'
redirect_to request.referer
end
end
def select_distributor
distributor = Enterprise.is_distributor.find params[:id]
@@ -57,6 +38,24 @@ Spree::OrdersController.class_eval do
redirect_to root_path
end
def update_distribution
@order = current_order(true)
if params[:commit] == 'Choose Hub'
distributor = Enterprise.is_distributor.find params[:order][:distributor_id]
@order.set_distributor! distributor
flash[:notice] = 'Your hub has been selected.'
redirect_to request.referer
elsif params[:commit] == 'Choose Order Cycle'
order_cycle = OrderCycle.active.find params[:order][:order_cycle_id]
@order.set_order_cycle! order_cycle
flash[:notice] = 'Your order cycle has been selected.'
redirect_to request.referer
end
end
private

View File

@@ -0,0 +1,5 @@
module HtmlHelper
def strip_html(html)
strip_tags(html).gsub(/&nbsp;/i, ' ').gsub(/&amp;/i, '&')
end
end

View File

@@ -0,0 +1,3 @@
Spree::OrderMailer.class_eval do
helper HtmlHelper
end

View File

@@ -48,7 +48,7 @@ class Enterprise < ActiveRecord::Base
def to_param
"#{id}-#{name.parameterize}"
end
def distributed_variants
Spree::Variant.joins(:product).merge(Spree::Product.in_distributor(self)).select('spree_variants.*')
end
@@ -57,6 +57,9 @@ class Enterprise < ActiveRecord::Base
Spree::Variant.joins(:product).merge(Spree::Product.in_product_distribution_by(self)).select('spree_variants.*')
end
def available_variants
Spree::Variant.joins(:product => :product_distributions).where('product_distributions.distributor_id=?', self.id)
end
private

View File

@@ -0,0 +1,19 @@
module Spree
class Gateway::Migs < Gateway
preference :login, :string
preference :password, :string
attr_accessible :preferred_login, :preferred_password
def provider_class
ActiveMerchant::Billing::MigsGateway
end
def options_with_test_preference
options_without_test_preference.merge(:test => self.preferred_test_mode)
end
alias_method_chain :options, :test_preference
end
end

View File

@@ -5,11 +5,16 @@ Spree::LineItem.class_eval do
before_create :set_itemwise_shipping_method
def itemwise_shipping_cost
order = OpenStruct.new :line_items => [self]
shipping_method.compute_amount(order)
end
def update_itemwise_shipping_method_without_callbacks!(distributor)
update_column(:shipping_method_id, self.product.shipping_method_for_distributor(distributor).id)
end
private

View File

@@ -8,9 +8,11 @@ Spree::Order.class_eval do
validate :products_available_from_new_distribution, :if => lambda { distributor_id_changed? || order_cycle_id_changed? }
attr_accessible :order_cycle_id, :distributor_id
before_validation :shipping_address_from_distributor
before_save :update_line_item_shipping_methods
after_create :set_default_shipping_method
def products_available_from_new_distribution
# Check that the line_items in the current order are available from a newly selected distribution
errors.add(:base, "Distributor or order cycle cannot supply the products in your cart") unless DistributionChangeValidator.new(self).can_change_to_distribution?(distributor, order_cycle)
@@ -22,6 +24,12 @@ Spree::Order.class_eval do
save!
end
def empty!
line_items.destroy_all
adjustments.destroy_all
set_default_shipping_method
end
def set_distributor!(distributor)
self.distributor = distributor
self.order_cycle = nil unless self.order_cycle.andand.has_distributor? distributor
@@ -44,7 +52,7 @@ Spree::Order.class_eval do
line_item.assign_attributes(attributes)
line_item.save!
end
def line_item_variants
line_items.map { |li| li.variant }
end
@@ -80,4 +88,11 @@ Spree::Order.class_eval do
end
end
end
def update_line_item_shipping_methods
if %w(cart address delivery resumed).include? state
self.line_items.each { |li| li.update_itemwise_shipping_method_without_callbacks!(distributor) }
self.update!
end
end
end

View File

@@ -0,0 +1,39 @@
class Spree::ProductSet < ModelSet
def initialize(attributes={})
super(Spree::Product, [], proc { |attrs| attrs[:product_id].blank? }, attributes)
end
# A separate method of updating products was required due to an issue with the way Rails' assign_attributes and updates_attributes behave when delegated attributes of a nested
# object are updated via the parent object (ie. price of variants). Updating such attributes by themselves did not work using:
# product.update_attributes( { variants_attributes: [ { id: y, price: xx.x } ] } )
# and so an explicit call to update attributes on each individual variant was required. ie:
# variant.update_attributes( { price: xx.x } )
def update_attributes(attributes)
e = @collection.detect { |e| e.id.to_s == attributes[:id].to_s && !e.id.nil? }
if e.nil?
@klass.new(attributes).save unless @reject_if.andand.call(attributes)
else
e.update_attributes(attributes.except(:id,:variants_attributes)) and (attributes[:variants_attributes] ? update_variants_attributes(e,attributes[:variants_attributes]) : true )
end
end
def update_variants_attributes(product,variants_attributes)
variants_attributes.each do |attributes|
e = product.variants.detect { |e| e.id.to_s == attributes[:id].to_s && !e.id.nil? }
if !e.nil?
e.update_attributes(attributes.except(:id))
end
end
end
def collection_attributes=(attributes)
@collection = Spree::Product.where( :id => attributes.each_value.map{ |p| p[:id] } )
@collection_hash = attributes
end
def save
@collection_hash.each_value.all? do |product_attributes|
update_attributes(product_attributes)
end
end
end

View File

@@ -0,0 +1,4 @@
Deface::Override.new(:virtual_path => "spree/admin/shared/_product_sub_menu",
:name => "add_bulk_edit_tab_to_products_admin_sub_menu",
:insert_bottom => "[data-hook='admin_product_sub_tabs']",
:text => "<%= tab :bulk_product_edit, :url => bulk_edit_admin_products_path %>")

View File

@@ -1,5 +0,0 @@
Deface::Override.new(:virtual_path => "spree/layouts/spree_application",
:insert_bottom => "[data-hook='inside_head']",
:partial => "layouts/feedback_script",
:name => "add_feedback_script",
:original => '429dfd9824ee588f51fb1b69013933424f149592')

View File

@@ -1,5 +1,5 @@
Deface::Override.new(:virtual_path => "spree/layouts/admin",
:name => "add_order_cycles_admin_tab",
:insert_bottom => "[data-hook='admin_tabs'], #admin_tabs[data-hook]",
:text => "<%= tab 'Order Cycles', :url => main_app.admin_order_cycles_path %>",
:text => "<%= tab :order_cycles, :url => main_app.admin_order_cycles_path %>",
:original => 'd4e321201ecb543e92192a031c8896a45dde3576')

View File

@@ -0,0 +1,5 @@
Deface::Override.new(:virtual_path => "spree/payments/_payment",
:replace => "code[erb-loud]:contains('content_tag(:span, payment.payment_method.name)')",
:text => "<%= content_tag( :span, ( payment.payment_method.description || payment.payment_method.name ).html_safe ) %>",
:name => "replace_payment_name_with_description",
:original => 'dff62efcadc0f9e6513b0f81a51ebbda035f78f6')

View File

@@ -6,7 +6,7 @@
%tr
%th Description:
%td= @enterprise.description
%tr
%tr{"data-hook" => "long_description"}
%th Extended Description:
%td= @enterprise.long_description.andand.html_safe
%tr

View File

@@ -1,23 +1,24 @@
%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
.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

@@ -0,0 +1,79 @@
- content_for :page_title do
= "Bulk Edit Products"
- content_for :page_actions do
%div{ :class => "toolbar", 'data-hook' => "toolbar" }
%ul{ :class => "actions header-action-links inline-menu" }
%li#new_product_link
= button_link_to t(:new_product), new_object_url, { :remote => true, :icon => 'icon-plus', :id => 'admin_new_product' }
= render :partial => 'spree/admin/shared/product_sub_menu'
%div#new_product(data-hook)
%div{ 'ng-app' => 'bulk_product_update', 'ng-controller' => 'AdminBulkProductsCtrl', 'ng-init' => "initialise('#{@spree_api_key}');" }
%div{ 'ng-show' => '!spree_api_key_ok' }
{{ api_error_msg }}
%div
%div.options
Filter Results:
%input.search{ 'ng-model' => 'query', :type => 'text', 'placeholder' => 'Search Value' }
%input{ :type => 'button', :value => 'Toggle Columns', 'ng-toggle-column-list' => true }
%div{ :style => 'display: none;' }
%ul.column-list{ style: 'border: 1px solid darkgray; background-color: white;' }
%li.column-list-item{ 'ng-toggle-column' => 'column', 'ng-repeat' => 'column in columns' }
{{ column.name }}
%br.clear
%br.clear
%table.index#listing_products
%thead
%tr
%th.left-actions
%th{ 'ng-show' => 'columns.name.visible' } Name
%th{ 'ng-show' => 'columns.supplier.visible' } Supplier
%th{ 'ng-show' => 'columns.price.visible' } Price
%th{ 'ng-show' => 'columns.on_hand.visible' } On Hand
%th{ 'ng-show' => 'columns.available_on.visible' } Av. On
%th.actions
%tbody{ 'ng-repeat' => 'product in products | filter:query', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" }
%tr.product
%td.left-actions
%a{ 'ng-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' }
%td{ 'ng-show' => 'columns.name.visible' }
%input{ 'ng-model' => "product.name", :name => 'product_name', 'ng-track-product' => 'name', :type => 'text' }
%td{ 'ng-show' => 'columns.supplier.visible' }
%select.select2{ 'ng-model' => 'product.supplier', :name => 'supplier', 'ng-track-product' => 'supplier', 'ng-options' => 's.name for s in suppliers' }
%td{ 'ng-show' => 'columns.price.visible' }
%input{ 'ng-model' => 'product.price', 'ng-decimal' => :true, :name => 'price', 'ng-track-product' => 'price', :type => 'text' }
%td{ 'ng-show' => 'columns.on_hand.visible' }
%span{ 'ng-bind' => 'product.on_hand', :name => 'on_hand', 'ng-show' => 'hasVariants(product)' }
%input.field{ 'ng-model' => 'product.on_hand', :name => 'on_hand', 'ng-track-product' => 'on_hand', 'ng-show' => '!hasVariants(product)', :type => 'number' }
%td{ 'ng-show' => 'columns.available_on.visible' }
%input{ 'ng-model' => 'product.available_on', :name => 'available_on', 'ng-track-product' => 'available_on', 'datetimepicker' => 'product.available_on', type: "text" }
%td.actions
%a{ 'ng-click' => 'cloneProduct(product)', :class => "clone-product icon-copy no-text" }
%td.actions
%a{ 'ng-click' => 'editWarn(product)', :class => "edit-product icon-edit no-text" }
%td.actions
%a{ 'ng-click' => 'deleteProduct(product)', :class => "delete-product icon-trash no-text" }
%tr.variant{ 'ng-repeat' => 'variant in product.variants', 'ng-show' => 'displayProperties[product.id].showVariants', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" }
%td.left-actions
%a{ :class => "variant-item icon-caret-right" }
%td{ 'ng-show' => 'columns.name.visible' }
{{ variant.options_text }}
%td{ 'ng-show' => 'columns.supplier.visible' }
%td{ 'ng-show' => 'columns.price.visible' }
%input{ 'ng-model' => 'variant.price', 'ng-decimal' => :true, :name => 'variant_price', 'ng-track-variant' => 'price', :type => 'text' }
%td{ 'ng-show' => 'columns.on_hand.visible' }
%input.field{ 'ng-model' => 'variant.on_hand', 'ng-change' => 'updateOnHand(product)', :name => 'variant_on_hand', 'ng-track-variant' => 'on_hand', :type => 'number' }
%td{ 'ng-show' => 'columns.available_on.visible' }
%td.actions
%td.actions
%a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text" }
%td.actions
%a{ 'ng-click' => 'deleteVariant(product,variant)', :class => "delete-variant icon-trash no-text" }
%input{ :type => 'button', :value => 'Update', 'ng-click' => 'prepareProductsForSubmit()'}
%span{ id: "update-status-message", 'ng-style' => 'updateStatusMessage.style' }
{{ updateStatusMessage.text }}

View File

@@ -3,11 +3,11 @@
%br
.date-range-filter
%div{"class" => "left sub-field"}
= f.text_field :completed_at_gt, :class => 'datepicker'
= f.text_field :completed_at_gt, :class => 'datetimepicker'
%br
= label_tag nil, t(:start), :class => 'sub'
%div{"class" => "right sub-field"}
= f.text_field :completed_at_lt, :class => 'datepicker'
= f.text_field :completed_at_lt, :class => 'datetimepicker'
%br
= label_tag nil, t(:stop)
%br

View File

@@ -0,0 +1,2 @@
collection @enterprises
extends "spree/api/enterprises/bulk_show"

View File

@@ -0,0 +1,3 @@
object @enterprise
attributes :id, :name

View File

@@ -0,0 +1,2 @@
collection @products.order('id ASC')
extends "spree/api/products/bulk_show"

View File

@@ -0,0 +1,11 @@
object @product
attributes :id, :name, :price, :on_hand
node( :available_on ) { |p| p.available_on.strftime("%F %T") }
node( :permalink_live ) { |p| p.permalink }
node( :supplier ) do |p|
partial 'spree/api/enterprises/bulk_show', :object => p.supplier
end
node( :variants ) do |p|
partial 'spree/api/variants/bulk_index', :object => p.variants.order('id ASC')
end

View File

@@ -0,0 +1,2 @@
object false
node(:success) { "Use of API Authorised" }

View File

@@ -0,0 +1,2 @@
collection @variants
extends "spree/api/variants/bulk_show"

View File

@@ -0,0 +1,3 @@
object @variant
attributes :id, :options_text, :price, :on_hand

View File

@@ -1,13 +1,13 @@
<% if @order.state == 'address' %>
<div class="columns omega four" style="padding-top: 30px">
<% if alternative_available_distributors(@order) %>
<div class="columns omega four alternative-available-distributors">
<% unless alternative_available_distributors(@order).empty? %>
<%= form_for(@order) do |f| %>
<%= f.label :distributor_label, "Alternative distributors for this order:" %>
<%= f.select :distributor_id, options_for_select( enterprises_options(alternative_available_distributors(@order)) ) %>
<%= f.submit "Change Distributor" %>
<% end %>
<%= f.select :distributor_id, options_for_select( enterprises_options(alternative_available_distributors(@order)) ) %>
<%= f.submit "Change Distributor" %>
<% end %>
<% else %>
No alternative distributors available.
No alternative distributors available.
<% end %>
</div>
<% end %>

View File

@@ -7,20 +7,20 @@ Order Summary
============================================================
Order for: <%= @order.bill_address.full_name %>
<% @order.line_items.each do |item| %>
<%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= number_to_currency item.price %> = <%= number_to_currency(item.price * item.quantity) %>
<%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= item.single_money %> = <%= item.display_amount %>
<% end %>
============================================================
Subtotal: <%= number_to_currency @order.item_total %>
Subtotal: <%= @order.display_item_total %>
<% @order.adjustments.each do |adjustment| %>
<%= raw(adjustment.label) %> <%= number_to_currency(adjustment.amount) %>
<%= raw(adjustment.label) %> <%= adjustment.display_amount %>
<% end %>
Order Total: <%= number_to_currency(@order.total) %>
Order Total: <%= @order.display_total %>
<% if @order.payment_method.name.include? "EFT" %>
<% if @order.payments.first.payment_method.name.include? "EFT" %>
============================================================
Payment Details
============================================================
<%= @order.payment_method.description.html_safe %>
<%= @order.payments.first.payment_method.description.html_safe %>
<% end %>
============================================================

View File

@@ -1,2 +1,2 @@
%div Distributor for your order:
.distributor Distributor for your order:
= select_tag "distributor_id", options_from_collection_for_select([Enterprise.new]+distributor_collection, "id", "name", current_distributor.andand.id)

View File

@@ -1,49 +1,37 @@
<div class="row steps-data">
<div class="columns alpha six">
<h6><%= "Customer Details" %> <%= link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %></h6>
<div class="address">
Name: <%= order.bill_address.full_name %><br />
Address: <%= order.bill_address.address1 + ", " + order.bill_address.city %>
</div>
</div>
<div class="columns alpha six">
<h6><%= "Distributor Details" %> <%# link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %></h6>
<div class="address">
Distributor: <%= order.distributor.name %><br />
Address: <%= order.distributor.address.address1 + ", " + order.distributor.address.city %>
<% if order.has_step?("address") %>
<div class="columns alpha six">
<h6><%= "Customer Details" %> <%= link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %></h6>
<div class="address">
<%= render 'enterprises/distributor_details', :distributor => order.distributor %>
</div>
</div>
</div>
<!-- <div class="columns alpha four">
<h6><%= t(:shipping_method) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:delivery) unless @order.completed? %></h6>
<div class="delivery">
<%= order.shipping_method.name %>
<div class="columns alpha two">
</div>
<div class="columns alpha six">
<h6><%= t(:billing_address) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:address) unless @order.completed? %></h6>
<div class="address">
Distributor: <%= order.distributor.name %><br />
Address: <%= order.distributor.address.address1 + ", " + order.distributor.address.city %>
</div>
</div>
</div> -->
<% end %>
</div>
<div class="row steps-data">
<div class="columns alpha six" <% if order.payment_method.name.include? "EFT" %>id="eft-payment-alert"<% end %>>
<div class="columns alpha six">
<h6><%= t(:payment_information) %> <%= link_to "(#{t(:edit)})", checkout_state_path(:payment) unless @order.completed? %></h6>
<div class="payment-info">
<% if order.payment_method.name.include? "PayPal" %>
<% if order.payments.any? { |payment| payment.payment_method.name.include? "PayPal" } %>
<div class="flash notice">Your payment via PayPal has been processed successfully.</div>
<% elsif order.payment_method.name.include? "EFT" %>
<span><%= order.payment_method.description.html_safe %></span>
<% elsif order.credit_cards.empty? == false %>
<span class="cc-type">
<%= image_tag "credit_cards/icons/#{order.credit_cards.first.cc_type}.png" %>
<%= t(:ending_in)%> <%= order.credit_cards.first.last_digits %>
</span>
<br />
<span class="full-name">
<%= order.credit_cards.first.first_name %>
<%= order.credit_cards.first.last_name %>
</span>
<% elsif order.payment_method.type == "Spree::PaymentMethod::Check" %>
<span><%= order.payment_method.description %></span>
<% end %>
<% end %>
<%= render order.payments.valid %>
</div>
</div>
</div>
</div>

View File

@@ -8,6 +8,7 @@ files = [
'app/assets/javascripts/shared/angular-*.js',
'app/assets/javascripts/admin/order_cycle.js.erb.coffee',
'app/assets/javascripts/admin/bulk_product_update.js',
'spec/javascripts/unit/**/*.js*'
];

View File

@@ -37,6 +37,10 @@ module Openfoodweb
OpenFoodWeb::Calculator::Weight]
end
# Register Spree payment methods
initializer "spree.gateway.payment_methods", :after => "spree.register.payment_methods" do |app|
app.config.spree.payment_methods << Spree::Gateway::Migs
end
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
@@ -72,7 +76,7 @@ module Openfoodweb
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
config.assets.initialize_on_precompile = false
config.assets.initialize_on_precompile = true
config.assets.precompile += ['store/all.css', 'store/all.js', 'admin/all.css', 'admin/*.js', 'admin/**/*.js', 'comfortable_mexican_sofa/*']
end
end

View File

@@ -3,6 +3,7 @@ development:
encoding: unicode
database: open_food_web_dev
pool: 5
host: localhost
username: ofw
password: f00d
@@ -11,6 +12,7 @@ test:
encoding: unicode
database: open_food_web_test
pool: 5
host: localhost
username: ofw
password: f00d

View File

@@ -26,7 +26,7 @@ Openfoodweb::Application.configure do
config.assets.compress = false
# Expands the lines which load the assets
config.assets.debug = true
config.assets.debug = false
# Show emails using Letter Opener
config.action_mailer.delivery_method = :letter_opener

View File

@@ -3,43 +3,43 @@
ComfortableMexicanSofa.configure do |config|
# Title of the admin area
# config.cms_title = 'ComfortableMexicanSofa CMS Engine'
# Module responsible for authentication. You can replace it with your own.
# It simply needs to have #authenticate method. See http_auth.rb for reference.
config.admin_auth = 'CmsSpreeAuth'
# Module responsible for public authentication. Similar to the above. You also
# will have access to @cms_site, @cms_layout, @cms_page so you can use them in
# your logic. Default module doesn't do anything.
# config.public_auth = 'ComfortableMexicanSofa::DummyAuth'
# Default url to access admin area is http://yourhost/cms-admin/
# Default url to access admin area is http://yourhost/cms-admin/
# You can change 'cms-admin' to 'admin', for example. To disable admin area
# entirely set this to '' or nil
# config.admin_route_prefix = 'cms-admin'
# When arriving at /cms-admin you may chose to redirect to arbirtary path,
# for example '/cms-admin/users'
# config.admin_route_redirect = ''
# Normally we include default routes from https://github.com/comfy/comfortable-mexican-sofa/blob/master/config/routes.rb
# If you want to include the routes manually set this to false
# config.use_default_routes = true
# /sitemap.xml that is used by search engines for indexing. It's enabled by
# default, but you may turn it off.
# config.enable_sitemap = true
# File uploads use Paperclip and can support filesystem or s3 uploads. Override
# the upload method and appropriate settings based on Paperclip. For S3 see:
# http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/S3, and for
# http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/S3, and for
# filesystem see: http://rdoc.info/gems/paperclip/2.3.8/Paperclip/Storage/Filesystem
config.upload_file_options = {
:storage => 's3',
:s3_credentials => {
:bucket => ENV['S3_BUCKET'],
:access_key_id => ENV['S3_KEY'],
:secret_access_key => ENV['S3_SECRET']
:bucket => Spree::Config[:s3_bucket],
:access_key_id => Spree::Config[:s3_access_key],
:secret_access_key => Spree::Config[:s3_secret]
}
}
@@ -47,11 +47,11 @@ ComfortableMexicanSofa.configure do |config|
# request (if necessary). Please note that database entries are destroyed if there's
# no corresponding file. Fixtures are disabled by default.
# config.enable_fixtures = false
# Path where fixtures can be located.
# config.fixtures_path = File.expand_path('db/cms_fixtures', Rails.root)
# Importing fixtures into Database
# Importing fixtures into Database
# To load fixtures into the database just run this rake task:
# local: $ rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=localhost
# Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:import FROM=example.local TO=yourapp.herokuapp.com
@@ -62,53 +62,53 @@ ComfortableMexicanSofa.configure do |config|
# local: $ rake comfortable_mexican_sofa:fixtures:export FROM=localhost TO=example.local
# Heroku: $ heroku run rake comfortable_mexican_sofa:fixtures:export FROM=yourapp.herokuapp.com TO=example.local
# This will create example.local folder and dump all content from example.com Site.
# Content for Layouts, Pages and Snippets has a revision history. You can revert
# a previous version using this system. You can control how many revisions per
# object you want to keep. Set it to 0 if you wish to turn this feature off.
# config.revisions_limit = 25
# Locale definitions. If you want to define your own locale merge
# {:locale => 'Locale Title'} with this.
# config.locales = {:en => 'English', :es => 'Español'}
# Admin interface will respect the locale of the site being managed. However you can
# force it to English by setting this to `:en`
# config.admin_locale = nil
# If you want to keep your CMS tables in a location other than the default database
# add a database_config. For example, setting it to 'cms' will look for a cms_#{Rails.env}
# definition in your database.yml file
# config.database_config = nil
# A class that is included as a sweeper to admin base controller if it's set
# config.admin_cache_sweeper = nil
# By default you cannot have irb code inside your layouts/pages/snippets.
# Generally this is to prevent putting something like this:
# <% User.delete_all %> but if you really want to allow it...
# config.allow_irb = false
# Whitelist of all helper methods that can be used via {{cms:helper}} tag. By default
# all helpers are allowed except `eval`, `send`, `call` and few others. Empty array
# will prevent rendering of all helpers.
# config.allowed_helpers = nil
# Whitelist of partials paths that can be used via {{cms:partial}} tag. All partials
# are accessible by default. Empty array will prevent rendering of all partials.
# config.allowed_partials = nil
# Site aliases, if you want to have aliases for your site. Good for harmonizing
# Site aliases, if you want to have aliases for your site. Good for harmonizing
# production env with dev/testing envs.
# e.g. config.site_aliases = {'host.com' => 'host.inv', 'host_a.com' => ['host.lvh.me', 'host.dev']}
# Default is nil (not used)
# config.hostname_aliases = nil
end
module CmsSpreeAuth
def authenticate
unless current_user && current_user.has_spree_role?('admin')
unless spree_current_user && spree_current_user.has_spree_role?('admin')
redirect_to spree.login_path
end
end

View File

@@ -0,0 +1,6 @@
DB2Fog.config = {
:aws_access_key_id => Spree::Config[:s3_access_key],
:aws_secret_access_key => Spree::Config[:s3_secret],
:directory => Spree::Config[:s3_bucket],
:provider => 'AWS'
}

View File

@@ -11,10 +11,6 @@ require 'spree/product_filters'
require 'open_food_web/searcher'
Spree.config do |config|
config.site_name = "Open Food Web"
config.logo = 'logo.jpg'
config.admin_interface_logo = 'logo.jpg'
config.shipping_instructions = true
config.checkout_zone = 'Australia'
config.address_requires_state = true

View File

@@ -2,4 +2,10 @@
# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
en:
hello: "Hello world"
spree:
devise:
failure:
invalid: |
Invalid email or password.
Were you a guest last time? Perhaps you need to create an account or reset your password.

255
config/newrelic.yml Normal file
View File

@@ -0,0 +1,255 @@
# Here are the settings that are common to all environments
common: &default_settings
# ============================== LICENSE KEY ===============================
# You must specify the license key associated with your New Relic
# account. This key binds your Agent's data to your account in the
# New Relic service.
license_key: '<%= ENV["NEW_RELIC_LICENSE_KEY"] %>'
# Agent Enabled (Rails Only)
# Use this setting to force the agent to run or not run.
# Default is 'auto' which means the agent will install and run only
# if a valid dispatcher such as Mongrel is running. This prevents
# it from running with Rake or the console. Set to false to
# completely turn the agent off regardless of the other settings.
# Valid values are true, false and auto.
#
# agent_enabled: auto
# Application Name Set this to be the name of your application as
# you'd like it show up in New Relic. The service will then auto-map
# instances of your application into an "application" on your
# dashboard page. If you want to map this instance into multiple
# apps, like "AJAX Requests" and "All UI" then specify a semicolon
# separated list of up to three distinct names, or a yaml list.
# Defaults to the capitalized RAILS_ENV or RACK_ENV (i.e.,
# Production, Staging, etc)
#
# Example:
#
# app_name:
# - Ajax Service
# - All Services
#
app_name: <%= ENV["NEW_RELIC_APP_NAME"] %>
# When "true", the agent collects performance data about your
# application and reports this data to the New Relic service at
# newrelic.com. This global switch is normally overridden for each
# environment below. (formerly called 'enabled')
monitor_mode: true
# Developer mode should be off in every environment but
# development as it has very high overhead in memory.
developer_mode: false
# The newrelic agent generates its own log file to keep its logging
# information separate from that of your application. Specify its
# log level here.
log_level: info
# Optionally set the path to the log file This is expanded from the
# root directory (may be relative or absolute, e.g. 'log/' or
# '/var/log/') The agent will attempt to create this directory if it
# does not exist.
# log_file_path: 'log'
# Optionally set the name of the log file, defaults to 'newrelic_agent.log'
# log_file_name: 'newrelic_agent.log'
# The newrelic agent communicates with the service via http by
# default. If you want to communicate via https to increase
# security, then turn on SSL by setting this value to true. Note,
# this will result in increased CPU overhead to perform the
# encryption involved in SSL communication, but this work is done
# asynchronously to the threads that process your application code,
# so it should not impact response times.
ssl: false
# EXPERIMENTAL: enable verification of the SSL certificate sent by
# the server. This setting has no effect unless SSL is enabled
# above. This may block your application. Only enable it if the data
# you send us needs end-to-end verified certificates.
#
# This means we cannot cache the DNS lookup, so each request to the
# service will perform a lookup. It also means that we cannot
# use a non-blocking lookup, so in a worst case, if you have DNS
# problems, your app may block indefinitely.
# verify_certificate: true
# Set your application's Apdex threshold value with the 'apdex_t'
# setting, in seconds. The apdex_t value determines the buckets used
# to compute your overall Apdex score.
# Requests that take less than apdex_t seconds to process will be
# classified as Satisfying transactions; more than apdex_t seconds
# as Tolerating transactions; and more than four times the apdex_t
# value as Frustrating transactions.
# For more about the Apdex standard, see
# http://newrelic.com/docs/general/apdex
apdex_t: 0.5
#============================== Browser Monitoring ===============================
# New Relic Real User Monitoring gives you insight into the performance real users are
# experiencing with your website. This is accomplished by measuring the time it takes for
# your users' browsers to download and render your web pages by injecting a small amount
# of JavaScript code into the header and footer of each page.
browser_monitoring:
# By default the agent automatically injects the monitoring JavaScript
# into web pages. Set this attribute to false to turn off this behavior.
auto_instrument: true
# Proxy settings for connecting to the service.
#
# If a proxy is used, the host setting is required. Other settings
# are optional. Default port is 8080.
#
# proxy_host: hostname
# proxy_port: 8080
# proxy_user:
# proxy_pass:
# Tells transaction tracer and error collector (when enabled)
# whether or not to capture HTTP params. When true, frameworks can
# exclude HTTP parameters from being captured.
# Rails: the RoR filter_parameter_logging excludes parameters
# Java: create a config setting called "ignored_params" and set it to
# a comma separated list of HTTP parameter names.
# ex: ignored_params: credit_card, ssn, password
capture_params: false
# Transaction tracer captures deep information about slow
# transactions and sends this to the service once a
# minute. Included in the transaction is the exact call sequence of
# the transactions including any SQL statements issued.
transaction_tracer:
# Transaction tracer is enabled by default. Set this to false to
# turn it off. This feature is only available at the Professional
# and above product levels.
enabled: true
# Threshold in seconds for when to collect a transaction
# trace. When the response time of a controller action exceeds
# this threshold, a transaction trace will be recorded and sent to
# the service. Valid values are any float value, or (default)
# "apdex_f", which will use the threshold for an dissatisfying
# Apdex controller action - four times the Apdex T value.
transaction_threshold: apdex_f
# When transaction tracer is on, SQL statements can optionally be
# recorded. The recorder has three modes, "off" which sends no
# SQL, "raw" which sends the SQL statement in its original form,
# and "obfuscated", which strips out numeric and string literals
record_sql: obfuscated
# Threshold in seconds for when to collect stack trace for a SQL
# call. In other words, when SQL statements exceed this threshold,
# then capture and send the current stack trace. This is
# helpful for pinpointing where long SQL calls originate from
stack_trace_threshold: 0.500
# Determines whether the agent will capture query plans for slow
# SQL queries. Only supported in mysql and postgres. Should be
# set to false when using other adapters.
# explain_enabled: true
# Threshold for query execution time below which query plans will not
# not be captured. Relevant only when `explain_enabled` is true.
# explain_threshold: 0.5
# Error collector captures information about uncaught exceptions and
# sends them to the service for viewing
error_collector:
# Error collector is enabled by default. Set this to false to turn
# it off. This feature is only available at the Professional and above
# product levels
enabled: true
# Rails Only - tells error collector whether or not to capture a
# source snippet around the place of the error when errors are View
# related.
capture_source: true
# To stop specific errors from reporting to New Relic, set this property
# to comma separated values. Default is to ignore routing errors
# which are how 404's get triggered.
#
ignore_errors: ActionController::RoutingError
# (Advanced) Uncomment this to ensure the cpu and memory samplers
# won't run. Useful when you are using the agent to monitor an
# external resource
# disable_samplers: true
# If you aren't interested in visibility in these areas, you can
# disable the instrumentation to reduce overhead.
#
# disable_view_instrumentation: true
# disable_activerecord_instrumentation: true
# disable_memcache_instrumentation: true
# disable_dj: true
# If you're interested in capturing memcache keys as though they
# were SQL uncomment this flag. Note that this does increase
# overhead slightly on every memcached call, and can have security
# implications if your memcached keys are sensitive
# capture_memcache_keys: true
# Certain types of instrumentation such as GC stats will not work if
# you are running multi-threaded. Please let us know.
# multi_threaded = false
# Application Environments
# ------------------------------------------
# Environment specific settings are in this section.
# For Rails applications, RAILS_ENV is used to determine the environment
# For Java applications, pass -Dnewrelic.environment <environment> to set
# the environment
# NOTE if your application has other named environments, you should
# provide newrelic configuration settings for these environments here.
development:
<<: *default_settings
# Turn off communication to New Relic service in development mode (also
# 'enabled').
# NOTE: for initial evaluation purposes, you may want to temporarily
# turn the agent on in development mode.
monitor_mode: false
# Rails Only - when running in Developer Mode, the New Relic Agent will
# present performance information on the last 100 transactions you have
# executed since starting the mongrel.
# NOTE: There is substantial overhead when running in developer mode.
# Do not use for production or load testing.
developer_mode: true
# Enable textmate links
# textmate: true
test:
<<: *default_settings
# It almost never makes sense to turn on the agent when running
# unit, functional or integration tests or the like.
monitor_mode: false
# Turn on the agent in production for 24x7 monitoring. NewRelic
# testing shows an average performance impact of < 5 ms per
# transaction, you you can leave this on all the time without
# incurring any user-visible performance degradation.
production:
<<: *default_settings
monitor_mode: true
# Many applications have a staging environment which behaves
# identically to production. Support for that environment is provided
# here. By default, the staging environment has the agent turned on.
staging:
<<: *default_settings
monitor_mode: true
app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> (Staging)

View File

@@ -31,6 +31,17 @@ Spree::Core::Engine.routes.prepend do
match '/admin/reports/bulk_coop' => 'admin/reports#bulk_coop', :as => "bulk_coop_admin_reports", :via => [:get, :post]
match '/admin/reports/payments' => 'admin/reports#payments', :as => "payments_admin_reports", :via => [:get, :post]
match '/admin/reports/order_cycles' => 'admin/reports#order_cycles', :as => "order_cycles_admin_reports", :via => [:get, :post]
match '/admin/products/bulk_edit' => 'admin/products#bulk_edit', :as => "bulk_edit_admin_products"
match '/api/users/authorise_api' => 'api/users#authorise_api', :via => :get, :defaults => { :format => 'json' }
match '/api/enterprises' => 'api/enterprises#bulk_index', :via => :get, :defaults => { :format => 'json' }
match '/api/enterprises/:id' => 'api/enterprises#bulk_show', :via => :get, :defaults => { :format => 'json' }
namespace :admin do
resources :products do
post :bulk_update, :on => :collection, :as => :bulk_update
end
end
resources :orders do
get :select_distributor, :on => :member

View File

@@ -1,2 +1,27 @@
preload_app true # https://newrelic.com/docs/ruby/no-data-with-unicorn
worker_processes 4 # amount of unicorn workers to spin up
timeout 30 # restarts workers that hang for 30 seconds
timeout 60 # restarts workers that hang for 30 seconds
# https://devcenter.heroku.com/articles/forked-pg-connections
before_fork do |server, worker|
Signal.trap 'TERM' do
puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
Process.kill 'QUIT', Process.pid
end
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
end
after_fork do |server, worker|
Signal.trap 'TERM' do
puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to sent QUIT'
end
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
end

View File

@@ -0,0 +1,6 @@
# This migration comes from enterprises_distributor_info_rich_text_feature (originally 20130426022945)
class AddDistributorInfoToEnterprises < ActiveRecord::Migration
def change
add_column :enterprises, :distributor_info, :text
end
end

View File

@@ -0,0 +1,10 @@
# This migration comes from spree (originally 20130207155350)
class AddOrderIdIndexToPayments < ActiveRecord::Migration
def self.up
add_index :spree_payments, :order_id
end
def self.down
remove_index :spree_payments, :order_id
end
end

View File

@@ -0,0 +1,6 @@
# This migration comes from spree (originally 20130208032954)
class AddPrimaryToSpreeProductsTaxons < ActiveRecord::Migration
def change
add_column :spree_products_taxons, :id, :primary_key
end
end

View File

@@ -0,0 +1,6 @@
# This migration comes from spree (originally 20130222032153)
class AddOrderIdIndexToShipments < ActiveRecord::Migration
def change
add_index :spree_shipments, :order_id
end
end

View File

@@ -0,0 +1,6 @@
# This migration comes from spree (originally 20130226032817)
class ChangeMetaDescriptionOnSpreeProductsToText < ActiveRecord::Migration
def change
change_column :spree_products, :meta_description, :text, :limit => nil
end
end

View File

@@ -0,0 +1,6 @@
# This migration comes from spree (originally 20130226054936)
class AddVariantIdIndexToSpreePrices < ActiveRecord::Migration
def change
add_index :spree_prices, :variant_id
end
end

View File

@@ -0,0 +1,9 @@
# This migration comes from spree (originally 20130319062004)
class ChangeOrdersTotalPrecision < ActiveRecord::Migration
def change
change_column :spree_orders, :item_total, :decimal, :precision => 10, :scale => 2, :default => 0.0, :null => false
change_column :spree_orders, :total, :decimal, :precision => 10, :scale => 2, :default => 0.0, :null => false
change_column :spree_orders, :adjustment_total, :decimal, :precision => 10, :scale => 2, :default => 0.0, :null => false
change_column :spree_orders, :payment_total, :decimal, :precision => 10, :scale => 2, :default => 0.0
end
end

View File

@@ -0,0 +1,8 @@
# This migration comes from spree (originally 20130319063911)
class ChangeSpreePaymentsAmountPrecision < ActiveRecord::Migration
def change
change_column :spree_payments, :amount, :decimal, :precision => 10, :scale => 2, :default => 0.0, :null => false
end
end

View File

@@ -0,0 +1,8 @@
# This migration comes from spree (originally 20130319064308)
class ChangeSpreeReturnAuthorizationAmountPrecision < ActiveRecord::Migration
def change
change_column :spree_return_authorizations, :amount, :decimal, :precision => 10, :scale => 2, :default => 0.0, :null => false
end
end

View File

@@ -0,0 +1,8 @@
# This migration comes from spree (originally 20130319082943)
class ChangeAdjustmentsAmountPrecision < ActiveRecord::Migration
def change
change_column :spree_adjustments, :amount, :decimal, :precision => 10, :scale => 2
end
end

View File

@@ -0,0 +1,10 @@
# This migration comes from spree (originally 20130328195253)
class AddSeoMetasToTaxons < ActiveRecord::Migration
def change
change_table :spree_taxons do |t|
t.string :meta_title
t.string :meta_description
t.string :meta_keywords
end
end
end

View File

@@ -0,0 +1,7 @@
# This migration comes from spree (originally 20130626232741)
class AddCvvResultCodeAndCvvResultMessageToSpreePayments < ActiveRecord::Migration
def change
add_column :spree_payments, :cvv_response_code, :string
add_column :spree_payments, :cvv_response_message, :string
end
end

View File

@@ -0,0 +1,6 @@
# This migration comes from spree (originally 20130628021056)
class AddUniqueIndexToPermalinkOnSpreeProducts < ActiveRecord::Migration
def change
add_index "spree_products", ["permalink"], :name => "permalink_idx_unique", :unique => true
end
end

View File

@@ -0,0 +1,7 @@
# This migration comes from spree (originally 20130628022817)
class AddUniqueIndexToOrdersShipmentsAndStockTransfers < ActiveRecord::Migration
def add
add_index "spree_orders", ["number"], :name => "number_idx_unique", :unique => true
add_index "spree_shipments", ["number"], :name => "number_idx_unique", :unique => true
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20130207043555) do
ActiveRecord::Schema.define(:version => 20130629120645) do
create_table "cms_blocks", :force => true do |t|
t.integer "page_id", :null => false
@@ -156,6 +156,7 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
t.string "next_collection_at"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.text "distributor_info"
end
create_table "exchange_fees", :force => true do |t|
@@ -240,17 +241,17 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
create_table "spree_adjustments", :force => true do |t|
t.integer "source_id"
t.decimal "amount", :precision => 8, :scale => 2
t.decimal "amount", :precision => 10, :scale => 2
t.string "label"
t.string "source_type"
t.integer "adjustable_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.boolean "mandatory"
t.boolean "locked"
t.integer "originator_id"
t.string "originator_type"
t.boolean "eligible", :default => true
t.boolean "eligible", :default => true
t.string "adjustable_type"
end
@@ -405,17 +406,17 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
create_table "spree_orders", :force => true do |t|
t.string "number", :limit => 15
t.decimal "item_total", :precision => 8, :scale => 2, :default => 0.0, :null => false
t.decimal "total", :precision => 8, :scale => 2, :default => 0.0, :null => false
t.decimal "item_total", :precision => 10, :scale => 2, :default => 0.0, :null => false
t.decimal "total", :precision => 10, :scale => 2, :default => 0.0, :null => false
t.string "state"
t.decimal "adjustment_total", :precision => 8, :scale => 2, :default => 0.0, :null => false
t.decimal "adjustment_total", :precision => 10, :scale => 2, :default => 0.0, :null => false
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.datetime "completed_at"
t.integer "bill_address_id"
t.integer "ship_address_id"
t.decimal "payment_total", :precision => 8, :scale => 2, :default => 0.0
t.decimal "payment_total", :precision => 10, :scale => 2, :default => 0.0
t.integer "shipping_method_id"
t.string "shipment_state"
t.string "payment_state"
@@ -442,10 +443,10 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
end
create_table "spree_payments", :force => true do |t|
t.decimal "amount", :precision => 8, :scale => 2, :default => 0.0, :null => false
t.decimal "amount", :precision => 10, :scale => 2, :default => 0.0, :null => false
t.integer "order_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "source_id"
t.string "source_type"
t.integer "payment_method_id"
@@ -453,8 +454,12 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
t.string "response_code"
t.string "avs_response"
t.string "identifier"
t.string "cvv_response_code"
t.string "cvv_response_message"
end
add_index "spree_payments", ["order_id"], :name => "index_spree_payments_on_order_id"
create_table "spree_paypal_accounts", :force => true do |t|
t.string "email"
t.string "payer_id"
@@ -486,6 +491,8 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
t.string "currency"
end
add_index "spree_prices", ["variant_id"], :name => "index_spree_prices_on_variant_id"
create_table "spree_product_groups", :force => true do |t|
t.string "name"
t.string "permalink"
@@ -534,7 +541,7 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
t.datetime "available_on"
t.datetime "deleted_at"
t.string "permalink"
t.string "meta_description"
t.text "meta_description"
t.string "meta_keywords"
t.integer "tax_category_id"
t.integer "shipping_category_id"
@@ -551,6 +558,7 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
add_index "spree_products", ["deleted_at"], :name => "index_products_on_deleted_at"
add_index "spree_products", ["name"], :name => "index_products_on_name"
add_index "spree_products", ["permalink"], :name => "index_products_on_permalink"
add_index "spree_products", ["permalink"], :name => "permalink_idx_unique", :unique => true
create_table "spree_products_promotion_rules", :id => false, :force => true do |t|
t.integer "product_id"
@@ -560,7 +568,7 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
add_index "spree_products_promotion_rules", ["product_id"], :name => "index_products_promotion_rules_on_product_id"
add_index "spree_products_promotion_rules", ["promotion_rule_id"], :name => "index_products_promotion_rules_on_promotion_rule_id"
create_table "spree_products_taxons", :id => false, :force => true do |t|
create_table "spree_products_taxons", :force => true do |t|
t.integer "product_id"
t.integer "taxon_id"
end
@@ -621,11 +629,11 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
create_table "spree_return_authorizations", :force => true do |t|
t.string "number"
t.string "state"
t.decimal "amount", :precision => 8, :scale => 2, :default => 0.0, :null => false
t.decimal "amount", :precision => 10, :scale => 2, :default => 0.0, :null => false
t.integer "order_id"
t.text "reason"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "spree_roles", :force => true do |t|
@@ -654,6 +662,7 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
end
add_index "spree_shipments", ["number"], :name => "index_shipments_on_number"
add_index "spree_shipments", ["order_id"], :name => "index_spree_shipments_on_order_id"
create_table "spree_shipping_categories", :force => true do |t|
t.string "name"
@@ -744,6 +753,9 @@ ActiveRecord::Schema.define(:version => 20130207043555) do
t.integer "icon_file_size"
t.datetime "icon_updated_at"
t.text "description"
t.string "meta_title"
t.string "meta_description"
t.string "meta_keywords"
end
add_index "spree_taxons", ["parent_id"], :name => "index_taxons_on_parent_id"

View File

@@ -2,6 +2,7 @@
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
require File.expand_path('../../spec/factories', __FILE__)
require File.expand_path('../../spec/support/spree/init', __FILE__)
# -- Spree
@@ -72,21 +73,31 @@ end
unless Spree::Product.count > 0
puts "[db:seed] Seeding products"
FactoryGirl.create(:product,
prod1 = FactoryGirl.create(:product,
:name => 'Garlic', :price => 20.00,
:supplier => Enterprise.is_primary_producer[0],
:distributors => [Enterprise.is_distributor[0]],
:taxons => [Spree::Taxon.find_by_name('Vegetables')])
FactoryGirl.create(:product,
ProductDistribution.create(:product => prod1,
:distributor => Enterprise.is_distributor[0],
:shipping_method => Spree::ShippingMethod.first)
prod2 = FactoryGirl.create(:product,
:name => 'Fuji Apple', :price => 5.00,
:supplier => Enterprise.is_primary_producer[1],
:distributors => Enterprise.is_distributor,
:taxons => [Spree::Taxon.find_by_name('Fruit')])
FactoryGirl.create(:product,
ProductDistribution.create(:product => prod2,
:distributor => Enterprise.is_distributor[1],
:shipping_method => Spree::ShippingMethod.first)
prod3 = FactoryGirl.create(:product,
:name => 'Beef - 5kg Trays', :price => 50.00,
:supplier => Enterprise.is_primary_producer[2],
:distributors => [Enterprise.is_distributor[2]],
:taxons => [Spree::Taxon.find_by_name('Meat and Fish')])
ProductDistribution.create(:product => prod3,
:distributor => Enterprise.is_distributor[2],
:shipping_method => Spree::ShippingMethod.first)
end

View File

@@ -0,0 +1,3 @@
.bundle/
log/*.log
pkg/

View File

@@ -0,0 +1,3 @@
= Eaterprises Feature
This feature is released under the AGPL licence.

View File

@@ -0,0 +1,13 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require_tree .

View File

@@ -0,0 +1,13 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style scope.
*
*= require_self
*= require_tree .
*/

View File

@@ -0,0 +1 @@
$link_text_color: #006066;

View File

@@ -0,0 +1,3 @@
<!-- insert_bottom 'head' -->
<%= stylesheet_link_tag 'eaterprises_feature/application' %>
<%= javascript_include_tag 'eaterprises_feature/application' %>

View File

@@ -1,3 +1,4 @@
<!-- insert_bottom "[data-hook='inside_head']" -->
<% if Rails.env.production? %>
<script type="text/javascript">
var uvOptions = {};

View File

@@ -0,0 +1,3 @@
EaterprisesFeature::Engine.automount!
EaterprisesFeature::Engine.routes.draw do
end

View File

@@ -0,0 +1,22 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "eaterprises_feature/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "eaterprises_feature"
s.version = EaterprisesFeature::VERSION
s.authors = ["Rohan Mitchell"]
s.email = ["rohan@rohanmitchell.com"]
s.homepage = ""
s.summary = "Summary of EaterprisesFeature."
s.description = "Description of EaterprisesFeature."
s.files = Dir["{app,config,db,lib}/**/*"] + ["README.rdoc"]
s.add_dependency "rails", "~> 3.2.11"
s.add_dependency 'chili', '~> 3.1'
s.add_development_dependency "sqlite3"
end

View File

@@ -0,0 +1,7 @@
require "chili"
require "eaterprises_feature/engine"
module EaterprisesFeature
extend Chili::Base
active_if { OpenFoodWeb::FeatureToggle.enabled? :eaterprises }
end

View File

@@ -0,0 +1,17 @@
require_relative '../../../../open_food_web/feature_toggle'
module EaterprisesFeature
class Engine < ::Rails::Engine
isolate_namespace EaterprisesFeature
if OpenFoodWeb::FeatureToggle.enabled? :eaterprises
initializer 'eaterprises_feature.sass', :after => :load_config_initializers do |app|
app.config.sass.load_paths += [self.root.join('app', 'assets', 'stylesheets', 'eaterprises_feature')] if Rails.application.config.respond_to? :sass
end
initializer :assets do |app|
app.config.assets.precompile += ['eaterprises_feature/*']
end
end
end
end

View File

@@ -0,0 +1,3 @@
module EaterprisesFeature
VERSION = "0.0.1"
end

View File

@@ -0,0 +1,3 @@
class EaterprisesFeatureGenerator < Rails::Generators::Base
include Chili::GeneratorProxy
end

View File

@@ -0,0 +1,3 @@
.bundle/
log/*.log
pkg/

View File

@@ -0,0 +1,3 @@
= EnterprisesDistributorInfoRichTextFeature
This project rocks and uses MIT-LICENSE.

View File

@@ -0,0 +1,13 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require_tree .

View File

@@ -0,0 +1,13 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style scope.
*
*= require_self
*= require_tree .
*/

View File

@@ -0,0 +1,5 @@
.distributor-details .next-collection-at {
font-size: 20px;
font-weight: bold;
color: #de790c;
}

View File

@@ -0,0 +1,4 @@
/ insert_after "[data-hook='long_description']"
%tr{"data-hook" => "distributor_info"}
%td Distributor Info:
%td= f.text_area :distributor_info, :class => 'rich_text'

View File

@@ -0,0 +1,3 @@
/ replace_contents "[data-hook='long_description']"
%td Profile Info:
%td= f.text_area :long_description, :class => 'rich_text'

View File

@@ -0,0 +1,4 @@
/ insert_after "[data-hook='long_description']"
%tr{'data-hook' => 'distributor_info'}
%th Distributor Info:
%td= @enterprise.distributor_info.andand.html_safe

View File

@@ -0,0 +1,3 @@
/ replace_contents "[data-hook='long_description']"
%th Profile Info:
%td= @enterprise.long_description.andand.html_safe

View File

@@ -0,0 +1,4 @@
/ replace_contents "[data-hook='distributor-details']"
%h2= distributor.name
= distributor.distributor_info.andand.html_safe
.next-collection-at= distributor.next_collection_at

View File

@@ -0,0 +1,2 @@
<!-- insert_bottom 'head' -->
<%= stylesheet_link_tag 'enterprises_distributor_info_rich_text_feature/application' %>

View File

@@ -0,0 +1,36 @@
Dear <%= @order.bill_address.firstname %>,
Please review and retain the following order information for your records.
============================================================
Order Summary
============================================================
Order for: <%= @order.bill_address.full_name %>
<% @order.line_items.each do |item| %>
<%= item.variant.sku %> <%= raw(item.variant.product.name) %> <%= raw(item.variant.options_text) -%> (<%=item.quantity%>) @ <%= item.single_money %> = <%= item.display_amount %>
<% end %>
============================================================
Subtotal: <%= @order.display_item_total %>
<% @order.adjustments.eligible.each do |adjustment| %>
<%= raw(adjustment.label) %> <%= adjustment.display_amount %>
<% end %>
Order Total: <%= @order.display_total %>
<% if @order.payments.first.payment_method.name.include? "EFT" %>
============================================================
Payment Details
============================================================
<%= @order.payments.first.payment_method.description.html_safe %>
<% end %>
============================================================
Collection / Delivery Details
============================================================
<%= raw strip_html @order.distributor.distributor_info %>
<%= @order.distributor.next_collection_at %>
Thanks for your support.
Marcus and Angie,
Local Organics

View File

@@ -0,0 +1,3 @@
EnterprisesDistributorInfoRichTextFeature::Engine.automount!
EnterprisesDistributorInfoRichTextFeature::Engine.routes.draw do
end

View File

@@ -0,0 +1,5 @@
class AddDistributorInfoToEnterprises < ActiveRecord::Migration
def change
add_column :enterprises, :distributor_info, :text
end
end

View File

@@ -0,0 +1,22 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "enterprises_distributor_info_rich_text_feature/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "enterprises_distributor_info_rich_text_feature"
s.version = EnterprisesDistributorInfoRichTextFeature::VERSION
s.authors = ["Rohan Mitchell"]
s.email = ["rohan@rohanmitchell.com"]
s.homepage = ""
s.summary = "Summary of EnterprisesDistributorInfoRichTextFeature."
s.description = "Description of EnterprisesDistributorInfoRichTextFeature."
s.files = Dir["{app,config,db,lib}/**/*"] + ["README.rdoc"]
s.add_dependency "rails", "~> 3.2.11"
s.add_dependency 'chili', '~> 3.1'
s.add_development_dependency "sqlite3"
end

View File

@@ -0,0 +1,7 @@
require "chili"
require "enterprises_distributor_info_rich_text_feature/engine"
module EnterprisesDistributorInfoRichTextFeature
extend Chili::Base
active_if { OpenFoodWeb::FeatureToggle.enabled? :enterprises_distributor_info_rich_text }
end

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