Merge branch 'master' into updated-gems

Conflicts:
	Gemfile.lock
This commit is contained in:
wvengen 2013-09-18 13:00:26 +02:00
commit 0abb63279f
55 changed files with 2898 additions and 136 deletions

1
.gitignore vendored
View file

@ -16,3 +16,4 @@ doc/app/
Capfile
config/deploy.rb
config/deploy/*
.localeapp

14
.travis.yml Normal file
View file

@ -0,0 +1,14 @@
language: ruby
rvm:
- 1.9.3
services:
- redis-server
before_install:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
before_script:
- "bundle exec rake foodsoft:setup:stock_config"
- "mysql -e 'create database foodsoft_test;'"
- 'printf "test:\n adapter: mysql2\n database: foodsoft_test\n username: travis\n encoding: utf8\n" >config/database.yml'
- 'bundle exec rake db:schema:load RAILS_ENV=test'
script: bundle exec rake spec

24
Gemfile
View file

@ -53,10 +53,6 @@ group :development do
gem 'binding_of_caller'
# gem "rails-i18n-debug"
# Re-enable rails benchmarker/profiler
gem 'ruby-prof'
gem 'test-unit'
# Get infos when not using proper eager loading
gem 'bullet'
@ -71,6 +67,22 @@ group :development do
gem 'thin'
end
# Gems left for backwards compatibility
gem 'acts_as_configurable', git: 'git://github.com/bwalding/acts_as_configurable.git' # user settings migration needs it
group :development, :test do
gem 'ruby-prof'
end
group :test do
gem 'rspec-rails'
gem 'factory_girl_rails', '~> 4.0'
gem 'faker'
# version requirements to avoid problem http://stackoverflow.com/questions/18114544
gem 'capybara', '~> 2.1.0'
# webkit and poltergeist don't seem to work yet
gem 'selenium-webdriver', '~> 2.35.1'
gem 'database_cleaner'
gem 'simplecov', require: false
# need to include rspec components before i18n-spec or rake fails in test environment
gem 'rspec-core'
gem 'rspec-expectations'
gem 'i18n-spec'
end

View file

@ -4,13 +4,6 @@ GIT
specs:
localize_input (0.1.0)
GIT
remote: git://github.com/bwalding/acts_as_configurable.git
revision: cdf6f6f979019275b523d10684b748f08e2dd8e8
specs:
acts_as_configurable (0.0.1)
rake
GIT
remote: git://github.com/technoweenie/acts_as_versioned.git
revision: 63b1fc8529d028fae632fe80ec0cb25df56cd76b
@ -71,6 +64,14 @@ GEM
net-ssh-gateway (>= 1.1.0)
capistrano-ext (1.2.1)
capistrano (>= 1.0.0)
capybara (2.1.0)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
childprocess (0.3.9)
ffi (~> 1.0, >= 1.0.11)
chronic (0.9.1)
client_side_validations (3.2.5)
client_side_validations-simple_form (2.1.0)
@ -86,7 +87,9 @@ GEM
coffee-script-source (1.6.3)
commonjs (0.2.6)
daemons (1.1.9)
database_cleaner (0.7.1)
debug_inspector (0.0.2)
diff-lcs (1.2.4)
erubis (2.7.0)
eventmachine (1.0.3)
exception_notification (4.0.0)
@ -95,6 +98,14 @@ GEM
execjs (1.4.0)
multi_json (~> 1.0)
expression_parser (0.9.0)
factory_girl (4.2.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.2.1)
factory_girl (~> 4.2.0)
railties (>= 3.0.0)
faker (1.1.2)
i18n (~> 0.5)
ffi (1.9.0)
haml (4.0.3)
tilt
haml-rails (0.4)
@ -107,9 +118,13 @@ GEM
highline (1.6.19)
hike (1.2.3)
i18n (0.6.1)
i18n-spec (0.4.0)
iso
inherited_resources (1.4.0)
has_scope (~> 0.5.0)
responders (~> 0.9)
iso (0.2.0)
i18n
journey (1.0.4)
jquery-rails (3.0.4)
railties (>= 3.0, < 5.0)
@ -142,6 +157,7 @@ GEM
activesupport (~> 3.1)
polyamorous (~> 0.5.0)
mime-types (1.23)
mini_portile (0.5.1)
mono_logger (1.1.0)
multi_json (1.7.7)
mysql2 (0.3.11)
@ -152,6 +168,8 @@ GEM
net-ssh (2.6.8)
net-ssh-gateway (1.2.0)
net-ssh (>= 2.6.5)
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
pdf-reader (1.3.3)
Ascii85 (~> 1.0.0)
afm (~> 0.2.0)
@ -196,7 +214,7 @@ GEM
rdoc (3.12.2)
json (~> 1.4)
redis (3.0.4)
redis-namespace (1.3.0)
redis-namespace (1.3.1)
redis (~> 3.0.0)
ref (1.0.5)
responders (0.9.3)
@ -207,8 +225,20 @@ GEM
redis-namespace (~> 1.2)
sinatra (>= 0.9.2)
vegas (~> 0.1.2)
rspec-core (2.14.2)
rspec-expectations (2.14.0)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.1)
rspec-rails (2.14.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
ruby-prof (0.13.0)
ruby-rc4 (0.1.5)
rubyzip (0.9.9)
sass (3.2.9)
sass-rails (3.2.6)
railties (~> 3.2.0)
@ -217,6 +247,11 @@ GEM
select2-rails (3.4.3)
sass-rails
thor (~> 0.14)
selenium-webdriver (2.35.1)
childprocess (>= 0.2.5)
multi_json (~> 1.0)
rubyzip (< 1.0.0)
websocket (~> 1.0.4)
simple-navigation (3.11.0)
activesupport (>= 2.3.2)
simple-navigation-bootstrap (1.0.0)
@ -225,6 +260,10 @@ GEM
simple_form (2.1.0)
actionpack (~> 3.0)
activemodel (~> 3.0)
simplecov (0.7.1)
multi_json (~> 1.0)
simplecov-html (~> 0.7.1)
simplecov-html (0.7.1)
sinatra (1.4.3)
rack (~> 1.4)
rack-protection (~> 1.4)
@ -238,7 +277,6 @@ GEM
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.7)
test-unit (2.5.5)
therubyracer (0.11.4)
libv8 (~> 3.11.8.12)
ref
@ -264,18 +302,20 @@ GEM
uniform_notifier (1.2.0)
vegas (0.1.11)
rack (>= 1.0.0)
websocket (1.0.7)
whenever (0.8.3)
activesupport (>= 2.3.4)
chronic (>= 0.6.3)
wikicloth (0.8.0)
builder
expression_parser
xpath (2.0.0)
nokogiri (~> 1.3)
PLATFORMS
ruby
DEPENDENCIES
acts_as_configurable!
acts_as_tree
acts_as_versioned!
better_errors
@ -284,12 +324,17 @@ DEPENDENCIES
bullet
capistrano (= 2.13.5)
capistrano-ext
capybara (~> 2.1.0)
client_side_validations
client_side_validations-simple_form
coffee-rails (~> 3.2.1)
daemons
database_cleaner
exception_notification
factory_girl_rails (~> 4.0)
faker
haml-rails
i18n-spec
inherited_resources
jquery-rails
kaminari
@ -303,14 +348,18 @@ DEPENDENCIES
rails (~> 3.2.9)
rails-settings-cached (= 0.2.4)
resque
rspec-core
rspec-expectations
rspec-rails
ruby-prof
sass-rails (~> 3.2.3)
select2-rails
selenium-webdriver (~> 2.35.1)
simple-navigation
simple-navigation-bootstrap
simple_form
simplecov
sqlite3
test-unit
therubyracer
thin
twitter-bootstrap-rails

View file

@ -1,10 +1,6 @@
Important
--------
We changed the branch structure. The rails3 branch is now master. But you can safely send pull requests to rails3. It'll remain there for a couple of weeks.
FoodSoft
=========
[![Build Status](https://travis-ci.org/foodcoops/foodsoft.png?branch=tests-rspec)](https://travis-ci.org/foodcoops/foodsoft)
[![Code Climate](https://codeclimate.com/github/foodcoops/foodsoft.png)](https://codeclimate.com/github/foodcoops/foodsoft)
[![Dependency Status](https://gemnasium.com/foodcoops/foodsoft.png)](https://gemnasium.com/foodcoops/foodsoft)

View file

@ -209,7 +209,7 @@ class ArticlesController < ApplicationController
# fills a form whith values of the selected shared_article
def import
@article = SharedArticle.find(params[:shared_article_id]).build_new_article
@article = SharedArticle.find(params[:shared_article_id]).build_new_article(@supplier)
render :action => 'new', :layout => false
end

View file

@ -18,7 +18,7 @@ class SuppliersController < ApplicationController
def new
if params[:shared_supplier_id]
shared_supplier = SharedSupplier.find(params[:shared_supplier_id])
@supplier = shared_supplier.build_supplier(shared_supplier.autofill_attributes)
@supplier = shared_supplier.suppliers.new(shared_supplier.autofill_attributes)
else
@supplier = Supplier.new
end

View file

@ -20,20 +20,28 @@ class OrderFax < OrderPdf
move_down 5
text "#{contact[:zip_code]} #{contact[:city]}", size: 9, align: :right
move_down 5
text "#{I18n.t('simple_form.labels.supplier.customer_number')}: #{@order.supplier.try(:customer_number)}", size: 9, align: :right
unless @order.supplier.try(:customer_number).blank?
text "#{I18n.t('simple_form.labels.supplier.customer_number')}: #{@order.supplier[:customer_number]}", size: 9, align: :right
move_down 5
end
unless contact[:phone].blank?
text "#{I18n.t('simple_form.labels.supplier.phone')}: #{contact[:phone]}", size: 9, align: :right
move_down 5
end
unless contact[:email].blank?
text "#{I18n.t('simple_form.labels.supplier.email')}: #{contact[:email]}", size: 9, align: :right
end
end
# Recipient
bounding_box [margin_box.left,margin_box.top-60], width: 200 do
text @order.name
move_down 5
text @order.supplier.try(:address).to_s
unless @order.supplier.try(:fax).blank?
move_down 5
text "#{I18n.t('simple_form.labels.supplier.fax')}: #{@order.supplier.try(:fax)}"
text "#{I18n.t('simple_form.labels.supplier.fax')}: #{@order.supplier[:fax]}"
end
end
move_down 5
@ -42,25 +50,37 @@ class OrderFax < OrderPdf
move_down 10
text "#{I18n.t('simple_form.labels.delivery.delivered_on')}:"
move_down 10
text "#{I18n.t('simple_form.labels.supplier.contact_person')}: #{@order.supplier.try(:contact_person)}"
unless @order.supplier.try(:contact_person).blank?
text "#{I18n.t('simple_form.labels.supplier.contact_person')}: #{@order.supplier[:contact_person]}"
move_down 10
end
# Articles
total = 0
data = [I18n.t('documents.order_fax.rows')]
data += @order.order_articles.ordered.all(include: :article).collect do |a|
subtotal = a.units_to_order * a.price.unit_quantity * a.price.price
total += subtotal
[a.article.order_number,
a.units_to_order,
a.article.name,
a.price.unit_quantity,
a.article.unit,
a.price.price]
number_to_currency(a.price.price),
number_to_currency(subtotal)]
end
data << [I18n.t('documents.order_fax.total'), nil, nil, nil, nil, nil, number_to_currency(total)]
table data, cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
table.header = true
table.cells.border_width = 1
table.cells.border_color = '666666'
table.row(0).border_bottom_width = 2
table.columns(1).align = :right
table.columns(3..5).align = :right
table.columns(3..6).align = :right
table.row(data.length-1).columns(0..5).borders = [:top, :bottom]
table.row(data.length-1).columns(0).borders = [:top, :bottom, :left]
table.row(data.length-1).border_top_width = 2
end
#font_size: 8,
#vertical_padding: 3,

View file

@ -0,0 +1,6 @@
module SuppliersHelper
def associated_supplier_names(shared_supplier)
"(#{shared_supplier.suppliers.map(&:name).join(', ')})"
end
end

View file

@ -7,8 +7,8 @@ class SharedArticle < ActiveRecord::Base
belongs_to :shared_supplier, :foreign_key => :supplier_id
def build_new_article
shared_supplier.supplier.articles.build(
def build_new_article(supplier)
supplier.articles.build(
:name => name,
:unit => unit,
:note => note,

View file

@ -5,7 +5,7 @@ class SharedSupplier < ActiveRecord::Base
# set correct table_name in external DB
self.table_name = 'suppliers'
has_one :supplier
has_many :suppliers
has_many :shared_articles, :foreign_key => :supplier_id
# These set of attributes are used to autofill attributes of new supplier,

View file

@ -13,11 +13,9 @@ class Supplier < ActiveRecord::Base
:delivery_days, :order_howto, :note, :shared_supplier_id, :min_order_quantity
validates :name, :presence => true, :length => { :in => 4..30 }
validates :phone, :presence => true, :length => { :in => 8..20 }
validates :phone, :presence => true, :length => { :in => 8..25 }
validates :address, :presence => true, :length => { :in => 8..50 }
validates_length_of :order_howto, :note, maximum: 250
validates_length_of :phone, :in => 8..20
validates_length_of :address, :in => 8..50
validate :uniqueness_of_name
scope :undeleted, -> { where(deleted_at: nil) }

View file

@ -66,7 +66,7 @@ class User < ActiveRecord::Base
end
def receive_email?
settings.messages['send_as_email'] == "1" && email.present?
settings.messages['send_as_email'] && email.present?
end
# Sets the user's password. It will be stored encrypted along with a random salt.

View file

@ -3,7 +3,7 @@ class Workgroup < Group
has_many :tasks
# returns all non-finished tasks
has_many :open_tasks, :class_name => 'Task', :conditions => ['done = ?', false], :order => 'due_date ASC'
has_many :open_tasks, :class_name => 'Task', :conditions => ['done = ?', false], order: 'due_date ASC, name ASC'
validates_uniqueness_of :name
validate :last_admin_on_earth, :on => :update

View file

@ -12,7 +12,7 @@
.well.well-small
%h3= t('.notes_and_journal')
#note
- unless @order.note.empty?
- unless @order.note.blank?
= simple_format @order.note
- else
%p= t('.comment_on_transaction')

View file

@ -5,7 +5,7 @@
%thead
%tr
%th= sort_link_helper t('.name'), "name", :per_page => @per_page
%th Kontakt
%th= t '.contact'
%th.numeric= sort_link_helper t('.account_balance'), "account_balance", :per_page => @per_page
%th
%tbody

View file

@ -56,7 +56,7 @@
%pre
* #{t '.help.list_item_1'}
%pre
** #{t '.help_list_item_2'}
** #{t '.help.list_item_2'}
%tr
%td= t '.help.ordered_list'
%td

View file

@ -4,4 +4,4 @@
= f.input :date
= f.input :note
= f.submit
= link_to t('ui.cancel'), stock_takings_path
= link_to t('ui.or_cancel'), stock_takings_path

View file

@ -14,4 +14,4 @@
= render :partial => 'stock_change', :collection => @stock_taking.stock_changes
.form-actions
= f.submit class: 'btn'
= link_to t('ui.cancel'), stock_takings_path
= link_to t('ui.or_cancel'), stock_takings_path

View file

@ -1,4 +1,4 @@
- title "Lager (#{StockArticle.available.count})"
- title t('.title', article_count: StockArticle.available.count)
- content_for :javascript do
:javascript
$(function() {

View file

@ -17,7 +17,9 @@
%td= shared_supplier.note
%td= shared_supplier.delivery_days
%td
- if shared_supplier.supplier
- if shared_supplier.suppliers.any?
%i.icon-ok
= associated_supplier_names(shared_supplier)
= link_to t('.subscribe_again'), new_supplier_path(:shared_supplier_id => shared_supplier), class: 'btn'
- else
= link_to t('.subscribe'), new_supplier_path(:shared_supplier_id => shared_supplier), class: 'btn'

View file

@ -4,7 +4,7 @@
- content_for :sidebar do
.well.well-small
%ul.nav.nav-list
%li.nav-header Seiten
%li.nav-header= t '.pages'
%li= link_to t('.my_tasks'), user_tasks_path
%li= link_to t('.all_tasks'), tasks_path
%li= link_to t('.archive'), archive_tasks_path

View file

@ -1,4 +1,4 @@
- title "Meine Aufgaben"
- title t('.title')
= render 'nav'
- unless @unaccepted_tasks.empty?

View file

@ -505,6 +505,7 @@ de:
- Gebinde
- Einheit
- Preis/Einheit
total: Gesamtpreis
order_matrix:
filename: Bestellung %{name}-%{date} - Sortiermatrix
heading: Artikelübersicht
@ -719,6 +720,7 @@ de:
ordergroups:
account_balance: Kontostand
account_statement: Kontoauszug
contact: Kontakt
name: Name
new_transaction: Neue Transaktion
update:
@ -1602,6 +1604,7 @@ de:
contact_person: Kontaktperson
contact_phone: Telefon
ignore_apple_restriction: Bestellstop bei zu wenig Äpfeln ignorieren
name: Name
page:
body: Inhalt
parent_id: Oberseite
@ -1669,6 +1672,7 @@ de:
language:
de: Deutsch
en: English
fr: Französisch
nl: Niederländisch
required:
mark: ! '*'
@ -1742,6 +1746,7 @@ de:
show_stock_takings: Inventurübersicht
stock_count: ! 'Artikelanzahl:'
stock_worth: ! 'Aktueller Lagerwert:'
title: Lager (%{article_count})
toggle_unavailable: Nicht verfügbare Artikel zeigen/verstecken
view_options: Ansichtsoptionen
new:
@ -1772,6 +1777,7 @@ de:
shared_suppliers:
body: <p>Hier werden die Lieferantinnen der externen Datenbank angezeigt.</p> <p>Ihr könnt externe Lieferantinnen importieren, indem ihr sie einfach abonniert. (siehe unten)</p> <p>Damit wird eine neue Lieferantin angelegt und mit der externen Datenbank verknüpft.</p>
subscribe: abonnieren
subscribe_again: erneut abonnieren
supplier: Lieferantin
title: Externe Listen
show:
@ -1832,6 +1838,7 @@ de:
group_tasks: Gruppenaufgaben
my_tasks: Meine Aufgaben
new_task: Neue Aufgabe erstellen
pages: Seiten
new:
title: Neue Aufgabe erstellen
repeated: Aufgabe wird wöchentlich wiederholt

View file

@ -236,7 +236,7 @@ en:
option_available: Make articles available
option_delete: Delete article
option_not_available: Make articles unavailable
option_select: Choose special offer ...
option_select: Select action ...
price_netto: Price
unit_quantity_desc: Unit quantity
unit_quantity_short: Quantity
@ -440,7 +440,7 @@ en:
article: Article
category: Category
create_from_blank: Create new article
create_stock_article: Create stock articles
create_stock_article: Create stock article
price: Netprice
quantity: Quantity
title_fill_quantities: 2. Set delivery quantities
@ -493,7 +493,7 @@ en:
- Article
- Amount
- Price
- Unit Quantity
- Unit quantity
- Unit
- Sum
sum: Sum
@ -504,20 +504,24 @@ en:
- Order Number
- Amount
- Name
- Barrel
- Unit quantity
- Unit
- Price/Unit
- Subtotal
total: Total
order_matrix:
filename: Order %{name}-%{date} - sorting matrix
heading: Article overview
rows:
- Article
- Unit
- Barrel
- Unit quantity
- FC-Price
- Amount
title: ! 'Order sorting matrix: %{name}, closed at %{date}'
total: ! '%{count} articles in total'
total:
one: One article in total
other: ! '%{count} articles in total'
errors:
format: ! '%{attribute} %{message}'
general: A problem has occured.
@ -721,6 +725,7 @@ en:
ordergroups:
account_balance: Account balance
account_statement: Account statement
contact: Contact
name: Name
new_transaction: New transaction
update:
@ -1604,6 +1609,7 @@ en:
contact_person: Contact person
contact_phone: Phone
ignore_apple_restriction: Ignore order stop by apple points restriction
name: Name
page:
body: Body
parent_id: Parent page
@ -1671,6 +1677,7 @@ en:
language:
de: German
en: English
fr: French
nl: Dutch
required:
mark: ! '*'
@ -1744,6 +1751,7 @@ en:
show_stock_takings: Inventory overview
stock_count: ! 'Number of articles:'
stock_worth: ! 'Current stock value:'
title: Stock (%{article_count})
toggle_unavailable: Show/hide unavailable articles
view_options: View options
new:
@ -1774,6 +1782,7 @@ en:
shared_suppliers:
body: <p>Suppliers of the external database are displayed here.</p> <p>You can import external suppliers by subscribing (see below).</p> <p>A new supplier will be created and connected to the external database.</p>
subscribe: Subscribe
subscribe_again: Subscribe again
supplier: Supplier
title: External lists
show:
@ -1834,6 +1843,7 @@ en:
group_tasks: Group tasks
my_tasks: My tasks
new_task: Create new task
pages: Pages
new:
title: Create new tasks
repeated: Task is repeated weekly

1919
config/locales/fr.yml Normal file

File diff suppressed because it is too large Load diff

View file

@ -479,48 +479,60 @@ nl:
notice:
documents:
order_by_articles:
filename:
filename: Bestelling %{name}-%{date} - Artikellijst
rows:
title:
- Huishouden
- Hoeveelheid
- Prijs
title: ! 'Artikellijst van bestelling: %{name}, gesloten op %{date}'
order_by_groups:
filename:
filename: Bestelling %{name}-%{date} - Huishoudenslijst
rows:
sum:
title:
- Artikel
- Hoeveelheid
- Prijs
- Gr.Eenh.
- Eenheid
- Som
sum: Som
title: ! 'Huishoudenslijst van bestelling: %{name}, gesloten op %{date}'
order_fax:
filename:
rows:
filename: Bestelling %{name}-%{date} - Fax
rows: ! '[]'
total: Totaal
order_matrix:
filename:
heading:
filename: Bestelling %{name}-%{date} - Sorteermatrix
heading: Artikeloverzicht
rows:
title:
title: ! 'Sorteermatrix van bestelling: %{name}, gesloten op %{date}'
total:
one: In totaal éen artikel
other: In totaal %{count} artikelen
errors:
format:
general:
general: Er is een probleem opgetreden.
general_again:
general_msg:
general_msg: ! 'Er is een probleem opgetreden: %{msg}'
messages:
accepted:
blank:
accepted: moet geaccepteerd worden
blank: moet ingevuld worden
confirmation:
empty:
equal_to:
empty: moet ingevuld worden
equal_to: moet precies %{count} zijn
even:
exclusion:
greater_than:
exclusion: moet even zijn
greater_than: moet groter dan %{count} zijn
greater_than_or_equal_to:
inclusion:
invalid:
less_than:
less_than_or_equal_to:
not_a_number:
not_an_integer:
odd:
less_than: moet kleiner dan %{count} zijn
less_than_or_equal_to: moet groter of gelijk aan %{count} zijn
not_a_number: is geen getal
not_an_integer: moet een geheel getal zijn
odd: moet oneven zijn
record_invalid:
taken:
taken_with_deleted:
taken: is al in gebruik
taken_with_deleted: is al in gebruik (verwijderde groep)
too_long:
too_short:
wrong_length:
@ -697,6 +709,7 @@ nl:
ordergroups:
account_balance: Tegoed
account_statement:
contact:
name: Naam
new_transaction: Nieuwe transactie
update:
@ -1493,6 +1506,7 @@ nl:
contact_person: Contactpersoon
contact_phone: Telefoon
ignore_apple_restriction:
name:
page:
body:
parent_id:
@ -1560,6 +1574,7 @@ nl:
language:
de: Duits
en: Engels
fr: Frans
nl: Nederlands
required:
mark: ! '*'
@ -1633,6 +1648,7 @@ nl:
show_stock_takings:
stock_count:
stock_worth:
title:
toggle_unavailable:
view_options:
new:
@ -1663,6 +1679,7 @@ nl:
shared_suppliers:
body:
subscribe:
subscribe_again:
supplier:
title:
show:
@ -1723,6 +1740,7 @@ nl:
group_tasks:
my_tasks:
new_task:
pages:
new:
title:
repeated:
@ -1763,9 +1781,9 @@ nl:
history:
marks:
close: ! '&times;'
success:
success: <i class="icon icon-ok"></i>
or_cancel: of annuleren
please_wait:
please_wait: Een moment alstublieft...
save: Opslaan
show: Tonen
views:

View file

@ -35,6 +35,8 @@ class MoveWeeklyTasks < ActiveRecord::Migration
private
def weekly_task?(workgroup, task)
return false if task.due_date.nil?
group_task = {
weekday: workgroup.weekday,
name: workgroup.task_name,

View file

@ -1,12 +1,25 @@
class MigrateUserSettings < ActiveRecord::Migration
def up
say_with_time 'Save old user settings in new RailsSettings module' do
# Allow setting default locale via env parameter
# This is used, when setting users language settings
default_locale = I18n.default_locale
tmp_locale = ENV['DEFAULT_LOCALE'].present? ? ENV['DEFAULT_LOCALE'].to_sym : default_locale
I18n.default_locale = tmp_locale
old_settings = ConfigurableSetting.all
old_settings.each do |old_setting|
# get target (user)
type = old_setting.configurable_type
id = old_setting.configurable_id
begin
user = type.constantize.find(id)
rescue ActiveRecord::RecordNotFound
Rails.logger.debug "Can't find configurable object with type: #{type.inspect}, id: #{id.inspect}"
next
end
# get the data (settings)
name = old_setting.name
@ -27,8 +40,16 @@ class MigrateUserSettings < ActiveRecord::Migration
# save the user to apply after_save callback
user.save
end
I18n.default_locale = default_locale
end
drop_table :configurable_settings
end
def down
end
end
# this is the base class of all configurable settings
class ConfigurableSetting < ActiveRecord::Base; end

View file

@ -66,18 +66,6 @@ ActiveRecord::Schema.define(:version => 20130718183101) do
add_index "assignments", ["user_id", "task_id"], :name => "index_assignments_on_user_id_and_task_id", :unique => true
create_table "configurable_settings", :force => true do |t|
t.integer "configurable_id"
t.string "configurable_type"
t.integer "targetable_id"
t.string "targetable_type"
t.string "name", :default => "", :null => false
t.string "value_type"
t.text "value"
end
add_index "configurable_settings", ["name"], :name => "index_configurable_settings_on_name"
create_table "deliveries", :force => true do |t|
t.integer "supplier_id"
t.date "delivered_on"

View file

@ -11,7 +11,7 @@ module FoodsoftFile
def self.parse(file)
articles, outlisted_articles = Array.new, Array.new
row_index = 2
::CSV.parse(file.read, {:col_sep => ";", :headers => true}) do |row|
::CSV.parse(file.read.force_encoding('utf-8'), {:col_sep => ";", :headers => true}) do |row|
# check if the line is empty
unless row[2] == "" || row[2].nil?
article = {:number => row[1],

View file

@ -38,6 +38,15 @@ namespace :foodsoft do
puts yellow "All done! Your foodcoft should be running smoothly."
start_server
end
namespace :setup do
desc "Initialize stock configuration"
task :stock_config do
setup_app_config
setup_development
setup_secret_token
end
end
end
def setup_bundler

View file

@ -1,17 +1,21 @@
# This namespace is used for a collection of tasks to maintain a hosting environment with multiple foodcoops
# This tasks are a kind of wrapper for other tasks. The wrapper makes sure, that the appropriate database and config
# for each foodcoop is used.
namespace :multicoops do
desc 'Runs a specific rake task for each registered foodcoop, use rake multicoops:run db:migrate'
desc 'Runs a specific rake task for each registered foodcoop, use rake multicoops:run TASK=db:migrate'
task :run => :environment do
task_to_run = ARGV[1]
task_to_run = ENV['TASK']
FoodsoftConfig.each_coop do |coop|
puts "Run '#{task_to_run}' for #{coop}"
Rake::Task[task_to_run].execute
end
end
desc 'Runs a specific rake task for a single coop, use rake mutlicoops:run_single db:migrate FOODCOOP=demo'
desc 'Runs a specific rake task for a single coop, use rake mutlicoops:run_single TASK=db:migrate FOODCOOP=demo'
task :run_single => :environment do
task_to_run = ARGV[1]
task_to_run = ENV['TASK']
FoodsoftConfig.select_foodcoop ENV['FOODCOOP']
puts "Run '#{task_to_run}' for #{ENV['FOODCOOP']}"
Rake::Task[task_to_run].execute

3
lib/tasks/rspec.rake Normal file
View file

@ -0,0 +1,3 @@
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task :default => :spec

View file

@ -55,12 +55,16 @@ fi
sed -i "s|^\\(\\s*gem\\s\\+'sqlite3'\\)|#\1|" Gemfile
sed -i "s|^\\(\\s*sqlite3\\b\)|#\1|" Gemfile.lock
# make sure postgresql db is present, as it is the default heroku db
echo $'\ngem "pg"' >>Gemfile
echo $'\ngem "localeapp"' >>Gemfile
echo "
gem 'pg'" >>Gemfile
# always use unicorn
echo $'\ngem "unicorn"' >>Gemfile
echo "
gem 'unicorn'" >>Gemfile
echo 'web: bundle exec unicorn -p $PORT -E $RACK_ENV' >Procfile
bundle install --quiet # to update Gemfile.lock
# don't complain when mail cannot be sent,
# XXX when you're hosting a production instance, use a real smtp server instead
sed -i 's|\(#\s*\)\?\(config\.action_mailer\.raise_delivery_errors\)\s*=.*|\2 = false|' config/environments/${RAILS_ENV}.rb
sed -i 's|\(#\s*\)\?\(config\.action_mailer\.delivery_method\)\s*=.*|\2 = :smtp|' config/environments/${RAILS_ENV}.rb
# do not ignore deployment files
sed -i 's|^\(config/\*.yml\)|#\1|' .gitignore
sed -i 's|^\(config/initializers/secret_token.rb\)|#\1|' .gitignore
@ -92,9 +96,13 @@ Localeapp.configure do |config|
config.polling_environments = ['$RAILS_ENV']
end
EOF
echo "
gem 'localeapp'" >>Gemfile
# also do not cache so we get locale updates
sed -i 's|config\.cache_classes\s*=.*|config.cache_classes = false|' config/environments/${RAILS_ENV}.rb
sed -i 's|\(#\s*\)\?\(config\.cache_classes\)\s*=.*|\2 = false|' config/environments/${RAILS_ENV}.rb
fi
# update Gemfile.lock after Gemfile updates (required by heroku)
bundle install --quiet
# TODO add more extensive database seed
# and push = deploy

20
spec/factories/article.rb Normal file
View file

@ -0,0 +1,20 @@
require 'factory_girl'
FactoryGirl.define do
factory :article do
sequence(:name) { |n| Faker::Lorem.words(rand(2..4)).join(' ') + " ##{n}" }
unit { Faker::Unit.unit }
price { rand(2600) / 100 }
tax { [6, 21].sample }
deposit { rand(10) < 8 ? 0 : [0.0, 0.80, 1.20, 12.00].sample }
unit_quantity { rand(5) < 3 ? 1 : rand(1..20) }
#supplier_id
article_category { FactoryGirl.create :article_category }
end
factory :article_category do
sequence(:name) { |n| Faker::Lorem.characters(rand(2..12)) + " ##{n}" }
end
end

View file

@ -0,0 +1,10 @@
require 'factory_girl'
FactoryGirl.define do
# requires order
factory :group_order do
ordergroup { FactoryGirl.create(:user, groups: [FactoryGirl.create(:ordergroup)]).ordergroup }
end
end

View file

@ -0,0 +1,9 @@
require 'factory_girl'
FactoryGirl.define do
# requires order_article
factory :group_order_article do
end
end

31
spec/factories/order.rb Normal file
View file

@ -0,0 +1,31 @@
require 'factory_girl'
FactoryGirl.define do
factory :order do
starts { Time.now }
supplier { FactoryGirl.create :supplier, article_count: (article_count.nil? ? true : article_count) }
article_ids { supplier.articles.map(&:id) unless supplier.nil? }
ignore do
article_count true
end
# for an order from stock; need to add articles
factory :stock_order do
supplier_id 0
# article_ids needs to be supplied
end
# In the order's after_save callback order articles are created, so
# until the order is saved, these articles do not yet exist.
after :create do |order|
order.reload
end
end
# requires order and article
factory :order_article do
end
end

View file

@ -0,0 +1,21 @@
require 'factory_girl'
FactoryGirl.define do
factory :supplier do
name { Faker::Company.name.truncate(30) }
phone { Faker::PhoneNumber.phone_number }
address { Faker::Address.street_address }
ignore do
article_count 0
end
after :create do |supplier, evaluator|
article_count = evaluator.article_count
article_count = rand(1..99) if article_count == true
FactoryGirl.create_list :article, article_count, supplier: supplier
end
end
end

36
spec/factories/user.rb Normal file
View file

@ -0,0 +1,36 @@
require 'factory_girl'
FactoryGirl.define do
factory :user do
sequence(:nick) { |n| "user#{n}"}
first_name { Faker::Name.first_name }
email { Faker::Internet.email }
password { new_random_password }
factory :admin do
sequence(:nick) { |n| "admin#{n}" }
first_name 'Administrator'
after :create do |user, evaluator|
FactoryGirl.create :workgroup, role_admin: true, user_ids: [user.id]
end
end
end
factory :group do
sequence(:name) {|n| "Group ##{n}"}
factory :workgroup do
type ''
end
factory :ordergroup do
type 'Ordergroup'
sequence(:name) {|n| "Order group ##{n}"}
# workaround to avoid needing to save the ordergroup
# avoids e.g. error after logging in related to applebar
after :create do |group| Ordergroup.find(group.id).update_stats! end
end
end
end

12
spec/i18n_spec.rb Normal file
View file

@ -0,0 +1,12 @@
require 'spec_helper'
require 'i18n-spec'
Dir.glob('config/locales/*.yml').each do |locale_file|
describe "#{locale_file}" do
it_behaves_like 'a valid locale file', locale_file
# We're currently allowing both German and English as source language
# besides, we're using localeapp, so that it's ok if pull requests
# don't have this - a localapp pull will fix that right away.
#it { expect(locale_file).to be_a_subset_of 'config/locales/en.yml' }
end
end

View file

@ -0,0 +1,55 @@
require 'spec_helper'
describe 'settling an order', :type => :feature do
let(:admin) { FactoryGirl.create :user, groups:[FactoryGirl.create(:workgroup, role_finance: true)] }
let(:supplier) { FactoryGirl.create :supplier }
let(:article) { FactoryGirl.create :article, supplier: supplier, unit_quantity: 1 }
let(:order) { FactoryGirl.create :order, supplier: supplier, article_ids: [article.id] } # need to ref article
let(:go1) { FactoryGirl.create :group_order, order: order }
let(:go2) { FactoryGirl.create :group_order, order: order }
let(:oa) { order.order_articles.find_by_article_id(article.id) }
let(:goa1) { FactoryGirl.create :group_order_article, group_order: go1, order_article: oa }
let(:goa2) { FactoryGirl.create :group_order_article, group_order: go2, order_article: oa }
before do
goa1.update_quantities(3, 0)
goa2.update_quantities(1, 0)
oa.update_results!
order.finish!(admin)
goa1.reload
goa2.reload
end
it 'has correct order result' do
expect(oa.quantity).to eq(4)
expect(oa.tolerance).to eq(0)
expect(goa1.result).to eq(3)
expect(goa2.result).to eq(1)
end
describe :type => :feature, :js => true do
before { login admin }
before { visit new_finance_order_path(order_id: order.id) }
it 'has product ordered visible' do
expect(page).to have_content(article.name)
expect(page).to have_selector("#order_article_#{oa.id}")
end
it 'shows order result' do
click_link article.name
expect(page).to have_selector("#group_order_articles_#{oa.id}")
within("#group_order_articles_#{oa.id}") do
# make sure these ordergroup names are in the list for this product
expect(page).to have_content(go1.ordergroup.name)
expect(page).to have_content(go2.ordergroup.name)
# and that their order results match what we expect
expect(page).to have_selector("#group_order_article_#{goa1.id}_quantity")
expect(find("#group_order_article_#{goa1.id}_quantity").text.to_f).to eq(3)
expect(page).to have_selector("#group_order_article_#{goa2.id}_quantity")
expect(find("#group_order_article_#{goa2.id}_quantity").text.to_f).to eq(1)
end
end
end
end

View file

@ -0,0 +1,57 @@
require 'spec_helper'
describe 'product distribution', :type => :feature do
let(:admin) { FactoryGirl.create :admin }
let(:user_a) { FactoryGirl.create :user, groups: [FactoryGirl.create(:ordergroup)] }
let(:user_b) { FactoryGirl.create :user, groups: [FactoryGirl.create(:ordergroup)] }
let(:supplier) { FactoryGirl.create :supplier }
let(:article) { FactoryGirl.create :article, supplier: supplier, unit_quantity: 5 }
let(:order) { FactoryGirl.create(:order, supplier: supplier, article_ids: [article.id]) }
let(:oa) { order.order_articles.first }
describe :type => :feature do
# make sure users have enough money to order
before do
[user_a, user_b].each do |user|
ordergroup = Ordergroup.find(user.ordergroup.id)
ordergroup.add_financial_transaction! 5000, 'for ordering', admin
end
end
it 'agrees to documented example', :js => true do
# gruppe a bestellt 2(3), weil sie auf jeden fall was von x bekommen will
login user_a
visit new_group_order_path(:order_id => order.id)
2.times { find("[data-increase_quantity='#{oa.id}']").click }
3.times { find("[data-increase_tolerance='#{oa.id}']").click }
find('input[type=submit]').click
expect(page).to have_selector('body')
# gruppe b bestellt 2(0)
login user_b
visit new_group_order_path(:order_id => order.id)
2.times { find("[data-increase_quantity='#{oa.id}']").click }
find('input[type=submit]').click
expect(page).to have_selector('body')
# gruppe a faellt ein dass sie doch noch mehr braucht von x und aendert auf 4(1).
login user_a
visit edit_group_order_path(order.group_order(user_a.ordergroup), :order_id => order.id)
2.times { find("[data-increase_quantity='#{oa.id}']").click }
2.times { find("[data-decrease_tolerance='#{oa.id}']").click }
find('input[type=submit]').click
expect(page).to have_selector('body')
# die zuteilung
order.finish!(admin)
oa.reload
# Endstand: insg. Bestellt wurden 6(1)
expect(oa.quantity).to eq(6)
expect(oa.tolerance).to eq(1)
# Gruppe a bekommt 3 einheiten.
goa_a = oa.group_order_articles.joins(:group_order).where(:group_orders => {:ordergroup_id => user_a.ordergroup.id}).first
expect(goa_a.result).to eq(3)
# gruppe b bekommt 2 einheiten.
goa_b = oa.group_order_articles.joins(:group_order).where(:group_orders => {:ordergroup_id => user_b.ordergroup.id}).first
expect(goa_b.result).to eq(2)
end
end
end

View file

@ -0,0 +1,21 @@
require 'spec_helper'
describe 'the session', :type => :feature do
let(:user) { FactoryGirl.create :user }
describe 'login page', :type => :feature do
it 'is accesible' do
get login_path
expect(response).to be_success
end
it 'logs me in' do
login user
expect(page).to_not have_selector('.alert-error')
end
it 'does not log me in with wrong password' do
login user.nick, 'XX'+user.password
expect(page).to have_selector('.alert-error')
end
end
end

View file

@ -0,0 +1,63 @@
require 'spec_helper'
describe 'supplier', :type => :feature do
let(:supplier) { FactoryGirl.create :supplier }
describe :type => :feature, :js => true do
let(:user) { FactoryGirl.create :user, groups:[FactoryGirl.create(:workgroup, role_suppliers: true)] }
before { login user }
it 'can be created' do
visit suppliers_path
click_on I18n.t('suppliers.index.action_new')
supplier = FactoryGirl.build :supplier
within('#new_supplier') do
fill_in 'supplier_name', :with => supplier.name
fill_in 'supplier_address', :with => supplier.address
fill_in 'supplier_phone', :with => supplier.phone
find('input[type="submit"]').click
end
expect(page).to have_content(supplier.name)
end
it 'is included in supplier list' do
supplier
visit suppliers_path
expect(page).to have_content(supplier.name)
end
end
describe :type => :feature, :js => true do
let(:article_category) { FactoryGirl.create :article_category }
let(:user) { FactoryGirl.create :user, groups:[FactoryGirl.create(:workgroup, role_article_meta: true)] }
before { login user }
it 'can visit supplier articles path' do
visit supplier_articles_path(supplier)
expect(page).to have_content(supplier.name)
expect(page).to have_content(I18n.t('articles.index.edit_all'))
end
it 'can create a new article' do
article_category.save!
visit supplier_articles_path(supplier)
click_on I18n.t('articles.index.new')
expect(page).to have_selector('form#new_article')
article = FactoryGirl.build :article, supplier: supplier, article_category: article_category
within('#new_article') do
fill_in 'article_name', :with => article.name
fill_in 'article_unit', :with => article.unit
select article.article_category.name, :from => 'article_article_category_id'
fill_in 'article_price', :with => article.price
fill_in 'article_unit_quantity', :with => article.unit_quantity
fill_in 'article_tax', :with => article.tax
fill_in 'article_deposit', :with => article.deposit
# "Element cannot be scrolled into view" error, js as workaround
#find('input[type="submit"]').click
page.execute_script('$("form#new_article").submit();')
end
expect(page).to have_content(article.name)
end
end
end

View file

@ -0,0 +1,47 @@
require 'spec_helper'
describe Article do
let(:supplier) { FactoryGirl.create :supplier }
let(:article) { FactoryGirl.create :article, supplier: supplier }
it 'has a unique name' do
article2 = FactoryGirl.build :article, supplier: supplier, name: article.name
expect(article2).to be_invalid
end
it 'computes the gross price correctly' do
article.deposit = 0
article.tax = 12
expect(article.gross_price).to eq((article.price * 1.12).round(2))
article.deposit = 1.20
expect(article.gross_price).to eq(((article.price + 1.20) * 1.12).round(2))
end
it 'gross price >= net price' do
expect(article.gross_price).to be >= article.price
end
it 'fc-price >= gross price' do
if article.gross_price > 0
expect(article.fc_price).to be > article.gross_price
else
expect(article.fc_price).to be >= article.gross_price
end
end
it 'knows when it is deleted' do
expect(supplier.deleted?).to be_false
supplier.mark_as_deleted
expect(supplier.deleted?).to be_true
end
it 'keeps a price history' do
expect(article.article_prices.all.map(&:price)).to eq([article.price])
oldprice = article.price
sleep 1 # so that the new price really has a later creation time
article.price += 1
article.save!
expect(article.article_prices.all.map(&:price)).to eq([article.price, oldprice])
end
end

View file

@ -0,0 +1,48 @@
require 'spec_helper'
describe GroupOrderArticle do
let(:user) { FactoryGirl.create :user, groups: [FactoryGirl.create(:ordergroup)] }
let(:order) { FactoryGirl.create(:order).reload }
let(:go) { FactoryGirl.create :group_order, order: order, ordergroup: user.ordergroup }
let(:goa) { FactoryGirl.create :group_order_article, group_order: go, order_article: order.order_articles.first }
it 'has zero quantity by default' do expect(goa.quantity).to eq(0) end
it 'has zero tolerance by default' do expect(goa.tolerance).to eq(0) end
it 'has zero result by default' do expect(goa.result).to eq(0) end
it 'is not ordered by default' do expect(GroupOrderArticle.ordered.where(:id => goa.id).exists?).to be_false end
it 'has zero total price by default' do expect(goa.total_price).to eq(0) end
describe do
let(:article) { FactoryGirl.create :article, supplier: order.supplier, unit_quantity: 1 }
let(:oa) { order.order_articles.create(:article => article) }
let(:goa) { FactoryGirl.create :group_order_article, group_order: go, order_article: oa }
it 'can be ordered by piece' do
goa.update_quantities(1, 0)
expect(goa.quantity).to eq(1)
expect(goa.tolerance).to eq(0)
end
it 'can be ordered in larger amounts' do
quantity, tolerance = rand(13..99), rand(0..99)
goa.update_quantities(quantity, tolerance)
expect(goa.quantity).to eq(quantity)
expect(goa.tolerance).to eq(tolerance)
end
it 'has a proper total price' do
quantity = rand(1..99)
goa.update_quantities(quantity, 0)
expect(goa.total_price).to eq(quantity * goa.order_article.price.fc_price)
end
it 'can unorder a product' do
goa.update_quantities(rand(1..99), rand(0..99))
goa.update_quantities(0, 0)
expect(goa.quantity).to eq(0)
expect(goa.tolerance).to eq(0)
end
end
end

View file

@ -0,0 +1,25 @@
require 'spec_helper'
describe GroupOrder do
let(:user) { FactoryGirl.create :user, groups: [FactoryGirl.create(:ordergroup)] }
let(:order) { FactoryGirl.create :order }
# the following two tests are currently disabled - https://github.com/foodcoops/foodsoft/issues/158
#it 'needs an order' do
# expect(FactoryGirl.build(:group_order, ordergroup: user.ordergroup)).to be_invalid
#end
#it 'needs an ordergroup' do
# expect(FactoryGirl.build(:group_order, order: order)).to be_invalid
#end
describe do
let(:go) { FactoryGirl.create :group_order, order: order, ordergroup: user.ordergroup }
it 'has zero price initially' do
expect(go.price).to eq(0)
end
end
end

47
spec/models/order_spec.rb Normal file
View file

@ -0,0 +1,47 @@
require 'spec_helper'
describe Order do
it 'needs a supplier' do
expect(FactoryGirl.build(:order, supplier: nil)).to be_invalid
end
it 'needs order articles' do
supplier = FactoryGirl.create :supplier, article_count: 0
expect(FactoryGirl.build(:order, supplier: supplier)).to be_invalid
end
it 'can be created' do
expect(FactoryGirl.build(:order, article_count: 1)).to be_valid
end
describe 'with articles' do
let(:order) { FactoryGirl.create :order }
it 'is open by default' do expect(order).to be_open end
it 'is not finished by default' do expect(order).to_not be_finished end
it 'is not closed by default' do expect(order).to_not be_closed end
it 'has valid order articles' do
order.order_articles.all.each {|oa| expect(oa).to be_valid }
end
it 'can be finished' do
# TODO randomise user
order.finish!(User.first)
expect(order).to_not be_open
expect(order).to be_finished
expect(order).to_not be_closed
end
it 'can be closed' do
# TODO randomise user
order.finish!(User.first)
order.close!(User.first)
expect(order).to_not be_open
expect(order).to be_closed
end
end
end

View file

@ -0,0 +1,16 @@
require 'spec_helper'
describe Supplier do
let(:supplier) { FactoryGirl.create :supplier }
it 'has a unique name' do
supplier2 = FactoryGirl.build :supplier, name: supplier.name
expect(supplier2).to be_invalid
end
it 'has valid articles' do
supplier = FactoryGirl.create :supplier, article_count: true
supplier.articles.all.each {|a| expect(a).to be_valid }
end
end

59
spec/models/user_spec.rb Normal file
View file

@ -0,0 +1,59 @@
require 'spec_helper'
describe User do
it 'is correctly created' do
user = FactoryGirl.create :user,
nick: 'johnnydoe', first_name: 'Johnny', last_name: 'DoeBar',
email: 'johnnydoe@foodcoop.test', phone: '+1234567890'
expect(user.nick).to eq('johnnydoe')
expect(user.first_name).to eq('Johnny')
expect(user.last_name).to eq('DoeBar')
expect(user.name).to eq('Johnny DoeBar')
expect(user.email).to eq('johnnydoe@foodcoop.test')
expect(user.phone).to eq('+1234567890')
end
describe 'does not have the role' do
let(:user) { FactoryGirl.create :user }
it 'admin' do expect(user.role_admin?).to be_false end
it 'finance' do expect(user.role_finance?).to be_false end
it 'article_meta' do expect(user.role_article_meta?).to be_false end
it 'suppliers' do expect(user.role_suppliers?).to be_false end
it 'orders' do expect(user.role_orders?).to be_false end
end
describe do
let(:user) { FactoryGirl.create :user, password: 'blahblah' }
it 'can authenticate with correct password' do
expect(User.authenticate(user.nick, 'blahblah')).to be_true
end
it 'can not authenticate with incorrect password' do
expect(User.authenticate(user.nick, 'foobar')).to be_nil
end
it 'can not set a password without matching confirmation' do
user.password = 'abcdefghij'
user.password_confirmation = 'foobarxyz'
expect(user).to be_invalid
end
it 'can set a password with matching confirmation' do
user.password = 'abcdefghij'
user.password_confirmation = 'abcdefghij'
expect(user).to be_valid
end
it 'has a unique nick' do
expect(FactoryGirl.build(:user, nick: user.nick, email: "x-#{user.email}")).to be_invalid
end
it 'has a unique email' do
expect(FactoryGirl.build(:user, email: "#{user.email}")).to be_invalid
end
end
describe 'admin' do
let(:user) { FactoryGirl.create :admin }
it 'default admin role' do expect(user.role_admin?).to be_true end
end
end

67
spec/spec_helper.rb Normal file
View file

@ -0,0 +1,67 @@
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require 'support/coverage' # needs to be first
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rails'
require 'capybara/rspec'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
#config.use_transactional_fixtures = true
# We use capybara with selenium, and need database_cleaner
config.before(:each) do
DatabaseCleaner.strategy = (example.metadata[:js] ? :truncation : :transaction)
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
config.include SessionHelper
end
module Faker
class Unit
class << self
def unit
['kg', '1L', '100ml', 'piece', 'bunch', '500g'].sample
end
end
end
end
# include default foodsoft scope in urls, so that *_path works
ActionDispatch::Integration::Runner.class_eval do
undef default_url_options
def default_url_options(options={})
{foodcoop: FoodsoftConfig.scope}.merge(options)
end
end

14
spec/support/coverage.rb Normal file
View file

@ -0,0 +1,14 @@
# optional test coverage
# needs to be loaded first, e.g. add a require at top of spec_helper
if ENV['COVERAGE']
require 'simplecov'
SimpleCov.start do
add_filter '/spec/'
add_filter '/test/'
add_group 'Models', '/app/models/'
add_group 'Controllers', '/app/controllers/'
add_group 'Helpers', '/app/helpers/'
add_group 'Documents', '/app/documents/'
add_group 'Libraries', '/lib/'
end
end

View file

@ -0,0 +1,17 @@
module SessionHelper
def login(user=nil, password=nil)
visit login_path
user = FactoryGirl.create :user if user.nil?
if user.instance_of? ::User
nick, password = user.nick, user.password
else
nick = user
end
fill_in 'nick', :with => nick
fill_in 'password', :with => password
find('input[type=submit]').click
end
end