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

@ -1,14 +1,17 @@
//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require select2
//= require twitter/bootstrap
//= require jquery.tokeninput
//= require bootstrap-datepicker
//= require bootstrap-datepicker.de
//= require bootstrap-datepicker/core
//= require bootstrap-datepicker/locales/bootstrap-datepicker.de
//= require bootstrap-datepicker/locales/bootstrap-datepicker.nl
//= require jquery.observe_field
//= require rails.validations
//= require_self
//= require ordering
//= require stupidtable
// allow touch devices to work on click events
// http://stackoverflow.com/a/16221066
@ -112,9 +115,16 @@ $(function() {
});
// Use bootstrap datepicker for dateinput
$('.datepicker').datepicker({format: 'yyyy-mm-dd', weekStart: 1, language: 'de'});
$('.datepicker').datepicker({format: 'yyyy-mm-dd', language: I18n.locale});
// See stupidtable.js for initialization of local table sorting
});
// retrigger last local table sorting
function updateSort(table) {
$('.sorting-asc, .sorting-desc', table).toggleClass('.sorting-asc .sorting-desc')
.removeData('sort-dir').trigger('click'); // CAUTION: removing data field of plugin
}
// gives the row an yellow background
function highlightRow(checkbox) {

View file

@ -10,6 +10,7 @@ var groupBalance = 0; // available group money
var currencySeparator = "."; // default decimal separator
var currencyPrecision = 2; // default digits behind comma
var currencyUnit = "€"; // default currency
var minimumBalance = 0; // minimum group balance for the order to be succesful
var toleranceIsCostly = true; // default tolerance behaviour
var isStockit = false; // Wheter the order is from stock oder normal supplier
@ -40,6 +41,10 @@ function setGroupBalance(amount) {
groupBalance = amount;
}
function setMinimumBalance(amount) {
minimumBalance = amount;
}
function addData(orderArticleId, itemPrice, itemUnit, itemSubtotal, itemQuantityOthers, itemToleranceOthers, allocated, available) {
var i = orderArticleId;
price[i] = itemPrice;
@ -159,7 +164,7 @@ function updateBalance() {
$('#total_balance').val(asMoney(balance));
// determine bgcolor and submit button state according to balance
var bgcolor = '';
if (balance < 0) {
if (balance < minimumBalance) {
bgcolor = '#FF0000';
$('#submit_button').attr('disabled', 'disabled')
} else {

View file

@ -0,0 +1,186 @@
// Stupid jQuery table plugin.
// Call on a table
// sortFns: Sort functions for your datatypes.
(function($) {
$.fn.stupidtable = function(sortFns) {
return this.each(function() {
var $table = $(this);
sortFns = sortFns || {};
// ==================================================== //
// Utility functions //
// ==================================================== //
// Merge sort functions with some default sort functions.
sortFns = $.extend({}, {
"int": function(a, b) {
return parseInt(a, 10) - parseInt(b, 10);
},
"float": function(a, b) {
return parseFloat(a) - parseFloat(b);
},
"string": function(a, b) {
if (a < b) return -1;
if (a > b) return +1;
return 0;
},
"string-ins": function(a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) return -1;
if (a > b) return +1;
return 0;
}
}, sortFns);
// Return the resulting indexes of a sort so we can apply
// this result elsewhere. This returns an array of index numbers.
// return[0] = x means "arr's 0th element is now at x"
var sort_map = function(arr, sort_function, reverse_column) {
var map = [];
var index = 0;
if (reverse_column) {
for (var i = arr.length-1; i >= 0; i--) {
map.push(i);
}
}
else {
var sorted = arr.slice(0).sort(sort_function);
for (var i=0; i<arr.length; i++) {
index = $.inArray(arr[i], sorted);
// If this index is already in the map, look for the next index.
// This handles the case of duplicate entries.
while ($.inArray(index, map) != -1) {
index++;
}
map.push(index);
}
}
return map;
};
// Apply a sort map to the array.
var apply_sort_map = function(arr, map) {
var clone = arr.slice(0),
newIndex = 0;
for (var i=0; i<map.length; i++) {
newIndex = map[i];
clone[newIndex] = arr[i];
}
return clone;
};
// ==================================================== //
// Begin execution! //
// ==================================================== //
// Do sorting when THs are clicked
$table.on("click", "th", function() {
var trs = $table.children("tbody").children("tr");
var $this = $(this);
var th_index = 0;
var dir = $.fn.stupidtable.dir;
$table.find("th").slice(0, $this.index()).each(function() {
var cols = $(this).attr("colspan") || 1;
th_index += parseInt(cols,10);
});
// Determine (and/or reverse) sorting direction, default `asc`
var sort_dir = $this.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC;
// Choose appropriate sorting function. If we're sorting descending, check
// for a `data-sort-desc` attribute.
if ( sort_dir == dir.DESC )
var type = $this.data("sort-desc") || $this.data("sort") || null;
else
var type = $this.data("sort") || null;
// Prevent sorting if no type defined
if (type === null) {
return;
}
// Trigger `beforetablesort` event that calling scripts can hook into;
// pass parameters for sorted column index and sorting direction
$table.trigger("beforetablesort", {column: th_index, direction: sort_dir});
// More reliable method of forcing a redraw
$table.css("display");
// Run sorting asynchronously on a timout to force browser redraw after
// `beforetablesort` callback. Also avoids locking up the browser too much.
setTimeout(function() {
// Gather the elements for this column
var column = [];
var sortMethod = sortFns[type];
// Push either the value of the `data-order-by` attribute if specified
// or just the text() value in this column to column[] for comparison.
trs.each(function(index,tr) {
var $e = $(tr).children().eq(th_index);
var sort_val = $e.data("sort-value");
var order_by = typeof(sort_val) !== "undefined" ? sort_val : $e.text();
column.push(order_by);
});
// Create the sort map. This column having a sort-dir implies it was
// the last column sorted. As long as no data-sort-desc is specified,
// we're free to just reverse the column.
var reverse_column = !!$this.data("sort-dir") && !$this.data("sort-desc");
var theMap = sort_map(column, sortMethod, reverse_column);
// Reset siblings
$table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc");
$this.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir);
// Replace the content of tbody with the sortedTRs. Strangely (and
// conveniently!) enough, .append accomplishes this for us.
var sortedTRs = $(apply_sort_map(trs, theMap));
$table.children("tbody").append(sortedTRs);
// Trigger `aftertablesort` event. Similar to `beforetablesort`
$table.trigger("aftertablesort", {column: th_index, direction: sort_dir});
// More reliable method of forcing a redraw
$table.css("display");
}, 10);
});
});
};
// Enum containing sorting directions
$.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"};
})(jQuery);
////////////////////////////////////////////////////////////////////////////////
//
// own additions for automatic initialization of table sorting
//
////////////////////////////////////////////////////////////////////////////////
$(function() {
var stupidtables = $('table.stupidtable');
if(stupidtables.length) {
// Add pseudo links just for matching foodsoft style
$('th[data-sort]', stupidtables).wrapInner('<a href="#" class="stupidlink"></a>');
$('.stupidlink', stupidtables).on('click', function(e) {e.preventDefault();});
// Init stupidtable sorting
stupidtables.stupidtable();
// Update class of sort link after sort to match foodsoft style
stupidtables.on('aftertablesort', function(e, data) {
// Ignore data and use the updated classes in DOM
var stupidthead = $('thead', this);
$('a.stupidlink', stupidthead).removeClass('sortup sortdown');
$('th.sorting-asc a.stupidlink', stupidthead).addClass('sortup');
$('th.sorting-desc a.stupidlink', stupidthead).addClass('sortdown');
});
// Sort tables with a default sort
$('.default-sort', stupidtables).trigger('click');
}
});

View file

@ -1,4 +1,6 @@
/*
*= require bootstrap_and_overrides
*= require select2
*= require token-input-bootstrappy
*= require bootstrap-datepicker
*/

View file

@ -31,10 +31,6 @@ body {
// Example:
// @linkColor: #ff0000;
// Bootstrap datepicker
@import "datepicker";
// Custom styles
// Fix empty dd tags in horizontal dl, see https://github.com/twitter/bootstrap/issues/4062
@ -42,6 +38,22 @@ body {
dd { .clearfix(); }
}
// Do not use additional margin for input in table
.form-horizontal .control-group.control-group-intable,
.form-horizontal .controls.controls-intable {
margin: 0;
}
// Light tooltips without empty space below tables
.tooltip-inner {
color: #000;
background-color: rgb(245,245,245);
border: 1px solid #ccc;
}
.tooltip-inner .table {
margin-bottom: 0;
}
@mainRedColor: #ED0606;
.logo {
@ -208,3 +220,21 @@ tr.unavailable {
dt { width: 160px; }
dd { margin-left: 170px; }
}
.settings {
.settings-group {
margin-bottom: 10px;
.control-label {
margin: 5px 0 0 0;
}
}
.control-group {
margin-bottom: 5px;
}
.control-group.h_wrapper {
margin-bottom: 5px;
}
.control-group.select {
margin-bottom: 15px
}
}

View file

@ -1,10 +1,13 @@
# encoding: utf-8
class ApplicationController < ActionController::Base
include Foodsoft::ControllerExtensions::Locale
helper_method :available_locales
protect_from_forgery
before_filter :select_language, :select_foodcoop, :authenticate, :store_controller, :items_per_page, :set_redirect_to
before_filter :select_foodcoop, :authenticate, :store_controller, :items_per_page, :set_redirect_to
after_filter :remove_controller
# Returns the controller handling the current request.
def self.current
Thread.current[:application_controller]
@ -26,7 +29,7 @@ class ApplicationController < ActionController::Base
redirect_to login_url, :alert => 'Access denied!'
end
private
private
def authenticate(role = 'any')
# Attempt to retrieve authenticated user from controller instance or session...
@ -141,9 +144,5 @@ class ApplicationController < ActionController::Base
def default_url_options(options = {})
{foodcoop: FoodsoftConfig.scope}
end
# Used to prevent accidently switching to :en in production mode.
def select_language
I18n.locale = :de
end
end

View file

@ -32,7 +32,7 @@ class ArticlesController < ApplicationController
end
def new
@article = @supplier.articles.build(:tax => 7.0)
@article = @supplier.articles.build(:tax => FoodsoftConfig[:tax_default])
render :layout => false
end
@ -145,19 +145,22 @@ class ArticlesController < ApplicationController
begin
@articles = Array.new
articles, outlisted_articles = FoodsoftFile::parse(params[:articles]["file"])
no_category = ArticleCategory.new
articles.each do |row|
# fallback to Others category
category = (ArticleCategory.find_by_name(row[:category]) or no_category)
# creates a new article and price
article = Article.new( :name => row[:name],
:note => row[:note],
:manufacturer => row[:manufacturer],
:origin => row[:origin],
:unit => row[:unit],
:article_category => ArticleCategory.find_by_name(row[:category]),
:article_category => category,
:price => row[:price],
:unit_quantity => row[:unit_quantity],
:order_number => row[:number],
:deposit => row[:deposit],
:tax => row[:tax])
:tax => (row[:tax] or FoodsoftConfig[:tax_default]))
# stop parsing, when an article isn't valid
unless article.valid?
raise I18n.t('articles.controller.error_parse', :msg => article.errors.full_messages.join(", "), :line => (articles.index(row) + 2).to_s)
@ -206,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

@ -5,43 +5,25 @@ class DeliveriesController < ApplicationController
def index
@deliveries = @supplier.deliveries.all :order => 'delivered_on DESC'
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @deliveries }
end
end
def show
@delivery = Delivery.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @delivery }
end
end
def new
@delivery = @supplier.deliveries.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @delivery }
end
@delivery.delivered_on = Date.today #TODO: move to model/database
end
def create
@delivery = Delivery.new(params[:delivery])
respond_to do |format|
if @delivery.save
flash[:notice] = I18n.t('deliveries.create.notice')
format.html { redirect_to([@supplier,@delivery]) }
format.xml { render :xml => @delivery, :status => :created, :location => @delivery }
else
format.html { render :action => "new" }
format.xml { render :xml => @delivery.errors, :status => :unprocessable_entity }
end
if @delivery.save
flash[:notice] = I18n.t('deliveries.create.notice')
redirect_to [@supplier, @delivery]
else
render :action => "new"
end
end
@ -52,15 +34,11 @@ class DeliveriesController < ApplicationController
def update
@delivery = Delivery.find(params[:id])
respond_to do |format|
if @delivery.update_attributes(params[:delivery])
flash[:notice] = I18n.t('deliveries.update.notice')
format.html { redirect_to([@supplier,@delivery]) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @delivery.errors, :status => :unprocessable_entity }
end
if @delivery.update_attributes(params[:delivery])
flash[:notice] = I18n.t('deliveries.update.notice')
redirect_to [@supplier,@delivery]
else
render :action => "edit"
end
end
@ -69,40 +47,60 @@ class DeliveriesController < ApplicationController
@delivery.destroy
flash[:notice] = I18n.t('deliveries.destroy.notice')
respond_to do |format|
format.html { redirect_to(supplier_deliveries_url(@supplier)) }
format.xml { head :ok }
redirect_to supplier_deliveries_url(@supplier)
end
# three possibilites to fill a new_stock_article form
# (1) start from blank or use params
def new_stock_article
@stock_article = @supplier.stock_articles.build(params[:stock_article])
render :layout => false
end
# (2) StockArticle as template
def copy_stock_article
@stock_article = StockArticle.find(params[:old_stock_article_id]).dup
render :layout => false
end
# (3) non-stock Article as template
def derive_stock_article
@stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
render :layout => false
end
def create_stock_article
@stock_article = StockArticle.new(params[:stock_article])
if @stock_article.valid? and @stock_article.save
render :layout => false
else
render :action => 'new_stock_article', :layout => false
end
end
def add_stock_article
article = @supplier.stock_articles.build(params[:stock_article])
render :update do |page|
if article.save
logger.debug "new StockArticle: #{article.id}"
page.insert_html :bottom, 'stock_changes', :partial => 'stock_change',
:locals => {:stock_change => article.stock_changes.build, :supplier => @supplier}
def edit_stock_article
@stock_article = StockArticle.find(params[:stock_article_id])
render :layout => false
end
page.replace_html 'new_stock_article', :partial => 'stock_article_form',
:locals => {:stock_article => @supplier.stock_articles.build}
else
page.replace_html 'new_stock_article', :partial => 'stock_article_form',
:locals => {:stock_article => article}
end
def update_stock_article
@stock_article = StockArticle.find(params[:stock_article][:id])
if @stock_article.update_attributes(params[:stock_article])
render :layout => false
else
render :action => 'edit_stock_article', :layout => false
end
end
def add_stock_change
@stock_change = StockChange.new
@stock_change.stock_article = StockArticle.find(params[:stock_article_id])
render :layout => false
end
def fill_new_stock_article_form
article = Article.find(params[:article_id])
@supplier = article.supplier
stock_article = @supplier.stock_articles.build(
article.attributes.reject { |attr| attr == ('id' || 'type')}
)
render :partial => 'stock_article_form', :locals => {:stock_article => stock_article}
end
end

View file

@ -65,7 +65,13 @@ class Finance::GroupOrderArticlesController < ApplicationController
def destroy
group_order_article = GroupOrderArticle.find(params[:id])
group_order_article.destroy
# only destroy if quantity and tolerance was zero already, so that we don't
# lose what the user ordered, if any
if group_order_article.quantity > 0 or group_order_article.tolerance >0
group_order_article.update_attribute(:result, 0)
else
group_order_article.destroy
end
update_summaries(group_order_article)
@order_article = group_order_article.order_article

View file

@ -42,6 +42,14 @@ class Finance::OrderArticlesController < ApplicationController
def destroy
@order_article = OrderArticle.find(params[:id])
@order_article.destroy
# only destroy if there are no associated GroupOrders; if we would, the requested
# quantity and tolerance would be gone. Instead of destroying, we set all result
# quantities to zero.
if @order_article.group_order_articles.count == 0
@order_article.destroy
else
@order_article.group_order_articles.each { |goa| goa.update_attribute(:result, 0) }
@order_article.update_results!
end
end
end

View file

@ -16,6 +16,7 @@ class HomeController < ApplicationController
def update_profile
if @current_user.update_attributes(params[:user])
session[:locale] = @current_user.locale
redirect_to my_profile_url, notice: I18n.t('home.changes_saved')
else
render :profile

View file

@ -58,6 +58,7 @@ class LoginController < ApplicationController
if @user.save
Membership.new(:user => @user, :group => @invite.group).save!
@invite.destroy
session[:locale] = @user.locale
redirect_to login_url, notice: I18n.t('login.controller.accept_invitation.notice')
end
end

View file

@ -11,6 +11,8 @@ class SessionsController < ApplicationController
if user
session[:user_id] = user.id
session[:scope] = FoodsoftConfig.scope # Save scope in session to not allow switching between foodcoops with one account
session[:locale] = user.locale
if session[:return_to].present?
redirect_to_url = session[:return_to]
session[:return_to] = nil

View file

@ -55,4 +55,9 @@ class StockitController < ApplicationController
render :partial => 'form', :locals => {:stock_article => stock_article}
end
def history
@stock_article = StockArticle.undeleted.find(params[:stock_article_id])
@stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC').each {|s| s.readonly!}
end
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

@ -18,6 +18,9 @@ class TasksController < ApplicationController
def create
@task = Task.new(params[:task])
if params[:periodic]
@task.periodic_task_group = PeriodicTaskGroup.new
end
if @task.save
redirect_to tasks_url, :notice => I18n.t('tasks.create.notice')
else
@ -32,13 +35,20 @@ class TasksController < ApplicationController
def edit
@task = Task.find(params[:id])
@task.current_user_id = current_user.id
if @task.periodic?
flash.now[:alert] = I18n.t('tasks.edit.warning_periodic').html_safe
end
end
def update
@task = Task.find(params[:id])
was_periodic = @task.periodic?
@task.attributes=(params[:task])
if @task.errors.empty? && @task.save
flash[:notice] = I18n.t('tasks.update.notice')
if was_periodic and not @task.periodic?
flash[:notice] = I18n.t('tasks.update.notice_converted')
end
if @task.workgroup
redirect_to workgroup_tasks_url(workgroup_id: @task.workgroup_id)
else
@ -53,7 +63,12 @@ class TasksController < ApplicationController
task = Task.find(params[:id])
# Save user_ids to update apple statistics after destroy
user_ids = task.user_ids
task.destroy
if params[:periodic]
task.periodic_task_group.exclude_tasks_before(task)
task.periodic_task_group.destroy
else
task.destroy
end
task.update_ordergroup_stats(user_ids)
redirect_to tasks_url, :notice => I18n.t('tasks.destroy.notice')

View file

@ -12,20 +12,29 @@ class OrderByArticles < OrderPdf
def body
@order.order_articles.ordered.each do |order_article|
text "#{order_article.article.name} (#{order_article.article.unit} | #{order_article.price.unit_quantity.to_s} | #{number_with_precision(order_article.price.fc_price, precision: 2)})",
style: :bold, size: 10
rows = []
rows << I18n.t('documents.order_by_articles.rows')
for goa in order_article.group_order_articles
dimrows = []
for goa in order_article.group_order_articles.ordered
rows << [goa.group_order.ordergroup.name,
"#{goa.quantity} + #{goa.tolerance}",
goa.result,
number_with_precision(order_article.price.fc_price * goa.result, precision: 2)]
dimrows << rows.length if goa.result == 0
end
next if rows.length == 0
rows.unshift I18n.t('documents.order_by_articles.rows') # table header
table rows, column_widths: [200,40,40], cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
table.columns(1..2).align = :right
text "#{order_article.article.name} (#{order_article.article.unit} | #{order_article.price.unit_quantity.to_s} | #{number_with_precision(order_article.price.fc_price, precision: 2)})",
style: :bold, size: 10
table rows, cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
table.column(0).width = 200
table.columns(1..3).align = :right
table.column(2).font_style = :bold
table.cells.border_width = 1
table.cells.border_color = '666666'
table.rows(0).border_bottom_width = 2
# dim rows which were ordered but not received
dimrows.each { |ri| table.row(ri).text_color = '999999' }
end
move_down 10
end

View file

@ -12,12 +12,10 @@ class OrderByGroups < OrderPdf
def body
# Start rendering
@order.group_orders.each do |group_order|
text group_order.ordergroup.name, size: 9, style: :bold
@order.group_orders.ordered.each do |group_order|
total = 0
rows = []
rows << I18n.t('documents.order_by_groups.rows') # Table Header
dimrows = []
group_order_articles = group_order.group_order_articles.ordered
group_order_articles.each do |goa|
@ -25,15 +23,20 @@ class OrderByGroups < OrderPdf
sub_total = price * goa.result
total += sub_total
rows << [goa.order_article.article.name,
"#{goa.quantity} + #{goa.tolerance}",
goa.result,
number_with_precision(price, precision: 2),
goa.order_article.price.unit_quantity,
goa.order_article.article.unit,
number_with_precision(sub_total, precision: 2)]
dimrows << rows.length if goa.result == 0
end
rows << [ I18n.t('documents.order_by_groups.sum'), nil, nil, nil, nil, number_with_precision(total, precision: 2)]
next if rows.length == 0
rows << [ I18n.t('documents.order_by_groups.sum'), nil, nil, nil, nil, nil, number_with_precision(total, precision: 2)]
rows.unshift I18n.t('documents.order_by_groups.rows') # Table Header
table rows, column_widths: [250,50,50,50,50,50], cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
text group_order.ordergroup.name, size: 9, style: :bold
table rows, width: 500, cell_style: {size: 8, overflow: :shrink_to_fit} do |table|
# borders
table.cells.borders = []
table.row(0).borders = [:bottom]
@ -41,8 +44,14 @@ class OrderByGroups < OrderPdf
table.cells.border_width = 1
table.cells.border_color = '666666'
table.columns(1..3).align = :right
table.columns(5).align = :right
table.column(0).width = 240
table.column(2).font_style = :bold
table.columns(1..4).align = :right
table.column(6).align = :right
table.column(6).font_style = :bold
# dim rows which were ordered but not received
dimrows.each { |ri| table.row(ri).text_color = '999999' }
end
move_down 15

View file

@ -20,11 +20,17 @@ 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
move_down 5
text "#{I18n.t('simple_form.labels.supplier.phone')}: #{contact[:phone]}", size: 9, align: :right
move_down 5
text "#{I18n.t('simple_form.labels.supplier.email')}: #{contact[:email]}", 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
@ -32,8 +38,10 @@ class OrderFax < OrderPdf
text @order.name
move_down 5
text @order.supplier.try(:address).to_s
move_down 5
text "#{I18n.t('simple_form.labels.supplier.fax')}: #{@order.supplier.try(:fax)}"
unless @order.supplier.try(:fax).blank?
move_down 5
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)}"
move_down 10
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

@ -158,5 +158,13 @@ module ApplicationHelper
end
flash_messages.join("\n").html_safe
end
# render base errors in a form after failed validation
# http://railsapps.github.io/twitter-bootstrap-rails.html
def base_errors resource
return '' if (resource.errors.empty?) or (resource.errors[:base].empty?)
messages = resource.errors[:base].map { |msg| content_tag(:li, msg) }.join
render :partial => 'shared/base_errors', :locals => {:error_messages => messages}
end
end

View file

@ -1,5 +1,5 @@
module DeliveriesHelper
def link_to_invoice(delivery)
if delivery.invoice
link_to number_to_currency(delivery.invoice.amount), [:finance, delivery.invoice],
@ -9,9 +9,29 @@ module DeliveriesHelper
class: 'btn btn-mini'
end
end
def stock_articles_for_select(supplier)
supplier.stock_articles.undeleted.reorder('articles.name ASC').map {|a| ["#{a.name} (#{number_to_currency a.price}/#{a.unit})", a.id] }
def articles_for_select2(supplier)
supplier.articles.undeleted.reorder('articles.name ASC').map {|a| {:id => a.id, :text => "#{a.name} (#{number_to_currency a.price}/#{a.unit})"} }
end
def stock_articles_for_table(supplier)
supplier.stock_articles.undeleted.reorder('articles.name ASC')
end
def stock_change_remove_link(stock_change_form)
return link_to t('.remove_article'), "#", :class => 'remove_new_stock_change btn btn-small' if stock_change_form.object.new_record?
output = stock_change_form.hidden_field :_destroy
output += link_to t('.remove_article'), "#", :class => 'destroy_stock_change btn btn-small'
return output.html_safe
end
def stock_article_price_hint(stock_article)
t('simple_form.hints.stock_article.edit_stock_article.price',
:stock_article_copy_link => link_to(t('.copy_stock_article'),
copy_stock_article_supplier_deliveries_path(@supplier, :old_stock_article_id => stock_article.id),
:remote => true
)
)
end
end

View file

@ -4,7 +4,7 @@ module Finance::OrderArticlesHelper
if @order.stockit?
StockArticle.order('articles.name')
else
@order.supplier.articles.order('articles.name')
@order.supplier.articles.undeleted.order('articles.name')
end
end
end

View file

@ -4,4 +4,14 @@ module StockitHelper
class_names << "unavailable" if article.quantity_available <= 0
class_names.join(" ")
end
def link_to_stock_change_reason(stock_change)
if stock_change.delivery_id
link_to t('.delivery'), supplier_delivery_path(stock_change.delivery.supplier, stock_change.delivery)
elsif stock_change.order_id
link_to t('.order'), order_path(stock_change.order)
elsif stock_change.stock_taking_id
link_to t('.stock_taking'), stock_taking_path(stock_change.stock_taking)
end
end
end

View file

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

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

View file

@ -20,10 +20,10 @@
.well
%h4= t '.preference'
%table.table
- for setting in User::setting_keys.keys
- @user.settings.profile.each do |key, setting|
%tr
%td= User::setting_keys[setting]
%td= @user.settings[setting] == '1' ? t('simple_form.yes') : t('simple_form.no')
%td= t("simple_form.labels.settings.profile.#{key}")
%td= (setting != true and setting != false) ? setting : (setting === true ? t('simple_form.yes') : t('simple_form.no'))
.span3
.well
%h4= t '.groupabos'

View file

@ -19,7 +19,9 @@
- @articles.each_with_index do |article, index|
= fields_for "articles[#{article.id || index}]", article do |form|
%tr
%td= form.check_box 'availability'
%td
= yield form # allow to add hidden fields to form
= form.check_box 'availability'
%td= form.text_field 'name', class: 'input-medium'
%td= form.text_field 'unit', class: 'input-mini'
%td= form.text_field 'price', class: 'input-mini'

View file

@ -2,7 +2,9 @@
%p= t('.body').html_safe
= form_tag(create_from_upload_supplier_articles_path(@supplier)) do
= render 'edit_all_table'
= render layout: 'edit_all_table' do |form|
= form.hidden_field :manufacturer
= form.hidden_field :origin
.form-actions
= submit_tag t('.submit', supplier: @supplier.name), class: 'btn btn-primary'
= link_to t('ui.or_cancel'), upload_supplier_articles_path(@supplier)

View file

@ -1,45 +1,134 @@
- content_for :javascript do
:javascript
$(function() {
$('.destroy_stock_change').live('click', function() {
$(this).prev('input').val('1').parent().hide();
$('#stock_changes').on('click', '.destroy_stock_change', function() {
$(this).prev('input').val('1'); // check for destruction
var stock_change = $(this).closest('tr');
stock_change.hide(); // do not remove (to ensure destruction)
stock_change.removeAttr('id'); // remove id to allow re-adding
mark_article_for_delivery( stock_change.data('id') );
return false;
});
$('.remove_new_stock_change').live('click', function() {
$(this).parent().remove();
$('#stock_changes').on('click', '.remove_new_stock_change', function() {
var stock_change = $(this).closest('tr');
stock_change.remove();
mark_article_for_delivery( stock_change.data('id') );
return false;
})
$('#new_stock_article').removeAttr('disabled').select2({
placeholder: '#{t '.create_stock_article'}',
data: #{articles_for_select2(@supplier).to_json},
createSearchChoice: function(term) {
return {
id: 'new',
text: term
};
},
formatResult: function(result, container, query, escapeMarkup) {
if(result.id == 'new') {
return result.text + ' (#{t '.create_from_blank'})';
}
var markup=[];
Select2.util.markMatch(result.text, query.term, markup, escapeMarkup);
return markup.join("");
}
}).on('change', function(e) {
var selectedArticle = $(e.currentTarget).select2('data');
if(!selectedArticle) {
return false;
}
if('new' == selectedArticle.id) {
$.ajax({
url: '#{new_stock_article_supplier_deliveries_path(@supplier)}',
type: 'get',
data: {stock_article: {name: selectedArticle.text}},
contentType: 'application/json; charset=UTF-8'
});
$('#new_stock_article').select2('data', null);
return true;
}
if('' != selectedArticle.id) {
$.ajax({
url: '#{derive_stock_article_supplier_deliveries_path(@supplier)}',
type: 'get',
data: {old_article_id: selectedArticle.id},
contentType: 'application/json; charset=UTF-8'
});
$('#new_stock_article').select2('data', null);
return true;
}
});
enablePriceTooltips();
});
function mark_article_for_delivery(stock_article_id) {
var articleTr = $('#stock_article_' + stock_article_id);
if( is_article_available_for_delivery(stock_article_id) ) {
articleTr.removeClass('unavailable');
$('.button-add-stock-change', articleTr).removeAttr('disabled');
}
else {
articleTr.addClass('unavailable');
$('.button-add-stock-change', articleTr).attr('disabled', 'disabled');
}
}
function is_article_available_for_delivery(stock_article_id) {
return ( 0 == $('#stock_change_stock_article_' + stock_article_id).length );
}
function enablePriceTooltips(context) {
$('[data-toggle~="tooltip"]', context).tooltip({
animation: false,
html: true,
placement: 'left'
});
}
= simple_form_for [@supplier, @delivery], validate: true do |f|
= f.hidden_field :supplier_id
#stock_changes
= f.fields_for :stock_changes do |stock_change_form|
%p
= stock_change_form.select :stock_article_id, stock_articles_for_select(@supplier)
Menge
= stock_change_form.text_field :quantity, size: 5, autocomplete: 'off'
= stock_change_form.hidden_field :_destroy
= link_to t('.remove_article'), "#", class: 'destroy_stock_change'
%p
= link_to t('.add_article'), {action: 'add_stock_change', supplier_id: @supplier.id}, remote: true
%p
%small= t('.note_new_article', new_link: link_to(t('.note_new_article_link'), new_stock_article_path)).html_safe
%hr/
= f.error_notification
= base_errors f.object
= f.association :supplier, :as => :hidden
%h2= t '.title_select_stock_articles'
%table#stock_articles_for_adding.table.table-hover.stupidtable
%thead
%tr
%th.default-sort{:data => {:sort => 'string'}}= t '.article'
%th= t '.price'
%th= t '.unit'
%th= t '.category'
%th= t '.actions'
%tfoot
%tr
%th{:colspan => 5}
- if articles_for_select2(@supplier).empty?
= link_to t('.create_stock_article'), new_stock_article_supplier_deliveries_path(@supplier), :remote => true, :class => 'btn'
- else
%input#new_stock_article{:style => 'width: 500px;'}
%tbody
- for article in stock_articles_for_table(@supplier)
= render :partial => 'stock_article_for_adding', :locals => {:article => article}
%h2= t '.title_fill_quantities'
%table.table#stock_changes.stupidtable
%thead
%tr
%th.default-sort{:data => {:sort => 'string'}}= t '.article'
%th= t '.price'
%th= t '.unit'
%th= t '.quantity'
%th= t '.actions'
%tbody
= f.simple_fields_for :stock_changes do |stock_change_form|
= render :partial => 'stock_change_fields', :locals => {:f => stock_change_form}
%h2= t '.title_finish_delivery'
= f.input :delivered_on, as: :date_picker
= f.input :note, input_html: {size: '35x4'}
.form-actions
= f.submit class: 'btn btn-primary'
= link_to t('ui.or_cancel'), supplier_deliveries_path(@supplier)
/
TODO: Fix this!!
.span6
%h2= t '.new_article.title'
%p
= t('.new_article.search', supplier: @supplier.name).html_safe + ': '
= text_field_tag 'article_name'
%hr/
#stock_article_form
= render 'stock_article_form', stock_article: @supplier.stock_articles.build

View file

@ -0,0 +1,11 @@
- css_class = ( @delivery and @delivery.includes_article? article ) ? ( 'unavailable' ) : ( false )
%tr{:id => "stock_article_#{article.id}", :class => css_class}
%td= article.name
%td{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => article})}}= number_to_currency article.price
%td= article.unit
%td= article.article_category.name
%td
= link_to t('.action_edit'), edit_stock_article_supplier_deliveries_path(@supplier, :stock_article_id => article.id), remote: true, class: 'btn btn-mini'
= link_to t('.action_other_price'), copy_stock_article_supplier_deliveries_path(@supplier, :old_stock_article_id => article.id), remote: true, class: 'btn btn-mini'
- deliver_button_disabled = ( @delivery and @delivery.includes_article? article ) ? ( 'disabled' ) : ( false )
= link_to t('.action_add_to_delivery'), add_stock_change_supplier_deliveries_path(@supplier, :stock_article_id => article.id), :method => :post, remote: true, class: 'button-add-stock-change btn btn-mini btn-primary', disabled: deliver_button_disabled

View file

@ -1,14 +1,23 @@
= simple_form_for stock_article, url: add_stock_article_supplier_deliveries_path(@supplier), remote: true,
validate: true do |f|
= f.hidden_field :supplier_id
= f.input :name
= f.input :unit
= f.input :note
= f.input :price
= f.input :tax, :wrapper => :append do
= f.input_field :tax
%span.add-on %
-# untested, because this view is currently not included (?)
= f.input :deposit
= f.association :article_category
= f.submit class: 'btn'
- url = ( stock_article.new_record? ) ? ( create_stock_article_supplier_deliveries_path(@supplier) ) : ( update_stock_article_supplier_deliveries_path(@supplier) )
= simple_form_for stock_article, url: url, remote: true, validate: true do |f|
= f.association :supplier, :as => :hidden
= f.hidden_field :id unless stock_article.new_record?
.modal-header
= link_to t('ui.marks.close').html_safe, '#', class: 'close', data: {dismiss: 'modal'}
%h3= t 'activerecord.models.stock_article'
.modal-body
= f.input :name
= f.input :unit
= f.input :note
- if stock_article.new_record?
= f.input :price
= f.input :tax, :wrapper => :append do
= f.input_field :tax
%span.add-on %
= f.input :deposit
- else
= f.input :price, :input_html => {:disabled => 'disabled'}, :hint => stock_article_price_hint(stock_article)
= f.association :article_category
.modal-footer
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
= f.submit :class => 'btn btn-primary', 'data-disable-with' => t('ui.please_wait')

View file

@ -1,6 +1,6 @@
%p
= fields_for "delivery[new_stock_changes][]", stock_change do |form|
= form.select :stock_article_id, stock_articles_for_select(supplier)
Menge
= form.text_field :quantity, :size => 5, :autocomplete => 'off'
= link_to t('.remove_article'), "#", :class => 'remove_new_stock_change'
- if stock_change.stock_article.new_record?
= simple_fields_for "delivery[new_stock_changes_new_stock_article][]", stock_change do |f|
= render :partial => 'stock_change_fields', :locals => {:f => f}
- else
= simple_fields_for "delivery[new_stock_changes][]", stock_change do |f|
= render :partial => 'stock_change_fields', :locals => {:f => f}

View file

@ -0,0 +1,10 @@
- stock_change = f.object
- stock_article = stock_change.stock_article
%tr{:id => "stock_change_stock_article_#{stock_article.id}", :data => {:id => stock_article.id}}
%td
%span.stock_article_name= stock_change.stock_article.name
= f.association :stock_article, :as => :hidden
%td.price{:data => {:toggle => :tooltip, :title => render(:partial => 'shared/article_price_info', :locals => {:article => stock_article})}}= number_to_currency stock_article.price
%td.unit= stock_change.stock_article.unit
%td= f.input :quantity, :wrapper => :intable, :input_html => {:class => 'stock-change-quantity', :autocomplete => :off}
%td= stock_change_remove_link f

View file

@ -0,0 +1,25 @@
(function(w) {
if(!is_article_available_for_delivery(<%= @stock_change.stock_article.id %>)) {
return false;
}
$('#stock_changes tr').removeClass('success');
var stock_change = $(
'<%= j(render(:partial => 'stock_change', :locals => {:stock_change => @stock_change})) %>'
).addClass('success');
enablePriceTooltips(stock_change);
$('#stock_changes').append(stock_change);
mark_article_for_delivery(<%= @stock_change.stock_article.id %>);
updateSort('#stock_changes');
var quantity = w.prompt('<%= j(t('.how_many_units', :unit => @stock_change.stock_article.unit, :name => @stock_change.stock_article.name)) %>'); <%# how to properly escape here? %>
if(null === quantity) {
stock_change.remove();
mark_article_for_delivery(<%= @stock_change.stock_article.id %>);
return false;
}
$('input.stock-change-quantity', stock_change).val(quantity);
})(window);

View file

@ -1 +0,0 @@
$('#stock_changes').append('#{escape_javascript(render(:partial => 'stock_change', :locals => {:stock_change => StockChange.new, :supplier => @supplier}))}');

View file

@ -0,0 +1,5 @@
$('#modalContainer').html(
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
);
$('#modalContainer').modal();

View file

@ -0,0 +1,17 @@
$('div.container-fluid').prepend(
'<%= j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.notice', :name => @stock_article.name)})) %>'
);
(function() {
$('#stock_articles_for_adding tr').removeClass('success');
var stock_article_for_adding = $(
'<%= j(render(:partial => 'stock_article_for_adding', :locals => {:article => @stock_article})) %>'
).addClass('success');
enablePriceTooltips(stock_article_for_adding);
$('#stock_articles_for_adding tbody').append(stock_article_for_adding);
updateSort('#stock_articles_for_adding');
})();
$('#modalContainer').modal('hide');

View file

@ -0,0 +1,5 @@
$('#modalContainer').html(
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
);
$('#modalContainer').modal();

View file

@ -0,0 +1,5 @@
$('#modalContainer').html(
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
);
$('#modalContainer').modal();

View file

@ -0,0 +1,5 @@
$('#modalContainer').html(
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
);
$('#modalContainer').modal();

View file

@ -0,0 +1,33 @@
$('div.container-fluid').prepend(
'<%= j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.notice', :name => @stock_article.name)})) %>'
);
(function() {
// update entry in stock_article table
$('#stock_articles_for_adding tr').removeClass('success');
var stock_article_for_adding = $(
'<%= j(render(:partial => 'stock_article_for_adding', :locals => {:article => @stock_article, :delivery => @delivery})) %>'
).addClass('success');
enablePriceTooltips(stock_article_for_adding);
$('#stock_article_<%= @stock_article.id %>').replaceWith(stock_article_for_adding);
updateSort('#stock_articles_for_adding');
mark_article_for_delivery(<%= @stock_article.id %>);
// update entry in stock_changes table
$('#stock_changes tr').removeClass('success');
var stock_change_entry = $('#stock_change_stock_article_<%= @stock_article.id %>');
$('.stock_article_name', stock_change_entry).text('<%= j(@stock_article.name) %>');
$('.unit', stock_change_entry).text('<%= j(@stock_article.unit) %>');
stock_change_entry.addClass('success');
updateSort('#stock_changes');
})();
$('#modalContainer').modal('hide');

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
@ -17,4 +17,4 @@
%td
= link_to t('.new_transaction'), new_finance_ordergroup_transaction_path(ordergroup), class: 'btn btn-mini'
= link_to t('.account_statement'), finance_ordergroup_transactions_path(ordergroup), class: 'btn btn-mini'

View file

@ -14,9 +14,9 @@
- for user in @users
%tr
%td= user.nick
%td= user.name if @current_user.role_admin? || user.settings["profile.nameIsPublic"] == '1'
%td= user.email if @current_user.role_admin? || user.settings["profile.emailIsPublic"] == '1'
%td= user.phone if @current_user.role_admin? || user.settings["profile.phoneIsPublic"] == '1'
%td= user.name if @current_user.role_admin? || user.settings.profile["name_is_public"]
%td= user.email if @current_user.role_admin? || user.settings.profile["email_is_public"]
%td= user.phone if @current_user.role_admin? || user.settings.profile["phone_is_public"]
%td= user.ordergroup_name
%td= user.workgroups.collect(&:name).join(', ')
%td= link_to_new_message(message_params: {mail_to: user.id})

View file

@ -4,6 +4,7 @@
#{data_to_js(@ordering_data)}
setGroupBalance(#{@ordering_data[:available_funds]});
setCurrencyFormat("#{t('number.currency.format.separator')}", #{t('number.currency.format.precision')}, "#{t('number.currency.format.unit')}");
setMinimumBalance(#{FoodsoftConfig[:minimum_balance] or 0});
setToleranceBehaviour(#{FoodsoftConfig[:tolerance_is_costly]});
setStockit(#{@order.stockit?});
});

View file

@ -22,5 +22,7 @@
Javascripts
\==================================================
/ Placed at the end of the document so the pages load faster
:javascript
I18n = {locale: '#{j(I18n.locale.to_s)}'}
= javascript_include_tag "application"
= yield(:javascript)

View file

@ -45,26 +45,6 @@
= f.label :role_orders
%br/
= f.check_box :role_orders
%p
= f.label :weekly_task
%br/
= f.check_box :weekly_task
%p
= f.label :weekday
%br/
= f.text_field :weekday
%p
= f.label :task_name
%br/
= f.text_field :task_name
%p
= f.label :task_description
%br/
= f.text_field :task_description
%p
= f.label :task_required_users
%br/
= f.text_field :task_required_users
%p
= f.label :deleted_at
%br/

View file

@ -12,11 +12,6 @@
%th Role Article Meta
%th Role Finance
%th Role Orders
%th Weekly Task
%th Weekday
%th Task Name
%th Task Description
%th Task Required Users
%th Deleted At
%th Contact Person
%th Contact Phone
@ -34,11 +29,6 @@
%td= h ordergroup.role_article_meta
%td= h ordergroup.role_finance
%td= h ordergroup.role_orders
%td= h ordergroup.weekly_task
%td= h ordergroup.weekday
%td= h ordergroup.task_name
%td= h ordergroup.task_description
%td= h ordergroup.task_required_users
%td= h ordergroup.deleted_at
%td= h ordergroup.contact_person
%td= h ordergroup.contact_phone

View file

@ -29,7 +29,10 @@
- if order.stockit?
%td= units
- else
%td= "#{order_article.quantity} + #{order_article.tolerance}" if unit_quantity > 1
- if unit_quantity > 1 or order_article.tolerance > 0
%td= "#{order_article.quantity} + #{order_article.tolerance}"
- else
%td= "#{order_article.quantity}"
%td= units
%p
= t '.prices_sum'

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

@ -0,0 +1,5 @@
.alert.fade.in.alert-success
%a.close{:href => '#', :data => {:dismiss => 'alert'}}
= t('ui.marks.close').html_safe
= t('ui.marks.success').html_safe
= alert_message

View file

@ -0,0 +1,17 @@
%table.table.table-condensed
%tr
%th= t 'activerecord.attributes.article.price'
%td.numeric= number_to_currency article.price
%tr
%th= t 'activerecord.attributes.article.deposit'
%td.numeric= number_to_currency article.deposit
%tr
%th= t 'activerecord.attributes.article.tax'
%td.numeric= number_to_percentage article.tax
- unless article.fc_price == article.gross_price
%tr
%th= t 'activerecord.attributes.article.fc_share'
%td.numeric= number_to_currency(article.fc_price-article.gross_price)
%tr
%th= t 'activerecord.attributes.article.fc_price'
%td.numeric= number_to_currency article.fc_price

View file

@ -2,8 +2,10 @@
%thead
%tr
%th{:style => 'width:70%'}= t '.ordergroup'
%th= t '.ordered'
%th= t '.received'
%th
%acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
%th
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
%th= t '.price'
- for order_article in order.order_articles.ordered.all(:include => [:article, :article_price])
@ -13,8 +15,8 @@
= order_article.article.name
= "(#{order_article.article.unit} | #{order_article.price.unit_quantity} | #{number_to_currency(order_article.price.gross_price)})"
%tbody
- for goa in order_article.group_order_articles
%tr{:class => cycle('even', 'odd', :name => 'groups')}
- for goa in order_article.group_order_articles.ordered
%tr{:class => [cycle('even', 'odd', :name => 'groups'), if goa.result == 0 then 'unavailable' end]}
%td{:style => "width:70%"}=h goa.group_order.ordergroup.name
%td= "#{goa.quantity} + #{goa.tolerance}"
%td

View file

@ -3,7 +3,9 @@
%tr
%th{:style => "width:40%"}= t '.name'
%th
%acronym{:title => t('.units_desc')}= t '.units'
%acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
%th
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
%th
%acronym{:title => t('.fc_price_desc')}= t '.fc_price'
%th
@ -11,10 +13,10 @@
%th= t '.unit'
%th= t '.price'
- for group_order in order.group_orders.all
- for group_order in order.group_orders.ordered
%thead
%tr
%th{:colspan => "6"}
%th{:colspan => "7"}
%h4= group_order.ordergroup.name
%tbody
- total = 0
@ -22,17 +24,19 @@
- fc_price = goa.order_article.price.fc_price
- subTotal = fc_price * goa.result
- total += subTotal
%tr{:class => cycle('even', 'odd', :name => 'articles')}
%tr{:class => [cycle('even', 'odd', :name => 'articles'), if goa.result == 0 then 'unavailable' end]}
%td{:style => "width:40%"}=h goa.order_article.article.name
%td= goa.result
%td= "#{goa.quantity} + #{goa.tolerance}"
%td
%b= goa.result
%td= number_to_currency(fc_price)
%td= goa.order_article.price.unit_quantity
%td= goa.order_article.article.unit
%td= number_to_currency(subTotal)
%tr{:class => cycle('even', 'odd', :name => 'articles')}
%th{:colspan => "5"} Summe
%th{:colspan => "6"} Summe
%th= number_to_currency(total)
%tr
%th(colspan="6")
%th(colspan="7")
- reset_cycle("articles")

View file

@ -0,0 +1,5 @@
.alert.alert-error.alert-block
%a.close{:href => '#', :data => {:dismiss => 'alert'}}
= t('ui.marks.close').html_safe
%ul
= error_messages.html_safe

View file

@ -13,13 +13,6 @@
- members = group.users
= "(#{members.size})"
= members.collect(&:nick).join(", ")
- if group.is_a?(Workgroup)
%dt= t('.weekly_job') + ':'
%dd
- if group.weekly_task
=h "#{group.task_name} am #{weekday(group.weekday)}"
- else
= t '.no_weekly_job'
- else
- unless group.is_a?(Workgroup)
%dt= t '.apple_limit'
%dd= group.ignore_apple_restriction ? t('.deactivated') : t('.activated')

View file

@ -3,17 +3,6 @@
= yield
- if f.object.is_a?(Workgroup)
%h3= t '.title'
= f.input :weekly_task
#weekly_task_fields
= f.input :weekday, as: :select, collection: Workgroup.weekdays
= f.input :task_name
= f.input :task_required_users
= f.input :task_duration, :as => :select, :collection => (1..3)
= f.input :task_description, as: :text, input_html: {rows: 5}
= f.input :next_weekly_tasks_number
= f.input :user_tokens, :as => :string,
:input_html => { 'data-pre' => f.object.users.map { |u| u.token_attributes }.to_json }

View file

@ -5,11 +5,31 @@
= f.input :phone
= f.input :password, :required => f.object.new_record?
= f.input :password_confirmation
.control-group
.controls
- for setting in User::setting_keys.keys
%label.checkbox{:for => "user[setting_attributes][#{setting}]"}
= hidden_field_tag "user[setting_attributes][#{setting}]", '0'
= check_box_tag "user[setting_attributes][#{setting}]", '1',
f.object.settings[setting] == '1' || f.object.settings_default(setting)
= User::setting_keys[setting]
= f.simple_fields_for :settings_attributes do |s|
= s.simple_fields_for :profile, defaults: { inline_label: true } do |profile|
= profile.input 'language', as: :select, collection: available_locales, required: false, selected: f.object.settings.profile['language']
.settings
.settings-group
= s.simple_fields_for :profile, defaults: { inline_label: true } do |profile|
%div{class: 'control-group h_wrapper'}
%h5{class: 'controls'}
= t 'simple_form.labels.settings.settings_group.privacy'
= profile.input 'phone_is_public', as: :boolean, label: false, input_html: { checked: f.object.settings.profile['phone_is_public'] }
= profile.input 'email_is_public', as: :boolean, label: false, input_html: { checked: f.object.settings.profile['email_is_public'] }
= profile.input 'name_is_public', as: :boolean, label: false, input_html: { checked: f.object.settings.profile['name_is_public'] }
.settings-group
%div{class: 'control-group'}
%h5{class: 'controls'}
= t 'simple_form.labels.settings.settings_group.messages'
= s.simple_fields_for :messages, defaults: { inline_label: true, label: false } do |messages|
= messages.input 'send_as_email', as: :boolean, input_html: { checked: f.object.settings.messages['send_as_email'] }
= s.simple_fields_for :notify, defaults: { inline_label: true, label: false } do |notify|
= notify.input 'order_finished', as: :boolean, input_html: { checked: f.object.settings.notify['order_finished'] }
= notify.input 'negative_balance', as: :boolean, input_html: { checked: f.object.settings.notify['negative_balance'] }
= notify.input 'upcoming_tasks', as: :boolean, input_html: { checked: f.object.settings.notify['upcoming_tasks'] }

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

@ -4,11 +4,5 @@ var successDiv = $('<div class="alert fade in alert-success"><a class="close" da
successDiv.append(document.createTextNode('#{escape_javascript(t('.notice', name: @article.name))}'));
$('div.container-fluid').prepend(successDiv);
-# WARNING: If you try to use the escape j(...) here, an error occurs:
-# Ein Fehler ist aufgetreten: undefined method `gsub' for 50:Fixnum
-# However, it should work without without escaping.
-# Note that article names which are purely numeric, e.g. 12345, are escaped correctly (see above).
$('#stockArticle-#{@article.id}').remove();
-# WARNING: Do not use a simple .fadeOut() above, because it conflicts with the show/hide function of unavailable articles.

View file

@ -0,0 +1,17 @@
- title t('.stock_changes', :article_name => @stock_article.name)
%table.table.table-hover#stock_changes
%thead
%tr
%th= t '.datetime'
%th= t '.reason'
%th= t '.change_quantity'
%th= t '.new_quantity'
%tbody
- reversed_history = @stock_article.quantity_history.reverse
- @stock_changes.each_with_index do |stock_change, index|
%tr
%td= l stock_change.created_at
%td= link_to_stock_change_reason(stock_change)
%td= stock_change.quantity
%td= reversed_history[index]

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() {
@ -56,6 +56,7 @@
%td= article.article_category.name
%td
= link_to t('ui.edit'), edit_stock_article_path(article), class: 'btn btn-mini'
= link_to t('ui.history'), stock_article_history_path(article), class: 'btn btn-mini'
= link_to t('ui.delete'), article, :method => :delete, :confirm => t('.confirm_delete'),
class: 'btn btn-mini btn-danger', :remote => true
%p

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

@ -25,5 +25,7 @@
= f.input :due_date, as: :date_picker
= f.input :done
.form-actions
= f.submit class: 'btn'
= f.submit class: 'btn btn-primary'
- if @task.new_record?
= f.submit t('.submit.periodic'), name: 'periodic', class: 'btn'
= link_to t('ui.or_cancel'), :back

View file

@ -2,6 +2,7 @@
%thead
%tr
%th= t '.due_date'
%th
%th= t '.task'
%th{:colspan => '2'}
= t '.who'
@ -11,6 +12,9 @@
- done = task.done ? " done" : ""
%tr{:class => done }
%td= format_date(task.due_date) unless task.due_date.nil?
%td
- if task.periodic?
%i.icon-repeat{title: t('tasks.repeated')}
%td= link_to t('.task_format', name: task.name, duration: task.duration), task_path(task)
%td
= task_assignments task

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

@ -10,7 +10,10 @@
%dd= simple_format(@task.description)
- if @task.due_date.present?
%dt= t '.due_date'
%dd= format_date(@task.due_date)
%dd
= format_date(@task.due_date)
- if @task.periodic?
%i.icon-repeat{title: t('tasks.repeated')}
%dt= t 'simple_form.labels.task.duration'
%dd= t('.hours', count: @task.duration)
%dt= t 'simple_form.labels.task.user_list'
@ -29,3 +32,6 @@
= link_to t('ui.edit'), edit_task_path(@task), class: 'btn'
= link_to t('ui.delete'), task_path(@task), :method => :delete, :confirm => "Die Aufgabe wirklich löschen?",
class: 'btn btn-danger'
- if @task.periodic?
= link_to t('.delete_group'), task_path(@task, periodic: true), method: :delete,
confirm: t('.confirm_delete_group'), class: 'btn btn-danger'

View file

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

View file

@ -1,16 +1,6 @@
- title t('.title', workgroup: @group.name)
= render 'nav'
%section.well
%h3= t '.weekly.title'
- if @group.weekly_task
= t('.weekly.desc', weekday: weekday(@group.weekday), task: @group.task_name).html_safe
- else
= t('.weekly.empty').html_safe
- if @current_user.member_of?(@group) or @current_user.role_admin?
= link_to t('.weekly.edit'), edit_foodcoop_workgroup_path(@group), class: 'btn'
%section
%h3= t '.title_all'
= render 'list', tasks: @group.open_tasks

View file

@ -45,26 +45,6 @@
= f.label :role_orders
%br/
= f.check_box :role_orders
%p
= f.label :weekly_task
%br/
= f.check_box :weekly_task
%p
= f.label :weekday
%br/
= f.text_field :weekday
%p
= f.label :task_name
%br/
= f.text_field :task_name
%p
= f.label :task_description
%br/
= f.text_field :task_description
%p
= f.label :task_required_users
%br/
= f.text_field :task_required_users
%p
= f.label :deleted_at
%br/

View file

@ -12,11 +12,6 @@
%th Role Article Meta
%th Role Finance
%th Role Orders
%th Weekly Task
%th Weekday
%th Task Name
%th Task Description
%th Task Required Users
%th Deleted At
%th Contact Person
%th Contact Phone
@ -34,11 +29,6 @@
%td= h workgroup.role_article_meta
%td= h workgroup.role_finance
%td= h workgroup.role_orders
%td= h workgroup.weekly_task
%td= h workgroup.weekday
%td= h workgroup.task_name
%td= h workgroup.task_description
%td= h workgroup.task_required_users
%td= h workgroup.deleted_at
%td= h workgroup.contact_person
%td= h workgroup.contact_phone

View file

@ -18,7 +18,7 @@ class UserNotifier
Order.find(order_id).group_orders.each do |group_order|
group_order.ordergroup.users.each do |user|
begin
Mailer.order_result(user, group_order).deliver if user.settings["notify.orderFinished"] == '1'
Mailer.order_result(user, group_order).deliver if user.settings.notify["order_finished"]
rescue
Rails.logger.warn "Can't deliver mail to #{user.email}"
end
@ -34,7 +34,7 @@ class UserNotifier
Ordergroup.find(ordergroup_id).users.each do |user|
begin
Mailer.negative_balance(user, transaction).deliver if user.settings["notify.negativeBalance"] == '1'
Mailer.negative_balance(user, transaction).deliver if user.settings.notify["negative_balance"]
rescue
Rails.logger.warn "Can't deliver mail to #{user.email}"
end