Merge branch 'master' into fix-closed-group_order-totals

Conflicts:
	db/schema.rb
This commit is contained in:
wvengen 2013-09-18 18:14:07 +02:00
commit ebb22ccb53
137 changed files with 4484 additions and 1507 deletions

View file

@ -2,11 +2,15 @@ class Delivery < ActiveRecord::Base
belongs_to :supplier
has_one :invoice
has_many :stock_changes, :dependent => :destroy
has_many :stock_changes,
:dependent => :destroy,
:include => 'stock_article',
:order => 'articles.name ASC'
scope :recent, :order => 'created_at DESC', :limit => 10
validates_presence_of :supplier_id
validates_presence_of :supplier_id, :delivered_on
validate :stock_articles_must_be_unique
accepts_nested_attributes_for :stock_changes, :allow_destroy => :true
@ -15,6 +19,18 @@ class Delivery < ActiveRecord::Base
stock_changes.build(attributes) unless attributes[:quantity].to_i == 0
end
end
def includes_article?(article)
self.stock_changes.map{|stock_change| stock_change.stock_article.id}.include? article.id
end
protected
def stock_articles_must_be_unique
unless stock_changes.reject{|sc| sc.marked_for_destruction?}.map {|sc| sc.stock_article.id}.uniq!.nil?
errors.add(:base, I18n.t('model.delivery.each_stock_article_must_be_unique'))
end
end
end

View file

@ -17,6 +17,8 @@ class GroupOrder < ActiveRecord::Base
scope :in_open_orders, joins(:order).merge(Order.open)
scope :in_finished_orders, joins(:order).merge(Order.finished_not_closed)
scope :ordered, :include => :ordergroup, :order => 'groups.name'
# Generate some data for the javascript methods in ordering view
def load_data
data = {}

View file

@ -14,7 +14,7 @@ class GroupOrderArticle < ActiveRecord::Base
validates_inclusion_of :tolerance, :in => 0..99
validates_uniqueness_of :order_article_id, :scope => :group_order_id # just once an article per group order
scope :ordered, :conditions => 'result > 0'
scope :ordered, :conditions => 'group_order_articles.result > 0 OR group_order_articles.quantity > 0 OR group_order_articles.tolerance > 0', :include => {:group_order => :ordergroup}, :order => 'groups.name'
localize_input_of :result

View file

@ -108,13 +108,13 @@ class Ordergroup < Group
# Make sure, the name is uniq, add usefull message if uniq group is already deleted
def uniqueness_of_name
id = new_record? ? nil : self.id
group = Ordergroup.where('groups.id != ? AND groups.name = ?', id, name).first
if group.present?
message = group.deleted? ? :taken_with_deleted : :taken
group = Ordergroup.where('groups.name = ?', name)
group = group.where('groups.id != ?', self.id) unless new_record?
if group.exists?
message = group.first.deleted? ? :taken_with_deleted : :taken
errors.add :name, message
end
end
end

View file

@ -0,0 +1,29 @@
class PeriodicTaskGroup < ActiveRecord::Base
has_many :tasks, dependent: :destroy
PeriodDays = 7
def has_next_task?
return false if tasks.empty?
return false if tasks.first.due_date.nil?
return true
end
def create_next_task
template_task = tasks.first
self.next_task_date ||= template_task.due_date + PeriodDays
next_task = template_task.dup
next_task.due_date = next_task_date
next_task.save
self.next_task_date += PeriodDays
self.save
end
def exclude_tasks_before(task)
tasks.where("due_date < '#{task.due_date}'").each do |t|
t.update_attribute(:periodic_task_group, nil)
end
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

@ -17,6 +17,10 @@ class StockArticle < Article
quantity - OrderArticle.where(article_id: id).
joins(:order).where("orders.state = 'open' OR orders.state = 'finished'").sum(:units_to_order)
end
def quantity_history
stock_changes.reorder('stock_changes.created_at ASC').map{|s| s.quantity}.cumulative_sum
end
def self.stock_value
available.collect { |a| a.quantity * a.gross_price }.sum

View file

@ -1,6 +1,7 @@
class StockChange < ActiveRecord::Base
belongs_to :delivery
belongs_to :order
belongs_to :stock_taking
belongs_to :stock_article
validates_presence_of :stock_article_id, :quantity

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) }
@ -82,10 +80,10 @@ class Supplier < ActiveRecord::Base
# Make sure, the name is uniq, add usefull message if uniq group is already deleted
def uniqueness_of_name
id = new_record? ? nil : self.id
supplier = Supplier.where('suppliers.id != ? AND suppliers.name = ?', id, name).first
if supplier.present?
message = supplier.deleted? ? :taken_with_deleted : :taken
supplier = Supplier.where('suppliers.name = ?', name)
supplier = supplier.where('suppliers.id != ?', self.id) unless new_record?
if supplier.exists?
message = supplier.first.deleted? ? :taken_with_deleted : :taken
errors.add :name, message
end
end

View file

@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
class Task < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :users, :through => :assignments
belongs_to :workgroup
belongs_to :periodic_task_group
scope :non_group, where(workgroup_id: nil, done: false)
scope :done, where(done: true)
@ -16,7 +18,9 @@ class Task < ActiveRecord::Base
validates :required_users, :presence => true
validates_numericality_of :duration, :required_users, :only_integer => true, :greater_than => 0
validates_length_of :description, maximum: 250
validates :done, exclusion: { in: [true] }, if: :periodic?, on: :create
before_save :exclude_from_periodic_task_group, if: :changed?, unless: :new_record?
after_save :update_ordergroup_stats
# Find all tasks, for which the current user should be responsible
@ -46,6 +50,10 @@ class Task < ActiveRecord::Base
end
end
def periodic?
not periodic_task_group.nil?
end
def is_assigned?(user)
self.assignments.detect {|ass| ass.user_id == user.id }
end
@ -100,5 +108,10 @@ class Task < ActiveRecord::Base
def update_ordergroup_stats(user_ids = self.user_ids)
Ordergroup.joins(:users).where(users: {id: user_ids}).each(&:update_stats!)
end
def exclude_from_periodic_task_group
self.periodic_task_group = nil
true
end
end

View file

@ -3,6 +3,7 @@
require 'digest/sha1'
# specific user rights through memberships (see Group)
class User < ActiveRecord::Base
include RailsSettings::Extend
#TODO: acts_as_paraniod ??
has_many :memberships, :dependent => :destroy
@ -19,8 +20,11 @@ class User < ActiveRecord::Base
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, :setting_attributes
attr_accessor :password, :settings_attributes
# makes the current_user (logged-in-user) available in models
cattr_accessor :current_user
validates_presence_of :nick, :email
validates_presence_of :password, :on => :create
validates_length_of :nick, :in => 2..25
@ -32,53 +36,37 @@ class User < ActiveRecord::Base
validates_length_of :password, :in => 5..25, :allow_blank => true
before_validation :set_password
after_save :update_settings
# Adds support for configuration settings (through "settings" attribute).
acts_as_configurable
# makes the current_user (logged-in-user) available in models
cattr_accessor :current_user
# User settings keys
# returns the User-settings and the translated description
def self.setting_keys
{
"notify.orderFinished" => I18n.t('model.user.notify.order_finished'),
"notify.negativeBalance" => I18n.t('model.user.notify.negative_balance'),
"notify.upcoming_tasks" => I18n.t('model.user.notify.upcoming_tasks'),
"messages.sendAsEmail" => I18n.t('model.user.notify.send_as_email'),
"profile.phoneIsPublic" => I18n.t('model.user.notify.phone_is_public'),
"profile.emailIsPublic" => I18n.t('model.user.notify.email_is_public'),
"profile.nameIsPublic" => I18n.t('model.user.notify.name_is_public')
}
after_initialize do
settings.defaults['profile'] = { 'language' => I18n.default_locale } unless settings.profile
settings.defaults['messages'] = { 'send_as_email' => true } unless settings.messages
settings.defaults['notify'] = { 'upcoming_tasks' => true } unless settings.notify
end
# retuns the default setting for a NEW user
# for old records nil will returned
# TODO: integrate default behaviour in acts_as_configurable plugin
def settings_default(setting)
# define a default for the settings
defaults = {
"messages.sendAsEmail" => true,
"notify.upcoming_tasks" => true
}
return true if self.new_record? && defaults[setting]
end
def update_settings
unless setting_attributes.nil?
for setting in User::setting_keys.keys
self.settings[setting] = setting_attributes[setting] && setting_attributes[setting] == '1' ? '1' : nil
after_save do
return if settings_attributes.nil?
settings_attributes.each do |key, value|
value.each do |k, v|
case v
when '1'
value[k] = true
when '0'
value[k] = false
end
end
self.settings.merge!(key, value)
end
end
def locale
settings.profile['language']
end
def name
[first_name, last_name].join(" ")
end
def receive_email?
settings['messages.sendAsEmail'] == "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,54 +3,12 @@ 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
validates_presence_of :task_name, :weekday, :task_required_users, :next_weekly_tasks_number,
:if => :weekly_task
validates_numericality_of :next_weekly_tasks_number, :greater_than => 0, :less_than => 21, :only_integer => true,
:if => :weekly_task
validates_length_of :task_description, maximum: 250
validate :last_admin_on_earth, :on => :update
before_destroy :check_last_admin_group
def self.weekdays
days = I18n.t('date.day_names')
(0..days.length-1).map {|i| [days[i], i.to_s]}
end
# Returns an Array with date-objects to represent the next weekly-tasks
def next_weekly_tasks
# our system starts from 0 (sunday) to 6 (saturday)
# get difference between groups weekday and now
diff = self.weekday - Time.now.wday
if diff >= 0
# weektask is in current week
nextTask = diff.day.from_now
else
# weektask is in the next week
nextTask = (diff + 7).day.from_now
end
# now generate the Array
nextTasks = Array.new
next_weekly_tasks_number.times do
nextTasks << nextTask.to_date
nextTask = 1.week.from_now(nextTask)
end
return nextTasks
end
def task_attributes(date)
{
:name => task_name,
:description => task_description,
:due_date => date,
:required_users => task_required_users,
:duration => task_duration,
:weekly => true
}
end
protected
# Check before destroy a group, if this is the last group with admin role