Merge branch 'master' into fix-closed-group_order-totals
Conflicts: db/schema.rb
This commit is contained in:
commit
ebb22ccb53
137 changed files with 4484 additions and 1507 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
186
app/assets/javascripts/stupidtable.js
Normal file
186
app/assets/javascripts/stupidtable.js
Normal 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');
|
||||
}
|
||||
});
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
/*
|
||||
*= require bootstrap_and_overrides
|
||||
*= require select2
|
||||
*= require token-input-bootstrappy
|
||||
*= require bootstrap-datepicker
|
||||
*/
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
6
app/helpers/suppliers_helper.rb
Normal file
6
app/helpers/suppliers_helper.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
module SuppliersHelper
|
||||
|
||||
def associated_supplier_names(shared_supplier)
|
||||
"(#{shared_supplier.suppliers.map(&:name).join(', ')})"
|
||||
end
|
||||
end
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
29
app/models/periodic_task_group.rb
Normal file
29
app/models/periodic_task_group.rb
Normal 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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
11
app/views/deliveries/_stock_article_for_adding.html.haml
Normal file
11
app/views/deliveries/_stock_article_for_adding.html.haml
Normal 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
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
10
app/views/deliveries/_stock_change_fields.html.haml
Normal file
10
app/views/deliveries/_stock_change_fields.html.haml
Normal 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
|
||||
25
app/views/deliveries/add_stock_change.js.erb
Normal file
25
app/views/deliveries/add_stock_change.js.erb
Normal 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);
|
||||
|
|
@ -1 +0,0 @@
|
|||
$('#stock_changes').append('#{escape_javascript(render(:partial => 'stock_change', :locals => {:stock_change => StockChange.new, :supplier => @supplier}))}');
|
||||
5
app/views/deliveries/copy_stock_article.js.erb
Normal file
5
app/views/deliveries/copy_stock_article.js.erb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
$('#modalContainer').html(
|
||||
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
|
||||
);
|
||||
|
||||
$('#modalContainer').modal();
|
||||
17
app/views/deliveries/create_stock_article.js.erb
Normal file
17
app/views/deliveries/create_stock_article.js.erb
Normal 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');
|
||||
5
app/views/deliveries/derive_stock_article.js.erb
Normal file
5
app/views/deliveries/derive_stock_article.js.erb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
$('#modalContainer').html(
|
||||
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
|
||||
);
|
||||
|
||||
$('#modalContainer').modal();
|
||||
5
app/views/deliveries/edit_stock_article.js.erb
Normal file
5
app/views/deliveries/edit_stock_article.js.erb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
$('#modalContainer').html(
|
||||
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
|
||||
);
|
||||
|
||||
$('#modalContainer').modal();
|
||||
5
app/views/deliveries/new_stock_article.js.erb
Normal file
5
app/views/deliveries/new_stock_article.js.erb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
$('#modalContainer').html(
|
||||
'<%= j(render(:partial => "stock_article_form", :locals => {:stock_article => @stock_article})) %>'
|
||||
);
|
||||
|
||||
$('#modalContainer').modal();
|
||||
33
app/views/deliveries/update_stock_article.js.erb
Normal file
33
app/views/deliveries/update_stock_article.js.erb
Normal 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');
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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?});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
5
app/views/shared/_alert_success.haml
Normal file
5
app/views/shared/_alert_success.haml
Normal 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
|
||||
17
app/views/shared/_article_price_info.html.haml
Normal file
17
app/views/shared/_article_price_info.html.haml
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
5
app/views/shared/_base_errors.haml
Normal file
5
app/views/shared/_base_errors.haml
Normal 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
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
|
|
@ -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'] }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
17
app/views/stockit/history.haml
Normal file
17
app/views/stockit/history.haml
Normal 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]
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
- title "Meine Aufgaben"
|
||||
- title t('.title')
|
||||
= render 'nav'
|
||||
|
||||
- unless @unaccepted_tasks.empty?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue