Merge branch 'master' into allow-no-nickname

Conflicts:
	app/views/home/ordergroup.html.haml
	app/views/login/new_password.html.haml
	app/views/shared/_auto_complete_users.rhtml
	app/views/shared/memberships/_current_members.rhtml
	app/views/shared/memberships/_non_members.rhtml
This commit is contained in:
wvengen 2013-11-18 11:42:49 +01:00
commit 66ac3be81f
63 changed files with 428 additions and 438 deletions

11
Gemfile
View File

@ -31,18 +31,20 @@ gem 'client_side_validations'
gem 'client_side_validations-simple_form'
gem 'inherited_resources'
gem 'localize_input', git: "git://github.com/bennibu/localize_input.git"
gem 'wikicloth'
gem 'daemons'
gem 'twitter-bootstrap-rails'
gem 'simple-navigation'
gem 'simple-navigation-bootstrap'
gem 'meta_search'
gem 'acts_as_versioned', git: 'git://github.com/technoweenie/acts_as_versioned.git' # Use this instead of rubygem
gem 'acts_as_tree'
gem "rails-settings-cached", "0.2.4"
gem 'resque'
gem 'whenever', require: false # For defining cronjobs, see config/schedule.rb
# we use the git version of acts_as_versioned, and need to include it in this Gemfile
gem 'acts_as_versioned', git: 'git://github.com/technoweenie/acts_as_versioned.git'
gem 'foodsoft_wiki', path: 'lib/foodsoft_wiki'
group :production do
gem 'exception_notification'
end
@ -78,10 +80,9 @@ 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'
gem 'capybara'
# webkit and poltergeist don't seem to work yet
gem 'selenium-webdriver', '~> 2.35.1'
gem 'selenium-webdriver'
gem 'database_cleaner'
gem 'simplecov', require: false
# need to include rspec components before i18n-spec or rake fails in test environment

View File

@ -18,6 +18,14 @@ GIT
acts_as_versioned (0.6.0)
activerecord (>= 3.0.9)
PATH
remote: lib/foodsoft_wiki
specs:
foodsoft_wiki (0.0.1)
acts_as_versioned
rails (~> 3.2.15)
wikicloth
GEM
remote: https://rubygems.org/
specs:
@ -251,7 +259,7 @@ GEM
rspec-mocks (~> 2.14.0)
ruby-prof (0.13.0)
ruby-rc4 (0.1.5)
rubyzip (0.9.9)
rubyzip (1.0.0)
sass (3.2.12)
sass-rails (3.2.6)
railties (~> 3.2.0)
@ -259,10 +267,10 @@ GEM
tilt (~> 1.3)
select2-rails (3.5.0)
thor (~> 0.14)
selenium-webdriver (2.35.1)
selenium-webdriver (2.37.0)
childprocess (>= 0.2.5)
multi_json (~> 1.0)
rubyzip (< 1.0.0)
rubyzip (~> 1.0.0)
websocket (~> 1.0.4)
simple-navigation (3.11.0)
activesupport (>= 2.3.2)
@ -308,9 +316,9 @@ GEM
rails (>= 3.1)
railties (>= 3.1)
tzinfo (0.3.38)
uglifier (2.2.1)
uglifier (2.3.0)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
json (>= 1.8.0)
uniform_notifier (1.3.0)
vegas (0.1.11)
rack (>= 1.0.0)
@ -336,7 +344,7 @@ DEPENDENCIES
bullet
capistrano (= 2.13.5)
capistrano-ext
capybara (~> 2.1.0)
capybara
client_side_validations
client_side_validations-simple_form
coffee-rails (~> 3.2.1)
@ -345,6 +353,7 @@ DEPENDENCIES
exception_notification
factory_girl_rails (~> 4.0)
faker
foodsoft_wiki!
haml-rails
i18n-js!
i18n-spec
@ -369,7 +378,7 @@ DEPENDENCIES
ruby-prof
sass-rails (~> 3.2.3)
select2-rails
selenium-webdriver (~> 2.35.1)
selenium-webdriver
simple-navigation
simple-navigation-bootstrap
simple_form
@ -380,4 +389,3 @@ DEPENDENCIES
twitter-bootstrap-rails
uglifier (>= 1.0.3)
whenever
wikicloth

View File

@ -7,6 +7,8 @@ FoodSoft
Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
A food cooperative is a group of people that buy food from suppliers of their own choosing. A collective do-it-yourself supermarket. Members order their products online and collect them on a specified day. And all put in a bit of work to make that possible. Foodsoft facilitates the process.
If you're a food coop considering to use foodsoft, please have a look at the [wiki page for foodcoops](https://github.com/foodcoops/foodsoft/wiki/For-foodcoops). When you'd like to experiment with or develop foodsoft, you can read [how to set it up](https://github.com/foodcoops/foodsoft/blob/master/doc/SETUP_DEVELOPMENT.md) on your own computer.
More information about using this software and contributing can be found on the [wiki](https://github.com/foodcoops/foodsoft/wiki).

View File

@ -65,7 +65,7 @@ class GroupOrdersController < ApplicationController
private
# Returns true if @current_user is member of an Ordergroup.
# Used as a :before_filter by OrderingController.
# Used as a :before_filter by OrdersController.
def ensure_ordergroup_member
@ordergroup = @current_user.ordergroup
if @ordergroup.nil?

View File

@ -1,33 +0,0 @@
class MembershipsController < ApplicationController
before_filter :authenticate_membership_or_admin
def add_member
user = User.find(params[:user_id])
Membership.create(:group => @group, :user => user)
redirect_to :action => 'reload', :id => @group
end
def drop_member
begin
Membership.find(params[:membership_id]).destroy
if User.find(@current_user.id).role_admin?
redirect_to :action => 'reload', :id => @group
else
# If the user drops himself from admin group
flash[:notice] = MESG_NO_ADMIN_ANYMORE
render(:update) {|page| page.redirect_to :controller => "index"}
end
rescue => error
flash[:error] = error.to_s
redirect_to :action => 'reload', :id => @group
end
end
def reload
render :update do |page|
page.replace_html 'members', :partial => 'shared/memberships/current_members', :object => @group
page.replace_html 'non_members', :partial => 'shared/memberships/non_members', :object => @group
end
end
end

View File

@ -48,6 +48,9 @@ class OrdersController < ApplicationController
end
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
end
format.text do
send_data text_fax_template, filename: @order.name+'.txt', type: 'text/plain'
end
end
end
@ -101,12 +104,14 @@ class OrdersController < ApplicationController
rescue => error
redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message)
end
protected
# Renders the fax-text-file
# e.g. for easier use with online-fax-software, which don't accept pdf-files
# TODO move to text template
def text_fax_template
order = Order.find(params[:id])
supplier = order.supplier
supplier = @order.supplier
contact = FoodsoftConfig[:contact].symbolize_keys
text = I18n.t('orders.fax.heading', :name => FoodsoftConfig[:name])
text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" unless supplier.customer_number.blank?
@ -117,15 +122,13 @@ class OrdersController < ApplicationController
text += "****** " + I18n.t('orders.fax.articles') + "\n\n"
text += I18n.t('orders.fax.number') + " " + I18n.t('orders.fax.amount') + " " + I18n.t('orders.fax.name') + "\n"
# now display all ordered articles
order.order_articles.ordered.all(:include => [:article, :article_price]).each do |oa|
@order.order_articles.ordered.all(:include => [:article, :article_price]).each do |oa|
number = oa.article.order_number
(8 - number.size).times { number += " " }
quantity = oa.units_to_order.to_i.to_s
quantity = " " + quantity if quantity.size < 2
text += "#{number} #{quantity} #{oa.article.name}\n"
end
send_data text,
:type => 'text/plain; charset=utf-8; header=present',
:disposition => "attachment; filename=#{order.name}"
text
end
end

View File

@ -78,14 +78,14 @@ module ApplicationHelper
# When the 'short' option is true, abbreviations will be used:
# When there is a non-empty model attribute 'foo', it looks for
# the model attribute translation 'foo_short' and use that as
# heading, with an acronym title of 'foo'.
# heading, with an abbreviation title of 'foo'.
# Other options are passed through to I18n.
def heading_helper(model, attribute, options = {})
i18nopts = options.select {|a| !['short'].include?(a) }
s = model.human_attribute_name(attribute, i18nopts)
if options[:short]
sshort = model.human_attribute_name("#{attribute}_short".to_sym, options.merge({defaults: ''}))
s = raw "<acronym title='#{s}'>#{sshort}</acronym>" unless sshort.empty?
s = raw "<abbr title='#{s}'>#{sshort}</abbr>" unless sshort.empty?
end
s
end

View File

@ -106,7 +106,7 @@ class Article < ActiveRecord::Base
# to get the correspondent shared article
def shared_article
@shared_article ||= self.supplier.shared_supplier.shared_articles.find_by_number(self.order_number)
@shared_article ||= self.supplier.shared_supplier.shared_articles.find_by_number(self.order_number) rescue nil
end
# convert units in foodcoop-size

View File

@ -90,7 +90,8 @@ class OrderArticle < ActiveRecord::Base
end
def ordered_quantities_equal_to_group_orders?
(units_to_order * price.unit_quantity) == group_orders_sum[:quantity]
# the rescue is a workaround for units_to_order not being defined in integration tests
(units_to_order * price.unit_quantity) == group_orders_sum[:quantity] rescue false
end
# Updates order_article and belongings during balancing process

View File

@ -17,7 +17,6 @@ class User < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :tasks, :through => :assignments
has_many :send_messages, :class_name => "Message", :foreign_key => "sender_id"
has_many :pages, :foreign_key => 'updated_by'
has_many :created_orders, :class_name => 'Order', :foreign_key => 'created_by_user_id', :dependent => :nullify
attr_accessor :password, :settings_attributes

View File

@ -1,3 +1,3 @@
%tr.edit_inline{:id=> "edit_"+@article.id.to_s}
%td{:colspan=>"10"}
= t('.note', article: h(@article.name), drop_link: link_to(t('.drop'), :controller => 'orders', :action => 'edit', :id => @order)).html_safe
= t('.note', article: h(@article.name), drop_link: link_to(t('.drop'), edit_order_path(@order))).html_safe

View File

@ -9,7 +9,14 @@
// create List for search-feature (using list.js, http://listjs.com)
var listjsResetPlugin = ['reset', {highlightClass: 'btn-primary'}];
var listjsDelayPlugin = ['delay', {delayedSearchTime: 500}];
new List(document.body, { valueNames: ['name'], engine: 'unlist', plugins: [listjsResetPlugin, listjsDelayPlugin] });
new List(document.body, {
valueNames: ['name'],
engine: 'unlist',
plugins: [listjsResetPlugin, listjsDelayPlugin],
// make large pages work too (as we don't have paging - articles may disappear!)
page: 10000,
indexAsync: true
});
});
- title t('.title'), false

View File

@ -4,7 +4,7 @@
%li.nav-header= t '.foodcoop'
%li= link_to t('.members'), foodcoop_users_path
%li= link_to t('.tasks'), user_tasks_path
%li= link_to t('.write_message'), :controller => "messages", :action => "new"
%li= link_to t('.write_message'), new_message_path
- has_ordergroup = !@current_user.ordergroup.nil?
- has_orders_role = @current_user.role_orders?

View File

@ -4,16 +4,14 @@
.span4
%h2= @ordergroup.name
.well
- unless @ordergroup.description.blank?
%p= @ordergroup.description
%p
%b= t '.description'
= @ordergroup.description
%p
%b= t '.funds'
%b= Ordergroup.human_attribute_name(:available_funds) + ':'
= number_to_currency(@ordergroup.get_available_funds())
%h2= t '.people'
%ul
- for membership in @ordergroup.memberships
%li= show_user membership.user
%p
%b= Ordergroup.human_attribute_name(:user_tokens) + ':'
= @ordergroup.memberships.map{|m| show_user m.user}.join(', ')
= link_to t('.invite'), new_invite_path(:id => @ordergroup), :remote => true, class: 'btn btn-primary'
.span8
%h2= t('.account_summary')

View File

@ -5,7 +5,7 @@
%h3
= h(t('.user.title', user: show_user))
%small= t '.user.since', when: distance_of_time_in_words(Time.now, @current_user.created_on)
= simple_form_for(@current_user, :url => { :action => 'update_profile'}) do |f|
= simple_form_for(@current_user, :url => update_profile_path) do |f|
= render :partial => 'shared/user_form_fields', :locals => {:f => f}
.form-actions
= submit_tag t('ui.save'), class: 'btn'

View File

@ -1,32 +0,0 @@
!!!
%html
%head
%meta{"http-equiv" => "content-type", :content => "text/html;charset=UTF-8"}
%title= t '.title', title: (yield(:title) or controller.controller_name)
= stylesheet_link_tag 'application'
= stylesheet_link_tag "print", :media => "print"
<!--[if lte IE 7]>
= stylesheet_link_tag 'ie_hacks'
<![endif]-->
= javascript_include_tag 'application'
= csrf_meta_tags
= yield(:head)
%body
#logininfo= render :partial => 'shared/loginInfo'
#header
#logo
= link_to root_path do
= t('layouts.logo').html_safe
%span{:style => "color:white; font-size:45%; letter-spacing: -1px;"}= FoodsoftConfig[:name]
#nav= render :partial => 'layouts/main_tabnav'
#main
#content
- flash.each do |name, msg|
= content_tag :h3, msg, :id => "flash#{name.to_s.camelize}", :class => "flash #{name}"
#loader{:style => "display:none;"}= image_tag("loader.gif", :border => 0)
- if show_title?
%h1= yield(:title)
= yield
#ajax_box(style="display:none")

View File

@ -1,6 +1,6 @@
- title t('.title')
= t('.body').html_safe
= simple_form_for User.new, url: {action: 'reset_password'} do |form|
= simple_form_for User.new, url: reset_password_path do |form|
= form.input :email
.form-actions
= form.submit t('.submit'), class: 'btn'

View File

@ -1,6 +1,6 @@
- title t('.title')
= raw t('.body', user: h(show_user(@user)))
= simple_form_for @user, :url => {:action => 'update_password', :id => @user.id, :token => @user.reset_password_token} do |form|
= simple_form_for @user, :url => update_password_path(@user.id, :token => @user.reset_password_token) do |form|
= form.input :password
= form.input :password_confirmation
.form-actions

View File

@ -50,7 +50,7 @@
%li= order_pdf(@order, :articles, t('.download.article_pdf'))
%li= order_pdf(@order, :matrix, t('.download.matrix_pdf'))
%li= order_pdf(@order, :fax, t('.download.fax_pdf'))
%li= link_to t('.download.fax_txt'), {action: 'text_fax_template', id: @order }, {title: t('.download.download_file')}
%li= link_to t('.download.fax_txt'), order_path(@order, format: :txt), {title: t('.download.download_file')}
%section#articles_table
= render 'articles', order: @order

View File

@ -1,4 +0,0 @@
%ul.autocomplete
- for article in @articles
- supplier = @supplier ? "" : " - #{truncate(article.supplier.name)}"
%li{:id => article.id.to_s}= "#{article.name} (#{article.unit_quantity} * #{article.unit} | #{number_to_currency(article.price)}#{supplier})"

View File

@ -1,5 +0,0 @@
<ul class="autocomplete">
<% for user in @users do -%>
<li><span class="nick"><%= show_user(user) %></span><span class="informal"> (<%= user.ordergroup.try(:name) %>)</span></li>
<% end -%>
</ul>

View File

@ -1,9 +0,0 @@
%ul
%li
= image_tag 'b_user.png' , :size => '7x10', :border => 0, :alt => t('.profile')
= link_to h(@current_user.nick), my_profile_path, { :title => t('.edit_profile') }
- if FoodsoftConfig[:homepage]
%li= link_to FoodsoftConfig[:name], FoodsoftConfig[:homepage], { :title => t('.homepage_title') }
%li= link_to t('.help'), FoodsoftConfig[:help_url]
%li= link_to t('.feedback.title'), new_feedback_path, :title => t('.feedback.desc')
%li= link_to t('.logout'), logout_path

View File

@ -1,21 +0,0 @@
<% if flash[:error] %>
<h3 class="error" id="flashError" ><%= flash[:error] %></h3>
<%= javascript_tag("new Effect.Highlight('flashError', {delay:0.8, duration:1});") -%>
<% end %>
<% memberships = @group.memberships
if memberships.size != 0 %>
<ul style="">
<% for membership in memberships %>
<li style="margin-left:-15px">
<%= show_user membership.user, full: true, markup: true %>
| <%= link_to_remote t('.drop'),
:url => { :controller => '/memberships', :action => 'drop_member', :id => @group, :membership_id => membership },
:before => "Element.show('loader')",
:success => "Element.hide('loader')" %>
</li>
<% end %>
</ul>
<% else %>
<p><i><%= t('.no_members', group: @group.name) %></i></p>
<% end %>

View File

@ -1,23 +0,0 @@
<h1><%=h t('.title', group: @group.name) %></h1>
<p>
<i><%= t('.desc', link: remote_link_to(t('.invite'), :url => new_invite_path(:id => @group))).html_safe %></i>
</p>
<div class="left_column" style="width:48%">
<div class="box_title">
<h2><%= t('.already_members') %></h2>
</div>
<div class="column_content" id="members">
<%=render :partial => 'shared/memberships/current_members' %>
</div>
</div>
<div class="right_column" style="width:48%">
<div class="box_title">
<h2><%= t('.no_members_yet') %></h2>
</div>
<div class="column_content" id="non_members">
<%= render :partial => 'shared/memberships/non_members' %>
<%= remote_link_to(t('.invite_someone'), :url => new_invite_path(:id => @group)) %>
</div>
</div>
<div id="edit_box" style="display:none"></div>

View File

@ -1,11 +0,0 @@
<ul>
<% for user in @group.non_members %>
<li>
<%= show_user user, full: true, markup: true %>
| <%= link_to_remote t('.add'),
:url => { :controller => '/memberships', :action => 'add_member', :id => @group, :user_id => user },
:before => "Element.show('loader')",
:success => "Element.hide('loader')" %>
</li>
<% end %>
</ul>

View File

@ -13,5 +13,5 @@
- @tasks.each do |task|
%tr
%td= task.due_date unless task.due_date.nil?
%td= link_to t('.task_format', name: task.name, duration: task.duration), :controller => "tasks", :action => "show", :id => task
%td= link_to t('.task_format', name: task.name, duration: task.duration), task_path(task)
%td= task_assignments task

View File

@ -853,10 +853,7 @@ de:
no_ordergroups: Leider bist Du kein Mitglied einer Bestellgruppe
ordergroup:
account_summary: Kontoauszug
description: Beschreibung
funds: ! 'Verfügbares Guthaben:'
invite: Neue Person einladen
people: Personen
search: Suchen ...
title: Meine Bestellgruppe
ordergroup_cancelled: Du bist jetzt kein Mitglied der Gruppe %{group} mehr.
@ -906,8 +903,6 @@ de:
ordering:
confirm_change: Änderungen an dieser Bestellung gehen verloren, wenn zu einer anderen Bestellung gewechselt wird. Möchtest Du trotzdem wechseln?
layouts:
application1:
title: Foodsoft - %{title}
email:
footer: ! '--
@ -1367,28 +1362,6 @@ de:
search_user: Nach Nutzerin suchen
title: Wöchentliche Jobs
user_not_found: Keine Nutzerin gefunden
loginInfo:
edit_profile: Profil bearbeiten
feedback:
desc: Fehler gefunden? Vorschlag? Idee? Kritik?
title: Feedback
help: Hilfe
homepage_title: Foodcoop Homepage besuchen
logout: Abmelden
profile: Profil
memberships:
current_members:
drop: entfernen
no_members: ! '%{group} hat keine Mitglieder.'
members:
already_members: Sind schon Mitglieder
desc: Hier kannst Du Mitglieder der Gruppe verwalten oder ein neues Foodcoop-Mitglied in die Gruppe %{link}.
invite: einladen
invite_someone: Person einladen
no_members_yet: Sind noch keine Mitglieder
title: Mitglieder von %{group}
non_members:
add: hinzufügen
open_orders:
ending: Ende
no_open_orders: Derzeit gibt es keine laufenden Bestellungen
@ -1416,7 +1389,6 @@ de:
name: Bitte ändern
edit_stock_article:
price: <ul><li>Preisänderung gesperrt.</li><li>Bei Bedarf %{stock_article_copy_link}.</li></ul>
supplier:
supplier:
min_order_quantity: Die Mindestbestellmenge wird während der Bestellung angezeigt und soll motivieren
task:

View File

@ -646,7 +646,7 @@ en:
title: Invoice %{number}
order_articles:
edit:
stock_alert:
stock_alert: The price of stock articles cannot be changed!
title: Update article
new:
title: Add delivered article to order
@ -858,10 +858,7 @@ en:
no_ordergroups: You are unfortunately not a member of an ordergroup.
ordergroup:
account_summary: Account Statement
description: description
funds: ! 'Available credit:'
invite: Invite a new Person
people: People
search: Search ...
title: My ordergroup
ordergroup_cancelled: You cancelled membership of the group %{group}.
@ -911,8 +908,6 @@ en:
ordering:
confirm_change: Modifications to this order will be lost when you change the order. Do you want to lose the changes you made and continue?
layouts:
application1:
title: Foodsoft - %{title}
email:
footer: ! '--
@ -1372,28 +1367,6 @@ en:
search_user: Search user
title: Weekly jobs
user_not_found: No user found
loginInfo:
edit_profile: Edit profile
feedback:
desc: Found a bug? Suggestions? Review?
title: Feedback
help: Help
homepage_title: Visit Foodcoop Homepage
logout: Logout
profile: Profile
memberships:
current_members:
drop: remove
no_members: ! '%{group} has no members.'
members:
already_members: Are already members
desc: Here you can manage members of the group or invite a new Foodcoop-member to the group %{link}.
invite: invite
invite_someone: Invite someone
no_members_yet: Are not members yet
title: Members of %{group}
non_members:
add: add
open_orders:
ending: Ending
no_open_orders: There are no current orders
@ -1421,7 +1394,6 @@ en:
name: Please modify
edit_stock_article:
price: <ul><li>Price changes are forbidden.</li><li>If necessary, %{stock_article_copy_link}.</li></ul>
supplier:
supplier:
min_order_quantity: The minimum amount which has to be orderd will be shown during the order process and should motivate ordering
task:

View File

@ -879,10 +879,7 @@ fr:
no_ordergroups: Tu ne fais encore partie d'aucune cellule
ordergroup:
account_summary: Relevé de compte
description: Description
funds: ! 'Crédit disponible:'
invite: Engrainer une nouvelle personne
people: Personnes
search: Rechercher ...
title: Ta cellule
ordergroup_cancelled: Tu ne fais plus partie de la cellule %{group}.
@ -932,8 +929,6 @@ fr:
ordering:
confirm_change:
layouts:
application1:
title: Foodsoft - %{title}
email:
footer: ! '--
@ -1374,28 +1369,6 @@ fr:
search_user: Rechercher par utilisatrice
title: Boulots hebdomadaires
user_not_found: Aucune utilisatrice n'a été trouvée.
loginInfo:
edit_profile: Modifier le profil
feedback:
desc: Tu as détecté une erreur? Tu as des propositions, des idées, des critiques?
title: Retours
help: Aide
homepage_title: Vers la page d'accueil de la bouffecoop
logout: Te déconnecter
profile: Profil
memberships:
current_members:
drop: désinscrire
no_members: ! '%{group} n''a aucun membre pour le moment.'
members:
already_members: Sont déjà membre
desc: Sur cette page, tu peux gérer les membres de l'équipe, et aussi %{link} un nouveau membre.
invite: engrainer
invite_someone: Inviter quelqu'unE
no_members_yet: Ne sont pas encore membre
title: Membre de %{group}
non_members:
add: ajouter
open_orders:
ending: Clôture le
no_open_orders: Il n'y a aucune commande en cours en ce moment
@ -1423,7 +1396,6 @@ fr:
name: Merci de modifier
edit_stock_article:
price: <ul><li>Modification du prix enregistrée. </li><li>Si nécessaire %{stock_article_copy_link}.</li></ul>
supplier:
supplier:
min_order_quantity: La quantité minimum à commander est affichée pendant la commande et doit motiver
task:

View File

@ -26,15 +26,15 @@ nl:
description: Omschrijving
name: Naam
delivery:
delivered_on:
note:
supplier:
delivered_on: Leverdatum
note: Notitie
supplier: Leverancier
financial_transaction:
amount: bedrag
note: notitie
group_order_article:
ordergroup_id:
result:
ordergroup_id: Huishouden
result: Hoeveelheid
invoice:
amount: Bedrag
date: Factuurdatum
@ -47,12 +47,12 @@ nl:
paid_on: Betaald op
supplier: Leverancier
message:
body:
group_id:
private:
recipient_tokens:
sent_to_all:
subject:
body: Bericht
group_id: Groep
private: Privé
recipient_tokens: Geadresseerden
sent_to_all: Aan alle leden sturen
subject: Onderwerp
order:
ends: Eindigt op
note: Notitie
@ -71,22 +71,22 @@ nl:
name: Naam
user_tokens: Leden
page:
body:
body: Bericht
parent_id:
title:
title: Titel
stock_article:
price: Prijs
quantity: Aantal
quantity_available: Beschikbaar
supplier: Leverancier
stock_taking:
date:
note:
date: Datum
note: Notitie
supplier:
address: Adres
contact_person: Contactpersoon
customer_number: Klantnummer
customer_number_short:
customer_number_short: Klantnr.
delivery_days: Bezorgdagen
email: Email
fax: Fax
@ -99,14 +99,14 @@ nl:
phone2: Telefoon 2
url: Homepage
task:
description:
done:
due_date:
duration:
name:
required_users:
user_list:
workgroup:
description: Beschrijving
done: Gedaan?
due_date: Voor wanneer?
duration: Tijdsduur
name: Naam
required_users: Aantal
user_list: Verantwoordelijken
workgroup: Werkgroep
user:
email: Email
first_name: Voornaam
@ -123,7 +123,7 @@ nl:
workgroup:
description: Omschrijving
name: Naam
next_weekly_tasks_number:
next_weekly_tasks_number: Hoeveel weken vooruit moet de taak zichtbaar zijn?
role_admin: Beheer
role_article_meta: Artikelen
role_finance: Financiën
@ -297,7 +297,7 @@ nl:
error_parse: ! '%{msg} ... in regel %{line}'
error_update: ! 'Er trad een fout op bij het bijwerken van artikel ''%{article}'': %{msg}'
parse_upload:
notice: ! '%{count} artikel zijn geanalyseerd'
notice: ! '%{count} artikelen zijn geanalyseerd'
sync:
notice: Catalogus is bijgewerkt
shared_alert: ! '%{supplier} is niet aan een externe database gekoppeld'
@ -393,9 +393,9 @@ nl:
title:
form:
actions:
article:
category:
create_from_blank:
article: Artikel
category: Categorie
create_from_blank: Nieuwe artikel invoeren
create_stock_article:
price:
quantity:
@ -639,7 +639,7 @@ nl:
title: Factuur %{number}
order_articles:
edit:
stock_alert:
stock_alert: De prijs van voorraadartikelen kan niet aangepast worden!
title: Artikel bijwerken
new:
title: Geleverd artikel aan bestelling toevoegen
@ -850,10 +850,7 @@ nl:
no_ordergroups: Jammergenoeg ben je niet aangesloten bij een huishouden.
ordergroup:
account_summary: Rekeningafschrift
description: Omschrijving
funds: ! 'Beschikbaar krediet:'
invite: Iemand uitnodigen
people: Personen
search: Zoeken ...
title: Mijn huishouden
ordergroup_cancelled: Je bent geen lid meer van de groep %{group}.
@ -903,8 +900,6 @@ nl:
ordering:
confirm_change:
layouts:
application1:
title: Foodsoft - %{title}
email:
footer: ! '--
@ -1279,7 +1274,7 @@ nl:
price: Totaalprijs
articles_by_groups:
fc_price: FC-Prijs
fc_price_desc: Prijs inclusief belasting, borg en foodcoop-toeslag
fc_price_desc: Prijs inclusief belasting, statiegeld en foodcoop marge
name: Naam
price: Totaalprijs
unit: Eenheid
@ -1301,28 +1296,6 @@ nl:
search_user: Gebruiker zoeken
title: Wekelijkse taken
user_not_found: Geen gebruiker gevonden
loginInfo:
edit_profile: Profiel aanpassen
feedback:
desc: Fout gevonden? Opmerking? Idee?
title: Feedback
help: Help
homepage_title: Foodcoop startpagina bezoeken
logout: Uitloggen
profile: Profiel
memberships:
current_members:
drop: verwijderen
no_members: ! '%{group} heeft geen leden.'
members:
already_members: Zijn al lid
desc:
invite: uitnodiging
invite_someone: Iemand uitnodigen
no_members_yet: Zijn nog geen lid
title: Leden van %{group}
non_members:
add: toevoegen
open_orders:
ending: Einde
no_open_orders: Er zijn momenteel geen lopende bestellingen.
@ -1350,7 +1323,6 @@ nl:
name:
edit_stock_article:
price:
supplier:
supplier:
min_order_quantity:
task:

View File

@ -3,6 +3,11 @@
SimpleNavigation::Configuration.run do |navigation|
# allow engines to add to the menu - https://gist.github.com/mjtko/4873ee0c112b6bd646f8
engines = Rails.application.railties.engines.select { |e| e.respond_to?(:navigation) }
# to include an engine but keep it from modifying the menu:
#engines.reject! { |e| e.instance_of? FoodsoftMyplugin::Engine }
navigation.items do |primary|
primary.dom_class = 'nav'
@ -16,11 +21,6 @@ SimpleNavigation::Configuration.run do |navigation|
subnav.item :tasks, I18n.t('navigation.tasks'), tasks_path, id: nil
end
primary.item :wiki, I18n.t('navigation.wiki.title'), '#', id: nil do |subnav|
subnav.item :wiki_home, I18n.t('navigation.wiki.home'), wiki_path, id: nil
subnav.item :all_pages, I18n.t('navigation.wiki.all_pages'), all_pages_path, id: nil
end
primary.item :orders, I18n.t('navigation.orders.title'), '#', id: nil do |subnav|
subnav.item :ordering, I18n.t('navigation.orders.ordering'), group_orders_path, id: nil
subnav.item :ordering_archive, I18n.t('navigation.orders.archive'), archive_group_orders_path, id: nil
@ -47,6 +47,8 @@ SimpleNavigation::Configuration.run do |navigation|
subnav.item :ordergroups, I18n.t('navigation.admin.ordergroups'), admin_ordergroups_path, id: nil
subnav.item :workgroups, I18n.t('navigation.admin.workgroups'), admin_workgroups_path, id: nil
end
engines.each { |e| e.navigation(primary, self) }
end
end

View File

@ -18,27 +18,20 @@ Foodsoft::Application.routes.draw do
match '/login' => 'sessions#new', :as => 'login'
match '/logout' => 'sessions#destroy', :as => 'logout'
get '/login/forgot_password' => 'login#forgot_password', as: :forgot_password
get '/login/new_password' => 'login#new_password', as: :new_password
get '/login/forgot_password' => 'login#forgot_password', as: :forgot_password
post '/login/reset_password' => 'login#reset_password', as: :reset_password
get '/login/new_password' => 'login#new_password', as: :new_password
get '/login/update_password' => 'login#update_password', as: :update_password
match '/login/accept_invitation/:token' => 'login#accept_invitation', as: :accept_invitation
resources :sessions, :only => [:new, :create, :destroy]
########### User specific
match '/home/profile' => 'home#profile', :as => 'my_profile'
put '/home/update_profile' => 'home#update_profile', :as => 'update_profile'
match '/home/ordergroup' => 'home#ordergroup', :as => 'my_ordergroup'
match '/home/cancel_membership' => 'home#cancel_membership', :as => 'cancel_membership'
############ Wiki
resources :pages do
get :all, :on => :collection
get :version, :on => :member
get :revert, :on => :member
end
match '/wiki/:permalink' => 'pages#show', :as => 'wiki_page' # , :constraints => {:permalink => /[^\s]+/}
match '/wiki' => 'pages#show', :defaults => {:permalink => 'Home'}, :as => 'wiki'
############ Orders, ordering
resources :orders do
@ -191,8 +184,5 @@ Foodsoft::Application.routes.draw do
resources :users, :only => [:index]
# TODO: This is very error prone. Better deactivate this catch all route
match ':controller(/:action(/:id))(.:format)'
end # End of /:foodcoop scope
end

View File

@ -0,0 +1,24 @@
# This migration comes from foodsoft_wiki_engine (originally 20090325175756)
class CreatePages < ActiveRecord::Migration
def self.up
create_table :pages do |t|
t.string :title
t.text :body
t.string :permalink
t.integer :lock_version, :default => 0
t.integer :updated_by
t.integer :redirect
t.integer :parent_id
t.timestamps
end
add_index :pages, :title
add_index :pages, :permalink
Page.create_versioned_table # Automaticly creates pages_versions table
end
def self.down
drop_table :pages
Page.drop_versioned_table
end
end

93
doc/BESTELLEN.md Normal file
View File

@ -0,0 +1,93 @@
# Bestellen
Das Bestellen ist der Hauptteil dieser Software und ein wenig kompliziert.
Hier starte ich den Versuch die Programmlogik in Text umzusetzen und
verweise auf die enstprechenden Controller bzw. Modelle.
Der relevante Controller ist `OrdersController`.
## Bestellung "in Netz stellen"
Darunter verstehen wir die Auswahl von Artikeln eines bestimmten Lieferanten fuer eine zeitlich begrenzte
Bestellung im Internet. Die relevanten Methoden sind `OrdersController#newOrder` und folgende.
Jede Bestellung wird durch die Klasse Order abgebildet.
Die zugehoerigen Artikel werden duch die Klasse `OrderArticle` mit den Artikeln verknuepft.
Dabei werden auch die Attribute `quantity`, `tolerance` und `quantity_to_order` gespeichert.
Diese Mengen repraesentieren die Gesamtbestellung, also alle Bestellgruppen.
## Eine Bestellgruppe bestellt...
Die Methode `OrdersController#order` schickt uns die Bestellenseite. Mit dieser
Oberflaeche koennen die Bestellgruppena die vorher ausgewaehlten Artikel
bestellen. Mittels den Buttons werden dabei live, also clientseitig, die
Preise ermittelt und der Gesamtpreis berechnet. Ist der Gesamtpreis groeßer als
der aktuelle Gruppenkontostand, so wird die Preisspalte rot unterlegt und die
Bestellung kann nicht gespeichert werden.
## (gruppen)-Bestellung wird gespeichert
Die Gruppenbestellung wird durch die Tabelle `group_oders` (`GroupOrder`)
abgebildet, bzw. die Bestellung und Bestellgruppe wird dort verknuepft.
Die bestellten Artikel der Bestellgruppe werden durch die Tabelle `group_order_articles`
(`GroupOrderArticle`) registriert. Dort werden nun die Modelle GroupOrder
und OrderArticle miteinander verbunden.
Bei jeder Bestellung wird außerdem die Summe der Menge, Toleranz in `GroupOrderArticle`
abgelegt. Allerdings muss jede Aenderung dieser Mengen mit protokolliert werden.
Dies ist wichtig, weil spaeter die Zuteilung der Einheiten pro bestellten Artikel
nach der chronologischen Reihenfolge erfolgt. (s.u.)
Das passiert dann in der Tabelle `group_order_article_quantities`
(`GroupOrderArticleQuantity`).
## Aenderunug einer Bestellung
Knifflig ist die Aenderung einer gruppenbestellung, weil die zeitliche
Reihenfolge dabei nicht durcheinander geraten darf.
Wir unterscheiden dehalb zwei Faelle:
### Erhoehe die Menge des Arikels.
Jetzt wird eine Zeile in `group_order_article_quantities` angelegt.
und zwar mit genau den Mengen, die zusatzlich bestellt wurden.
Quantity und Tolerance funktionieren analog.
Beispiel:
* Urspruenglich bestellt: 2(2) um 17uhr.
* Erhoehe Bestellung auf 4(2) um 18hur.
=> neue Zeile mit quantity = 2, tolerance = 0, und created_on = 18uhr
* Jetzt gibt es zwei zeilen die insgesamt 4(2) ergeben.
(die summen in `GroupOrderArticle` werden aktualisiert)
### Verringere die Mengen des Artikels.
Jetzt muss chronologisch zurueckgegangen werden und um die urspruenglich bestellten
Mengen zu verringern.
Beispiel von oben:
* Verringe Bestellung auf 2(1) um 19uhr.
=> Zeile mit created_on = 18uhr wird gelöscht und
in der Zeile mit created_on = 17uhr wird der Wert tolerance auf 1 gaendert.
## Wer bekommt wieviel?
Diese Frage wird wie schon erwaehnt mittels der `group_order_article_quantites`-Tabelle
geloest.
Beispiel.
* articel x mit unit_quantity = 5.
* 17uhr: gruppe a bestellt 2(3), weil sie auf jeden fall was von x bekommen will
* 18uhr: gruppe b bestellt 2(0)
* 19uhr: gruppe a faellt ein dass sie doch noch mehr braucht von x und aendert auf 4(1).
* jetzt gibt es drei zeilen in der tabelle, die so aussehen:
* (gruppe a), 2(1), 17uhr (wurde um 19uhr von 2(3) auf 2(1) geaendert)
* (gruppe b), 2(0), 18uhr
* (gruppe a), 2(0), 19uhr.
* die zuteilung wird dann wie folgt ermittelt:
* zeile 1: gruppe a bekommt 2
* zeile 2: gruppe b bekommt 2
* zeile 3: gruppe a bekommt 1, weil jetzt das gebinde schon voll ist.
* Endstand: insg. Bestellt wurden 6(1)
* Gruppe a bekommt 3 einheiten.
* gruppe b bekommt 2 einheiten.
* eine Einheit verfaellt.

View File

@ -1,94 +0,0 @@
Use this README file to introduce your application and point to useful places in the API for learning more.
Run "rake doc:app" to generate API documentation for your models and controllers.
= The Foodsoft
is a Web-based software to manage a non-profit food coop (product catalog, ordering, accounting, job scheduling).
== Bestellen
Das Bestellen ist der Hauptteil dieser Software und ein wenig kompliziert.
Hier starte ich den Versuch die Programmlogik in Text umzusetzen und
verweise auf die enstprechenden Controller bzw. Modelle.
Der relevante Controller ist OrderingController.
=== Bestellung "in Netz stellen"
Darunter verstehen wir die Auswahl von Artikeln eines bestimmten Lieferanten fuer eine zeitlich begrenzte
Bestellung im Internet. Die relevanten Methoden sind OrdersController#newOrder und folgende.
Jede Bestellung wird durch die Klasse Order abgebildet.
Die zugehoerigen Artikel werden duch die Klasse OrderArticle mit den Artikeln verknuepft.
Dabei werden auch die Attribute quantity, tolerance und quantity_to_order gespeichert.
Diese Mengen repraesentieren die Gesamtbestellung, also alle Bestellgruppen.
=== Eine Bestellgruppe bestellt...
Die Methode OrdersController#order schickt uns die Bestellenseite. Mit dieser Oberflaeche
koennen die Bestellgruppena die vorher ausgewaehlten Artikel bestellen.
Mittels den Buttons werden dabei live, also clientseitig, die Preise ermittelt
und der Gesamtpreis berechnet. Ist der Gesamtpreis groeßer als der aktuelle
Gruppenkontostand, so wird die Preisspalte rot unterlegt und die Bestellung
kann nicht gespeichert werden.
=== (gruppen)-Bestellung wird gespeichert
Die Gruppenbestellung wird durch die Tabelle group_oders (GroupOrder)
abgebildet, bzw. die Bestellung und Bestellgruppe wird dort verknuepft.
Die bestellten Artikel der Bestellgruppe werden durch die Tablle group_order_articles
(GroupOrderArticle) registriert. Dort werden nun die Modelle GroupOrder
und OrderArticle miteinander verbunden.
Bei jeder Bestellung wird außerdem die Summe der Menge, Toleranz in GroupOrderArticle
abgelegt. Allerdings muss jede Aenderung dieser Mengen mit protokolliert werden.
Dies ist wichtig, weil spaeter die Zuteilung der Einheiten pro bestellten Artikel
nach der chronologischen Reihenfolge erfolgt. (s.u.)
Das passiert dann in der Tabelle group_order_article_quantities.
(GroupOrderArticleQuantity)
=== Aenderunug einer Bestellung
Knifflig ist die Aenderung einer gruppenbestellung, weil die zeitliche
Reihenfolge dabei nicht durcheinander geraten darf.
Wir unterscheiden dehalb zwei Faelle:
==== Erhoehe die Menge des Arikels.
Jetzt wird eine Zeile in group_order_article_quantities angelegt.
und zwar mit genau den Mengen, die zusatzlich bestellt wurden.
Quantity und Tolerance funktionieren analog.
Beispiel:
Urspruenglich bestellt: 2(2) um 17uhr.
Erhoehe Bestellung auf 4(2) um 18hur.
=> neue Zeile mit quantity= 2, tolerance = 0, und created_on = 18uhr
Jetzt gibt es zwei zeilen die insgesamt 4(2) ergeben.
(die summen in GroupOrderArticle werden aktualisiert)
==== Verringere die Mengen des Artikels.
Jetzt muss chronologisch zurueckgegangen werden und um die urspruenglich bestellten
Mengen zu verringern.
Beispiel von oben:
Verringe Bestellung auf 2(1) um 19uhr.
=> Zeile mit created_on = 18uhr wird gelöscht und
in der Zeile mit created_on = 17uhr wird der Wert tolerance auf 1 gaendert.
=== Wer bekommt wieviel?
Diese Frage wird wie schon erwaehnt mittels der group_order_article_quantites -Tabelle
geloest.
Beipspiel.
articel x mit unit_quantity = 5.
17uhr: gruppe a bestellt 2(3), weil sie auf jeden fall was von x bekommen will
18uhr: gruppe b bestellt 2(0)
19uhr: gruppe a faellt ein dass sie doch noch mehr braucht von x und aendert auf 4(1).
jetzt gibt es drei zeilen in der tabelle, die so aussehen:
(gruppe a), 2(1), 17uhr (wurde um 19uhr von 2(3) auf 2(1) geaendert)
(gruppe b), 2(0), 18uhr
(gruppe a), 2(0), 19uhr.
die zuteilung wird dann wie folgt ermittelt:
zeile 1: gruppe a bekommt 2
zeile 2: gruppe b bekommt 2
zeile 3: gruppe a bekommt 1, weil jetzt das gebinde schon voll ist.
Endstand: insg. Bestellt wurden 6(1)
Gruppe a bekommt 3 einheiten.
gruppe b bekommt 2 einheiten.
eine Einheit verfaellt.

View File

@ -66,7 +66,7 @@ If you want to have more control, you can do these steps manually as
explained here.
1. **Configure datebase**
1. **Configure database**
Create the database configuration from the default:
```sh

View File

@ -0,0 +1,18 @@
FoodsoftWiki
============
This plugin adds wiki pages to foodsoft. A new 'Wiki' menu is added next to
the 'Foodcoops' menu in the navigation bar.
This plugin is enabled by default in foodsoft, so you don't need to do anything
to install it. If you still want to, for example when it has been disabled,
add the following to foodsoft's Gemfile:
```Gemfile
# we use the git version of acts_as_versioned, so this needs to be in foodsoft's Gemfile
gem 'acts_as_versioned', git: 'git://github.com/technoweenie/acts_as_versioned.git'
gem 'foodsoft_wiki', path: 'lib/foodsoft_wiki'
```
This plugin is part of the foodsoft package and uses the GPL-3 license (see
foodsoft's LICENSE for the full license text).

View File

@ -0,0 +1,40 @@
#!/usr/bin/env rake
begin
require 'bundler/setup'
rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end
begin
require 'rdoc/task'
rescue LoadError
require 'rdoc/rdoc'
require 'rake/rdoctask'
RDoc::Task = Rake::RDocTask
end
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'FoodsoftWiki'
rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
Bundler::GemHelper.install_tasks
require 'rake/testtask'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = false
end
task :default => :test

View File

@ -0,0 +1,15 @@
Rails.application.routes.draw do
scope '/:foodcoop' do
resources :pages do
get :all, :on => :collection
get :version, :on => :member
get :revert, :on => :member
end
match '/wiki/:permalink' => 'pages#show', :as => 'wiki_page' # , :constraints => {:permalink => /[^\s]+/}
match '/wiki' => 'pages#show', :defaults => {:permalink => 'Home'}, :as => 'wiki'
end
end

View File

@ -0,0 +1,24 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "foodsoft_wiki/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "foodsoft_wiki"
s.version = FoodsoftWiki::VERSION
s.authors = ["wvengen"]
s.email = ["dev-foodsoft@willem.engen.nl"]
s.homepage = "https://github.com/foodcoops/foodsoft"
s.summary = "Wiki plugin for foodsoft."
s.description = "Adds a wiki to foodsoft."
s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"]
s.test_files = Dir["test/**/*"]
s.add_dependency "rails", "~> 3.2.15"
s.add_dependency 'wikicloth'
s.add_dependency 'acts_as_versioned' # need git version, make sure that is included in foodsoft's Gemfile
s.add_development_dependency "sqlite3"
end

View File

@ -0,0 +1,6 @@
require 'wikicloth'
require 'acts_as_versioned'
require 'foodsoft_wiki/engine'
module FoodsoftWiki
end

View File

@ -0,0 +1,14 @@
module FoodsoftWiki
class Engine < ::Rails::Engine
def navigation(primary, ctx)
primary.item :wiki, I18n.t('navigation.wiki.title'), '#', id: nil do |subnav|
subnav.item :wiki_home, I18n.t('navigation.wiki.home'), ctx.wiki_path, id: nil
subnav.item :all_pages, I18n.t('navigation.wiki.all_pages'), ctx.all_pages_path, id: nil
end
# move this last added item to just after the foodcoop menu
if i = primary.items.index(primary[:foodcoop])
primary.items.insert(i+1, primary.items.delete_at(-1))
end
end
end
end

View File

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

View File

@ -1,5 +1,6 @@
begin
require 'rspec/core/rake_task'
task(:spec).clear
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
rescue LoadError

View File

@ -52,4 +52,58 @@ describe Article do
order = create :order, supplier: supplier, article_ids: [article.id]
expect(article.in_open_order).to eq(order)
end
it 'has no shared article by default' do
expect(article.shared_article).to be_nil
end
describe 'connected to a shared database', :type => :feature do
let(:shared_supplier) { create(:supplier) }
let(:shared_article) { create :article, supplier: shared_supplier, order_number: Faker::Lorem.characters(rand(1..12)) }
let(:supplier) { create :supplier, shared_supplier_id: shared_supplier.id }
let(:article) { create :article, supplier: supplier, order_number: shared_article.order_number }
it 'can be found in the shared database' do
expect(article.shared_article).to_not be_nil
end
it 'can find updates' do
changed = article.shared_article_changed?
expect(changed).to_not be_false
expect(changed.length).to be > 1
end
it 'can be synchronised' do
# TODO move article sync from supplier to article
article # need to reference for it to exist when syncing
updated_article = supplier.sync_all[0].select{|s| s[0].id==article.id}.first[0]
article.update_attributes updated_article.attributes.reject{|k,v| k=='id' or k=='type'}
expect(article.name).to eq(shared_article.name)
# now synchronising shouldn't change anything anymore
expect(article.shared_article_changed?).to be_false
end
it 'does not need to synchronise an imported article' do
article = SharedArticle.find(shared_article.id).build_new_article(supplier)
expect(article.shared_article_changed?).to be_false
end
it 'adapts to foodcoop units when synchronising' do
shared_article.unit = '1kg'
shared_article.unit_quantity = 1
shared_article.save!
article = SharedArticle.find(shared_article.id).build_new_article(supplier)
article.article_category = create :article_category
article.unit = '200g'
article.shared_updated_on -= 1 # to make update do something
article.save!
# TODO get sync functionality in article
updated_article = supplier.sync_all[0].select{|s| s[0].id==article.id}.first[0]
article.update_attributes! updated_article.attributes.reject{|k,v| k=='id' or k=='type'}
expect(article.unit).to eq '200g'
expect(article.unit_quantity).to eq 5
expect(article.price).to be_within(1e-3).of(shared_article.price/5)
end
end
end

View File

@ -29,6 +29,8 @@ RSpec.configure do |config|
config.before(:each) do
DatabaseCleaner.strategy = (example.metadata[:js] ? :truncation : :transaction)
DatabaseCleaner.start
# maximise window so that buttons can be found on popups
example.metadata[:js] and page.driver.browser.manage.window.maximize
end
config.after(:each) do
DatabaseCleaner.clean

View File

@ -0,0 +1,29 @@
# http://stackoverflow.com/questions/8774227
# http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite
class ActiveRecord::Base
mattr_accessor :shared_connection
@@shared_connection = nil
def self.connection
@@shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
ActiveSupport.on_load(:after_initialize) do
# We simulate the shared database by pointing to our own database.
# This allows running tests without additional database setup.
# But take care when designing tests using the shared database.
SharedSupplier.establish_connection Rails.env
SharedArticle.establish_connection Rails.env
# hack for different structure of shared database
SharedArticle.class_eval do
alias_attribute :number, :order_number
alias_attribute :updated_on, :updated_at
def self.find_by_number(n)
find_by_order_number(n)
end
end
end

View File