Import current_orders plugin from foodcoop-adam

This commit is contained in:
wvengen 2015-04-10 20:18:49 +02:00
parent ae4979a3f0
commit 5907ff11bb
43 changed files with 1031 additions and 0 deletions

View file

@ -0,0 +1,16 @@
FoodsoftCurrentOrders
=====================
Quick support for working with all current orders, to bridge the time until we
have full support for order cycles in foodsoft.
* `current_orders/ordergroups` to edit an ordergroup's order articles for all
orders that are closed but not settled.
* `current_orders/articles` to edit an order article's ordergroups for all
orders that are closed but not settled.
* `current_orders/orders.pdf?document=(groups|articles)` for PDFs for all
orders that are closed but not settled.
* `current_orders/orders/receive` for a list of orders that can be received.
* `current_orders/group_orders` for all articles in the user's group orders
from orders that are not settled. Can be used as a "shopping-cart overview"
page.

View file

@ -0,0 +1,43 @@
# encoding: utf-8
class CurrentOrders::ArticlesController < ApplicationController
before_filter :authenticate_orders
before_filter :find_order_and_order_article, only: [:index, :show]
def index
# sometimes need to pass id as parameter for forms
show if @order_article
end
def show
respond_to do |format|
format.html { render :show }
format.js { render :show, layout: false }
end
end
def show_on_group_order_article_create
@goa = GroupOrderArticle.find(params[:group_order_article_id])
end
def show_on_group_order_article_update
#@goa = GroupOrderArticle.find(params[:group_order_article_id])
end
protected
def find_order_and_order_article
@current_orders = Order.finished_not_closed
unless params[:order_id].blank?
@order = Order.find(params[:order_id])
@order_articles = @order.order_articles
else
@order_articles = OrderArticle.where(order_id: @current_orders.all.map(&:id))
end
params[:q] ||= params[:search] # for meta_search instead of ransack
@q = OrderArticle.search(params[:q])
@order_articles = @order_articles.ordered.merge(@q.relation).includes(:article, :article_price)
@order_article = @order_articles.where(id: params[:id]).first
end
end

View file

@ -0,0 +1,26 @@
class CurrentOrders::GroupOrdersController < ApplicationController
# Security
before_filter :ensure_ordergroup_member
def index
# XXX code duplication lib/foodsoft_current_orders/app/controllers/current_orders/ordergroups_controller.rb
@order_ids = Order.where(state: ['open', 'finished']).all.map(&:id)
@goas = GroupOrderArticle.includes(:group_order => :ordergroup).includes(:order_article).
where(group_orders: {order_id: @order_ids, ordergroup_id: @ordergroup.id}).ordered
@articles_grouped_by_category = @goas.includes(:order_article => {:article => :article_category}).
order('articles.name').
group_by { |a| a.order_article.article.article_category.name }.
sort { |a, b| a[0] <=> b[0] }
end
private
# XXX code duplication from GroupOrdersController
def ensure_ordergroup_member
@ordergroup = @current_user.ordergroup
if @ordergroup.nil?
redirect_to root_url, :alert => I18n.t('group_orders.errors.no_member')
end
end
end

View file

@ -0,0 +1,43 @@
# encoding: utf-8
class CurrentOrders::OrdergroupsController < ApplicationController
before_filter :authenticate_orders
before_filter :find_group_orders, only: [:index, :show]
def index
# sometimes need to pass id as parameter for forms
render 'show' if @ordergroup
end
def show
respond_to do |format|
format.html { render :show }
format.js { render :show, layout: false }
end
end
def show_on_group_order_article_create
@goa = GroupOrderArticle.find(params[:group_order_article_id])
end
def show_on_group_order_article_update
#@goa = GroupOrderArticle.find(params[:group_order_article_id])
@group_order = GroupOrder.find(params[:group_order_id])
@ordergroup = @group_order.ordergroup
end
protected
def find_group_orders
@order_ids = Order.finished_not_closed.map(&:id)
@all_ordergroups = Ordergroup.undeleted.order(:name).all
@ordered_group_ids = GroupOrder.where(order_id: @order_ids).pluck('DISTINCT(ordergroup_id)')
@all_ordergroups.sort_by! {|o| @ordered_group_ids.include?(o.id) ? o.name : "ZZZZZ#{o.name}" }
@ordergroup = Ordergroup.find(params[:id]) unless params[:id].nil?
@goas = GroupOrderArticle.includes(:group_order, :order_article => [:article, :article_price]).
where(group_orders: {order_id: @order_ids, ordergroup_id: @ordergroup.id}).ordered.all unless @ordergroup.nil?
end
end

View file

@ -0,0 +1,40 @@
class CurrentOrders::OrdersController < ApplicationController
before_filter :authenticate_orders, except: :my
def show
@doc_options ||= {}
@order_ids = if params[:id]
params[:id].split('+').map(&:to_i)
else
Order.finished_not_closed.all.map(&:id)
end
@view = (params[:view] or 'default').gsub(/[^-_a-zA-Z0-9]/, '')
respond_to do |format|
format.pdf do
pdf = case params[:document]
when 'groups' then MultipleOrdersByGroups.new(@order_ids, @doc_options)
when 'articles' then MultipleOrdersByArticles.new(@order_ids, @doc_options)
end
send_data pdf.to_pdf, filename: pdf.filename, type: 'application/pdf'
end
end
end
def my
@doc_options ||= {}
@doc_options[:ordergroup] = @current_user.ordergroup.id
respond_to do |format|
format.pdf do
params[:document] = 'groups'
show
end
end
end
def receive
@orders = Order.finished_not_closed.includes(:supplier)
end
end

View file

@ -0,0 +1,6 @@
# encoding: utf-8
class CurrentOrdersController < ApplicationController
before_filter :authenticate_orders
end

View file

@ -0,0 +1,79 @@
# encoding: utf-8
class MultipleOrdersByArticles < OrderPdf
include OrdersHelper
def filename
I18n.t('documents.multiple_orders_by_articles.filename', count: @order.count) + '.pdf'
end
def title
I18n.t('documents.multiple_orders_by_articles.title', count: @order.count)
end
def order_articles
@order_articles ||= OrderArticle.joins(:order, :article).where(:orders => {:id => @order}).ordered.reorder('orders.id, articles.name')
end
def body
order_articles.each do |order_article|
down_or_page
rows = []
dimrows = []
has_units_str = ''
for goa in order_article.group_order_articles.ordered
units = result_in_units(goa, order_article.article)
rows << [show_group(goa.group_order.ordergroup),
goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : goa.quantity,
goa.result,
units,
number_to_currency(goa.total_price(order_article))]
dimrows << rows.length if goa.result == 0
has_units_str = units.to_s if units.to_s.length > has_units_str.length # hack for prawn line-breaking units cell
end
next if rows.length == 0
sum = order_article.group_orders_sum
rows.unshift I18n.t('documents.order_by_articles.rows').dup # table header
rows[0][2] = {content: rows[0][2], colspan: 2}
rows << [I18n.t('documents.order_by_groups.sum'),
order_article.tolerance > 0 ? "#{order_article.quantity} + #{order_article.tolerance}" : order_article.quantity,
sum[:quantity],
result_in_units(sum[:quantity], order_article.article),
nil] #number_to_currency(sum[:price])]
text "<b>#{order_article.article.name}</b> " +
"(#{order_article.article.unit}; #{number_to_currency order_article.price.fc_price}; " +
units_history_line(order_article, nil, plain: true) + ')',
size: fontsize(10), inline_format: true
s = ::OrderByArticles.article_info(order_article.article) and text s, size: fontsize(8), inline_format: true
table rows, cell_style: {size: fontsize(8), overflow: :shrink_to_fit} do |table|
# borders
table.cells.borders = [:bottom]
table.cells.border_width = 0.02
table.cells.border_color = 'dddddd'
table.rows(0).border_width = 1
table.rows(0).border_color = '666666'
table.row(rows.length-2).border_width = 1
table.row(rows.length-2).border_color = '666666'
table.row(rows.length-1).borders = []
table.column(0).width = 200
table.columns(1..2).align = :center
table.column(2..3).font_style = :bold
table.columns(3).width = width_of(has_units_str)
table.columns(3..4).align = :right
# dim rows which were ordered but not received
dimrows.each { |ri| table.row(ri).text_color = '999999' }
end
end
end
protected
def pdf_add_page_breaks?
super 'order_by_articles'
end
end

View file

@ -0,0 +1,125 @@
# encoding: utf-8
class MultipleOrdersByGroups < OrderPdf
include OrdersHelper
def filename
I18n.t('documents.multiple_orders_by_groups.filename', count: @order.count) + '.pdf'
end
def title
I18n.t('documents.multiple_orders_by_groups.title', count: @order.count)
end
def ordergroups
unless @ordergroups
@ordergroups = Ordergroup.joins(:orders).where(orders: {id: @order}).select('distinct(groups.id)').select('groups.*').reorder(:name)
@ordergroups = @ordergroups.where(id: @options[:ordergroup]) if @options[:ordergroup]
end
@ordergroups
end
def body
# Start rendering
ordergroups.each do |ordergroup|
down_or_page 15
totals = {net_price: 0, deposit: 0, gross_price: 0, fc_price: 0, fc_markup_price: 0}
taxes = Hash.new {0}
rows = []
dimrows = []
group_order_articles = GroupOrderArticle.ordered.joins(:group_order => :order).where(:group_orders =>{:ordergroup_id => ordergroup.id}).where(:orders => {id: @order}).includes(:order_article => :article_price).reorder('orders.id')
has_tolerance = group_order_articles.where('article_prices.unit_quantity > 1').any?
group_order_articles.each do |goa|
price = goa.order_article.price
goa_totals = goa.total_prices
totals[:net_price] += goa_totals[:net_price]
totals[:deposit] += goa_totals[:deposit]
totals[:gross_price] += goa_totals[:gross_price]
totals[:fc_price] += goa_totals[:price]
totals[:fc_markup_price] += goa_totals[:fc_markup_price]
taxes[goa.order_article.price.tax.to_f.round(2)] += goa_totals[:fc_tax_price]
rows << [goa.order_article.article.name,
goa.group_order.order.name.truncate(10, omission: ''),
number_to_currency(price.fc_price(goa.group_order.ordergroup)),
goa.order_article.article.unit,
goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : goa.quantity,
goa.result,
result_in_units(goa),
number_to_currency(goa_totals[:price]),
(goa.order_article.price.unit_quantity if has_tolerance)]
dimrows << rows.length if goa.result == 0
end
next if rows.length == 0
# total
rows << [{content: I18n.t('documents.order_by_groups.sum'), colspan: 7}, number_to_currency(totals[:fc_price]), nil]
# price details
price_details = []
price_details << "#{Article.human_attribute_name :price} #{number_to_currency totals[:net_price]}" if totals[:net_price] > 0
price_details << "#{Article.human_attribute_name :deposit} #{number_to_currency totals[:deposit]}" if totals[:deposit] > 0
taxes.each do |tax, tax_price|
price_details << "#{Article.human_attribute_name :tax} #{number_to_percentage tax} #{number_to_currency tax_price}" if tax_price > 0
end
price_details << "#{Article.human_attribute_name :fc_share_short} #{number_to_percentage ordergroup.markup_pct} #{number_to_currency totals[:fc_markup_price]}"
rows << [{content: (' ' + price_details.join('; ') if totals[:fc_price] > 0), colspan: 8}]
# table header
rows.unshift I18n.t('documents.order_by_groups.rows').dup
rows.first.insert(1, Article.human_attribute_name(:supplier))
rows.first[5] = {content: rows.first[5], colspan: 2}
if has_tolerance
rows.first[-1] = {image: "#{Rails.root}/app/assets/images/package-bg.png", scale: 0.6, position: :center}
else
rows.first[-1] = nil
end
text show_group(ordergroup), size: fontsize(13), style: :bold
table rows, width: bounds.width, cell_style: {size: fontsize(8), overflow: :shrink_to_fit} do |table|
# borders
table.cells.borders = [:bottom]
table.cells.border_width = 0.02
table.cells.border_color = 'dddddd'
table.rows(0).border_width = 1
table.rows(0).border_color = '666666'
table.rows(0).column(5).font_style = :bold
table.row(rows.length-3).border_width = 1
table.row(rows.length-3).border_color = '666666'
table.row(rows.length-2).borders = []
table.row(rows.length-1).borders = []
# bottom row with price details
table.row(rows.length-1).text_color = '999999'
table.row(rows.length-1).size = fontsize(7)
table.row(rows.length-1).padding = [0, 5, 0, 5]
table.row(rows.length-1).height = 0 if totals[:fc_price] == 0
table.column(0).width = 150 # @todo would like to set minimum width here
table.column(1).width = 62
table.column(2).align = :right
table.column(5..7).font_style = :bold
table.columns(3..5).align = :center
table.column(6..7).align = :right
table.column(8).align = :center
# dim rows not relevant for members
table.column(4).text_color = '999999'
table.column(8).text_color = '999999'
# hide unit_quantity if there's no tolerance anyway
table.column(-1).width = has_tolerance ? 20 : 0
# dim rows which were ordered but not received
dimrows.each do |ri|
table.row(ri).text_color = 'aaaaaa'
table.row(ri).columns(0..-1).font_style = nil
end
end
end
end
protected
def pdf_add_page_breaks?
super 'order_by_groups'
end
end

View file

@ -0,0 +1,14 @@
module CurrentOrdersHelper
def to_pay_message(ordergroup)
funds = ordergroup.get_available_funds
if funds > 0
content_tag :b, I18n.t('helpers.current_orders.pay_done'), style: 'color: green'
elsif funds == 0
I18n.t('helpers.current_orders.pay_none')
else
content_tag :b, I18n.t('helpers.current_orders.pay_amount', amount: number_to_currency(-ordergroup.get_available_funds))
end
end
end

View file

@ -0,0 +1 @@
/ remove 'erb:contains("link_to"):contains("action_receive")'

View file

@ -0,0 +1,7 @@
/ insert_before '#articles_table'
- unless @orders.nil? or @orders.empty? or @order_articles.nil? or @order_articles.empty?
- content_for :actionbar do
= link_to url_for(controller: 'current_orders/orders', action: 'my', id: @orders.map(&:id).join('+'), format: 'pdf'), class: 'btn' do
= glyph :download
PDF

View file

@ -0,0 +1,8 @@
/ insert_before 'h2:contains(".orders_finished"), h2:contains(".orders_settled")'
.btn-group.pull-right#orders_finished_toolbar
= link_to '#', data: {toggle: 'dropdown'}, class: 'btn dropdown-toggle' do
= t 'orders.show.download.title'
%span.caret
%ul.dropdown-menu
%li= link_to t('orders.show.download.group_pdf'), current_orders_orders_path(document: :groups, format: :pdf)
%li= link_to t('orders.show.download.article_pdf'), current_orders_orders_path(document: :articles, format: :pdf)

View file

@ -0,0 +1,7 @@
.btn-group.pull-right
= link_to '#', data: {toggle: 'dropdown'}, class: 'btn dropdown-toggle' do
= t 'orders.show.download.title'
%span.caret
%ul.dropdown-menu
%li= link_to t('orders.show.download.group_pdf'), current_orders_orders_path(document: :groups, format: :pdf)
%li= link_to t('orders.show.download.article_pdf'), current_orders_orders_path(document: :articles, format: :pdf)

View file

@ -0,0 +1,9 @@
#order_article
- if order_article
= render 'article_info', order_article: order_article
= render 'ordergroups', order_article: order_article
- else
%h2= t('current_orders.articles.index.title')
%i#articles_by_articles= t '.no_selection'

View file

@ -0,0 +1,22 @@
#article_info
%h2{style: 'margin-bottom: 0'}
= t('current_orders.articles.show.title', name: order_article.article.name)
%span.normal= order_article.article.unit
-# @todo unduplicate from group_orders's order_article_info
%p
- if order_article.article.manufacturer.blank?
= raw t 'group_orders.order_article_info.supplied_by', supplier: content_tag(:em, supplier_link(order_article.article.supplier))
- elsif order_article.article.supplier.name == order_article.article.manufacturer
= raw t 'group_orders.order_article_info.supplied_and_made_by', manufacturer: content_tag(:em, supplier_link(order_article.article.supplier))
- else
= raw t 'group_orders.order_article_info.supplied_by_made_by', supplier: content_tag(:em, supplier_link(order_article.article.supplier)), manufacturer: content_tag(:em, order_article.article.manufacturer)
- unless order_article.article.origin.blank?
= raw t 'group_orders.order_article_info.origin_in', origin: content_tag(:em, order_article.article.origin)
- pkg_info = pkg_helper(order_article.price)
= ", #{pkg_info}".html_safe unless pkg_info.blank?
= ", "
= Article.human_attribute_name(:fc_price_short) + ": "
= number_to_currency(order_article.price.fc_price)
= t 'current_orders.articles.article_info.unit', unit: order_article.article.unit

View file

@ -0,0 +1,4 @@
- @order_articles.includes(:order => :supplier).reorder('suppliers.name, articles.article_category_id, articles.name').group_by {|oa| oa.order.name}.each do |name, articles|
%li.nav-header= name
- articles.each do |oa|
%li= link_to oa.article.name, current_orders_articles_path(order: oa.order.id, id: oa.id, anchor: 'order_article'), remote: true

View file

@ -0,0 +1,70 @@
.row-fluid
.span4
.well
%ul.nav.nav-list#article_list
%li.keep
= form_for @q, url: current_orders_articles_path, method: 'get', html: {data: {'submit-onchange' => true}, class: 'form-search'}, remote: true do |f|
.input-append
= f.text_field :article_name_contains, placeholder: t('.article_placeholder'), class: 'search-query input-block-level resettable'
%button.add-on.btn.reset-search{type: 'button'}
%i.icon.icon-remove
= render 'articles', orders: @current_orders
.span8
= render 'actions'
= render 'article', order_article: @order_article
= render 'shared/articles_by/common'
- content_for :javascript do
:javascript
$(function() {
// update number of received items - would prefer to do this server-side to
// keep working when showing a partial list, but this avoids an extra ajax call
$(document).on('GroupOrderArticle#update #update_articles_summary', function(e) {
var count_sum = 0;
// number of received items
$('#articles_by_articles input[data-delta]').each(function() {
count_sum += Number($(this).val());
});
$('#single_order_article_total .count_sum').html(count_sum);
// delta
// @todo partial code-duplication with orders/_edit_amounts
var expected = $('#single_order_article_delta .units_delta').data('quantity-expected');
var delta = Math.round((count_sum - expected)*100)/100.0;
if (isNaN(delta)) {
html = '';
color = 'inherit';
} else if (delta == 0) {
html = I18n.t('js.current_orders.articles.equal');
color = 'green';
//$('#single_order_article_total .count_sum').html(count_sum + ' <i class="icon-ok"></i>');
} else if (delta < 0) {
html = I18n.t('js.current_orders.articles.below', {count: -delta});
color = 'inherit';
} else {
html = I18n.t('js.current_orders.articles.above', {count: delta});
color = 'red';
}
$('#single_order_article_delta .units_delta').html(html).attr('style', 'color: '+color);
$('#single_order_article_total .count_sum').attr('style', 'color: '+color);
});
// add ordergroup
$('#group_order_article_ordergroup_id').select2().select2('data', null);
$(document).on('GroupOrderArticle#create', function(e) {
var base_unit = $('#articles_by_articles').data('base-unit');
// reset selection
$('#group_order_article_ordergroup_id').select2('data', null);
// update table
$.ajax({
url: '#{show_on_group_order_article_create_current_orders_articles_path}',
type: 'get',
data: {group_order_article_id: e.group_order_article_id, base_unit: base_unit}
});
});
});

View file

@ -0,0 +1,37 @@
- units = order_article.article.unit_unit
- multiplier = (params[:base_unit] and units) ? units.scalar : 1
%table.table.table-hover#articles_by_articles{data: {'base-unit' => params[:base_unit]}}
%thead.list-heading
%tr
%th{:style => 'width:70%'}= Ordergroup.model_name.human
%th.center.dimmed
%acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
%th.center
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
%td.center
.btn-group
- if units and units.units != order_article.article.unit
= link_to t('.piece'), current_orders_articles_path(order: order_article.order.id, id: order_article.id), remote: true, class: "btn btn-small #{'active' unless params[:base_unit]}"
= link_to units.units, current_orders_articles_path(order: order_article.order.id, id: order_article.id, base_unit: true), remote: true, class: "btn btn-small #{'active' if params[:base_unit]}"
= render 'shared/articles_by/article_single', order_article: order_article, heading: false, delta_column: true, base_unit: params[:base_unit]
%tfoot
%tr
%td
= form_for GroupOrderArticle.new, remote: true, html: {'data-submit-onchange' => true, style: 'margin: 0'} do |f|
= f.hidden_field :order_article_id, value: order_article.id
= f.select :ordergroup_id,
options_for_select(Ordergroup.undeleted.order(:name).all.map { |g| [ show_group(g), g.id ] }),
{include_blank: true}, {style: 'width: 100%', 'data-placeholder' => t('.add_new')}
%td{colspan: 3}
%tr#single_order_article_total
%th= t 'shared.articles_by.price_sum'
%td.center.dimmed #{order_article.quantity*multiplier} + #{order_article.tolerance*multiplier}
- sum = order_article.group_orders_sum
%th.center.count_sum= sum[:quantity]*multiplier
%tr.no-top-border#single_order_article_delta
%td
%td
%td.center
%span.units_delta{data: {'quantity-expected' => order_article.units * order_article.price.unit_quantity * multiplier}}

View file

@ -0,0 +1,3 @@
- title t('.title'), false
= render 'form'

View file

@ -0,0 +1,2 @@
$('#article_list li:not(.keep)').remove();
$('#article_list').append('<%= j(render('articles', orders: @current_orders)) %>');

View file

@ -0,0 +1,3 @@
- title t('.title', name: @order_article.article.name), false
= render 'form'

View file

@ -0,0 +1,3 @@
$('#order_article').replaceWith('<%= j(render('article', order_article: @order_article)) if @order_article %>');
// add javascript improvements (delta)
$(document).trigger('#update_articles_summary');

View file

@ -0,0 +1,7 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function(w) {
$('#goa_<%= @goa.id %>').remove(); // just to be sure: remove table row which is added below
$('#articles_by_articles tbody').append('<%= j render('shared/articles_by/article_single_goa', goa: @goa, base_unit: params[:base_unit]) %>').addClass('success');
})(window);

View file

@ -0,0 +1,8 @@
.pull-right
= link_to t('ui.back'), root_path(anchor: ''), class: 'btn'
%p.pull-left
= link_to t('current_orders.ordergroups.payment_bar.account_balance'), my_ordergroup_path
= number_to_currency (ordergroup.account_balance)
%p#to_pay_message{style: 'text-align: center'}= to_pay_message(ordergroup)

View file

@ -0,0 +1,55 @@
-# XXX code duplication of foodcoop-adam's app/views/group_orders/show.html.haml
- if @articles_grouped_by_category.count > 0
%table.table.table-hover
%thead
%tr
%th{style: "width:40%"}= heading_helper Article, :name
%th= heading_helper Article, :unit
%th= t 'group_orders.show.articles.unit_price'
%th
%abbr{title: t('group_orders.show.articles.ordered_title')}= t 'group_orders.show.articles.ordered'
%th
%abbr{title: t('group_orders.show.articles.order_nopen_title')}
- if (@order.open? rescue true)
= t 'group_orders.show.articles.order_open'
- else
= t 'group_orders.show.articles.order_not_open'
%th= heading_helper GroupOrderArticle, :total_price
%tbody
- group_order_sum = 0
- for category_name, goas in @articles_grouped_by_category
%tr.article-category.list-heading
%td
= category_name
%i.icon-tag
%td{colspan: "9"}
- goas.each do |goa|
- # get the order-results for the ordergroup
- oa = goa.order_article
- r = {quantity: goa.quantity, tolerance: goa.tolerance, result: goa.result, sub_total: goa.total_price(oa)}
- group_order_sum += r[:sub_total]
%tr{class: cycle('even', 'odd', name: 'articles') + " order-article " + order_article_class_name(r[:quantity], r[:tolerance], r[:result])}
-# article_info is present in foodcoop-adam only
%td.name{style: "width:40%", title: (article_info_title(oa.article) rescue nil)}
= article_info_icon oa.article rescue nil
= oa.article.name
%td
= oa.article.unit
%span{style: 'opacity: 0.4; margin-left: 1em;'}= pkg_helper(oa.price, soft_uq: true)
%td= number_to_currency oa.price.fc_price(@ordergroup)
%td
= r[:quantity]
= "+ #{r[:tolerance]}" if oa.price.unit_quantity > 1
%td= r[:result] > 0 ? r[:result] : "0"
%td= number_to_currency(r[:sub_total])
- unless oa.article.note.blank?
%tr{id: "note_#{oa.id}", class: "note even", style: "display:none"}
%td{colspan: "6"}=h oa.article.note
%tr{class: cycle('even', 'odd', name: 'articles')}
%th{colspan: "5"}= heading_helper GroupOrder, :price
%th= number_to_currency(group_order_sum)
- elsif @articles_grouped_by_category.count == 0
= t 'group_orders.show.articles.no_articles'
- else
= t 'group_orders.show.articles.order_closed_msg'

View file

@ -0,0 +1,13 @@
- title t('.title')
// Article box
%section
.column_content#result
= render 'result'
.well
= render 'payment_bar', ordergroup: @ordergroup
%br/
= link_to_top

View file

@ -0,0 +1,18 @@
-# @todo move multiplier to model & unduplicate with shared/articles_by/
- multiplier, unit = 1, '× ' + goa.order_article.article.unit
- if local_assigns[:base_unit] and unit = goa.order_article.article.unit_unit
- multiplier = unit.scalar
- unit = unit.units
-# output row
%tr{:class => [cycle('even', 'odd', :name => 'articles'), if goa.result == 0 then 'unavailable' end], id: "goa_#{goa.id}"}
%td.name= goa.order_article.article.name
%td{title: goa.order_article.order.name}= link_to goa.order_article.order.name.truncate(15), goa.order_article.order
%td= goa.order_article.article.unit
%td.center= "#{goa.quantity} + #{goa.tolerance}"
%td.center.input-delta= group_order_article_edit_result(goa, multiplier: multiplier, edit: local_assigns[:edit]||true)
%td.nowrap.dimmed= unit if multiplier != 1
%td.symbol
&times;
%td= number_to_currency goa.order_article.price.fc_price(@ordergroup)/multiplier
%td.symbol =
%td.price{data: {value: goa.total_price}}= number_to_currency goa.total_price

View file

@ -0,0 +1,53 @@
- if @ordergroup
%table.table.table-hover#articles_by_groups_table{data: {'base-unit' => params[:base_unit]}}
%thead
%tr
%th{style: 'width: 35%'}= heading_helper Article, :name
%th= Order.model_name.human
%th= heading_helper Article, :unit
%th.center
%acronym{:title => t('shared.articles.ordered_desc')}= t 'shared.articles.ordered'
%th.center{style: 'width: 88px'}
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
%th.nowrap.dimmed
%th.symbol
%th= heading_helper Article, :fc_price, short: true
%th.symbol
%td.center
.btn-group
- if true #if units and units.units != order_article.article.unit
= link_to t('current_orders.articles.ordergroups.piece'), current_orders_ordergroups_path(id: @ordergroup.id), remote: true, class: "btn btn-small #{'active' unless params[:base_unit]}"
= link_to t('current_orders.articles.ordergroups.unit'), current_orders_ordergroups_path(id: @ordergroup.id, base_unit: true), remote: true, class: "btn btn-small #{'active' if params[:base_unit]}"
- total = 0
%tbody.list
- if @goas and @goas.length > 0
- for goa in @goas
- total += goa.total_price
= render 'article', goa: goa, base_unit: params[:base_unit]
- else
%tr
%td{colspan: 11}
%i No articles for #{show_group(@ordergroup)} in the current orders.
%tfoot
%tr
%td{colspan: 10}
- new_articles = OrderArticle.includes(:article, :article_price).where(order_id: @order_ids)
- new_article_data = articles_for_select2(new_articles) {|a| "#{a.article.name} (#{a.article.unit}, #{number_to_currency a.price.fc_price})"}
= form_for GroupOrderArticle.new, remote: true, html: {'data-submit-onchange' => true, style: 'margin: 0'} do |f|
= f.select :order_article_id,
options_for_select(new_article_data.map {|a| [a[:text], a[:id]]}),
{}, {style: 'width: 500px', 'data-placeholder' => t('.add_new') }
= f.hidden_field :ordergroup_id, value: @ordergroup.id
%tr#single_ordergroup_total{:class => cycle('even', 'odd', :name => 'articles')}
%th{colspan: 9}= t 'shared.articles_by.price_sum'
%th.price_sum{colspan: 2, data: {value: total}}= number_to_currency(total)
.well#payment_bar
= render 'payment_bar', ordergroup: @ordergroup
- else
%i= t '.no_selection'

View file

@ -0,0 +1,72 @@
.well
= form_tag current_orders_ordergroups_path, method: :get, 'data-submit-onchange' => true, style: 'margin: 0' do
= select_tag 'id',
options_for_select(@all_ordergroups.map { |g| [ show_group(g), g.id, {class: ('muted' unless @ordered_group_ids.include? g.id)}] }, (@ordergroup.id rescue '')),
include_blank: true, id: 'ordergroup_select', style: 'min-width: 300px',
'data-placeholder' => t('.ordergroup_placeholder'), 'data-submit-on-change' => :true
-#.form-search.pull-right # see below why this is disabled
.input-append
= text_field_tag :query, params[:query], class: 'search-query delayed-search resettable', disabled: @ordergroup.nil?,
'placeholder' => t('orders.show.search_placeholder.articles')
%button.add-on.btn.reset-search{:type => :button, :title => t('orders.show.search_reset')}
%i.icon.icon-remove
- if @ordergroup and FoodsoftConfig[:price_markup_list]
%span.price_markup_note{style: 'margin-left: 1em'}= show_price_markup @ordergroup, format: :full_label, optional: true
#articles_by_groups
= render 'articles'
= render 'shared/articles_by/common', order: @order
- content_for :javascript do
:javascript
$(function() {
// TODO group by ordered / not-ordered
$('#ordergroup_select').select2();
// add article
$('#group_order_article_order_article_id').select2({
placeholder: '#{j t('orders.receive.add_article')}',
formatNoMatches: function(term) { return '#{j t('.no_articles_available')}';}
});
$(document).on('GroupOrderArticle#create', function(e) {
var base_unit = $('#articles_by_groups_table').data('base-unit');
// reset selection
$('#group_order_article_order_article_id').select2('data', null);
// update table
$.ajax({
url: '#{show_on_group_order_article_create_current_orders_ordergroups_path}',
type: 'get',
data: {group_order_article_id: e.group_order_article_id, base_unit: base_unit}
});
});
$(document).on('GroupOrderArticle#update', function(e) {
$.ajax({
url: '#{show_on_group_order_article_update_current_orders_ordergroups_path}',
type: 'get',
data: {group_order_id: e.group_order_id, group_order_article_id: e.group_order_article_id}
});
});
// article search
// DO NOT USE because listjs can't handle updates https://github.com/javve/list.js/issues/86
/*
new List(document.body, {
valueNames: ['name'],
engine: 'unlist',
plugins: [
['reset', {highlightClass: 'btn-primary'}],
['delay', {delayedSearchTime: 500}],
],
// make large pages work too (as we don't have paging)
page: 10000,
indexAsync: true
});
*/
});

View file

@ -0,0 +1,15 @@
- if current_user.role_finance?
.pull-right
= t('.payment')
- if current_user.role_finance?
= link_to t('.new_transaction'), new_finance_ordergroup_transaction_path(ordergroup), class: 'btn'
%p.pull-left
- if current_user.role_finance?
= link_to 'Account balance', finance_ordergroup_transactions_path(ordergroup)
- else
= t '.account_balance'
of #{show_group(ordergroup)}: #{number_to_currency ordergroup.account_balance}
%p#to_pay_message{style: 'text-align: center'}= to_pay_message(ordergroup)

View file

@ -0,0 +1,3 @@
- title t('.title')
= render 'form'

View file

@ -0,0 +1,3 @@
- title t('.title', name: show_group(@ordergroup))
= render 'form'

View file

@ -0,0 +1,3 @@
// untested
$('h1, title').html('<%= j t('.title', name: show_group(@ordergroup)) %>');
$('#articles_by_groups').html('<%= j render('articles') %>');

View file

@ -0,0 +1,7 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function(w) {
$('#goa_<%= @goa.id %>').remove(); // just to be sure: remove table row which is added below
$('#articles_by_groups tbody').append('<%= j render('article', goa: @goa, base_unit: params[:base_unit]) %>').addClass('success');
})(window);

View file

@ -0,0 +1,7 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function(w) {
// table update is done by group_order_article hook already
$('#to_pay_message').html('<%= j to_pay_message(@ordergroup) %>');
})(window);

View file

@ -0,0 +1,24 @@
- title t('.title')
.well
- if @orders.empty?
= t '.no_finished_orders'
- else
%table.table.table-striped
%thead
%tr
%th= heading_helper Order, :name
%th= heading_helper Order, :ends
%th= heading_helper Order, :note
%th{colspan: "2"}
%tbody
- for order in @orders
%tr
%td= order.name
%td= format_time(order.ends)
%td= truncate(order.note)
%td= receive_button order, class: 'btn-small' unless order.stockit?
%td
= link_to t('ui.show'), order, class: 'btn btn-small'
= order_pdf order, :fax, t('orders.show.download.fax_pdf'), class: 'btn btn-small'

View file

@ -0,0 +1,61 @@
en:
current_orders:
articles:
article:
no_selection: Choose an article to show who ordered it, or download pick lists at the right.
article_info:
from: from %{supplier}
unit: per %{unit}
form:
article_placeholder: Search articles...
current_orders: All current orders
index:
title: Distribute article
ordergroups:
piece: pc
unit: unit
add_new: Add an ordergroup...
show:
title: ! 'Distribute %{name}'
group_orders:
index:
title: Your current orders
ordergroups:
articles:
add_new: Add an article...
form:
ordergroup_placeholder: Choose an ordergroup...
no_selection: Choose an ordergroup to show the articles.
index:
title: Articles for ordergroup
payment_bar:
account_balance: Account balance
new_pin: PIN
new_transaction: New transaction
payment: ! 'Payment:'
show:
title: Articles for %{name}
orders:
receive:
title: Receive orders
no_finished_orders: There are currently no orders to receive.
documents:
multiple_orders_by_articles:
filename: Current orders sorted by article
title: Current orders - by article
multiple_orders_by_groups:
filename: Current orders sorted by group
title: Current orders - by group
helpers:
current_orders:
pay_done: Fully paid
pay_none: Nothing to pay
pay_amount: To pay %{amount}
js:
current_orders:
articles:
above: '%{count} more<br>than available'
below: '%{count} left over'
equal: all distributed
ui:
back: Back

View file

@ -0,0 +1,55 @@
nl:
current_orders:
articles:
article:
no_selection: Kies een artikel om te zien wie het besteld heeft, of download verdeellijsten rechtsboven.
article_info:
from: van %{supplier}
unit: per %{unit}
form:
article_placeholder: Zoek artikelen...
current_orders: Alle huidige bestellingen
index:
title: Artikelen verdelen
ordergroups:
piece: st
add_new: Huishouden toevoegen...
show:
title: ! '%{name} verdelen'
ordergroups:
articles:
add_new: Artikel toevoegen...
form:
ordergroup_placeholder: Kies een huishouden...
no_selection: Kies een huishouden om de artikelen te tonen.
index:
title: Artikelen voor huishouden
payment_bar:
account_balance: Account balance
new_pin: PIN
new_transaction: Nieuwe transactie
payment: ! 'Betaling:'
show:
title: Artikelen voor %{name}
orders:
receive:
title: Bestellingen ontvangen
no_finished_orders: Er zijn momenteel geen bestellingen die ontvangen hoeven te worden.
documents:
multiple_orders_by_articles:
filename: Huidige bestellingen per artikel
title: Huidige bestellingen - per artikel
multiple_orders_by_groups:
filename: Huidige bestellingen per huishouden
title: Huidige bestellingen - per huishouden
js:
current_orders:
articles:
above: '%{count} meer dan<br>beschikbaar'
below: '%{count} blijft over'
equal: alles verdeeld
helpers:
current_orders:
pay_done: Alles betaald
pay_none: Niets te betalen
pay_amount: Te betalen %{amount}

View file

@ -0,0 +1,27 @@
Rails.application.routes.draw do
scope '/:foodcoop' do
namespace :current_orders do
resources :ordergroups, :only => [:index, :show] do
collection do
get :show_on_group_order_article_create
get :show_on_group_order_article_update
end
end
resources :articles, :only => [:index, :show] do
collection do
get :show_on_group_order_article_create
end
end
resource :orders, :only => [:show] do
collection do
get :my
get :receive
end
end
resources :group_orders, :only => [:index]
end
end
end

View file

@ -0,0 +1,20 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "foodsoft_current_orders/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "foodsoft_current_orders"
s.version = FoodsoftCurrentOrders::VERSION
s.authors = ["wvengen"]
s.email = ["dev-voko@willem.engen.nl"]
s.homepage = "https://github.com/foodcoop-adam/foodsoft"
s.summary = "Quick support for working on all currently active orders in foodsoft."
s.description = ""
s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"]
s.add_dependency "rails"
s.add_dependency "deface", "~> 1.0.0"
end

View file

@ -0,0 +1,5 @@
require "deface"
require "foodsoft_current_orders/engine"
module FoodsoftCurrentOrders
end

View file

@ -0,0 +1,4 @@
module FoodsoftCurrentOrders
class Engine < ::Rails::Engine
end
end

View file

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