end of day commit

This commit is contained in:
2012-08-29 17:42:04 +02:00
parent 89700f36e9
commit 8213bae2c6
57 changed files with 1109 additions and 128 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ gem 'simply_stored' , :git => 'git://github.com/bterkuile/simply_stored.git'
gem 'devise', '2.0.4' gem 'devise', '2.0.4'
gem 'devise_simply_stored' gem 'devise_simply_stored'
gem 'rqrcode-rails3' gem 'rqrcode'
gem 'mini_magick' gem 'mini_magick'
#gem "less-rails-bootstrap-devise", :git => 'git://github.com/bigbento/less-rails-bootstrap-devise.git' #gem "less-rails-bootstrap-devise", :git => 'git://github.com/bigbento/less-rails-bootstrap-devise.git'
+1 -3
View File
@@ -157,8 +157,6 @@ GEM
rest-client (1.6.7) rest-client (1.6.7)
mime-types (>= 1.16) mime-types (>= 1.16)
rqrcode (0.4.2) rqrcode (0.4.2)
rqrcode-rails3 (0.1.5)
rqrcode (>= 0.4.2)
rspec (2.11.0) rspec (2.11.0)
rspec-core (~> 2.11.0) rspec-core (~> 2.11.0)
rspec-expectations (~> 2.11.0) rspec-expectations (~> 2.11.0)
@@ -240,7 +238,7 @@ DEPENDENCIES
mini_magick mini_magick
pry pry
rails (= 3.2.8) rails (= 3.2.8)
rqrcode-rails3 rqrcode
rspec-rails rspec-rails
sass-rails (~> 3.2.3) sass-rails (~> 3.2.3)
selenium-webdriver selenium-webdriver
+67
View File
@@ -0,0 +1,67 @@
<map version="0.9.0">
<!-- To view this file, download free mind mapping software FreeMind from http://freemind.sourceforge.net -->
<node COLOR="#000000" CREATED="1346248504629" ID="ID_685891399" MODIFIED="1346249525729" TEXT="Qr scan">
<font NAME="SansSerif" SIZE="20"/>
<hook NAME="accessories/plugins/AutomaticLayout.properties"/>
<node COLOR="#0033ff" CREATED="1346248520045" ID="ID_454905514" MODIFIED="1346249526866" POSITION="right" TEXT="I already have an open list">
<edge STYLE="sharp_bezier" WIDTH="8"/>
<font NAME="SansSerif" SIZE="18"/>
<node COLOR="#00b439" CREATED="1346248563729" ID="ID_733708643" MODIFIED="1346249526869" TEXT="Table is from same supplier">
<edge STYLE="bezier" WIDTH="thin"/>
<font NAME="SansSerif" SIZE="16"/>
<node COLOR="#990000" CREATED="1346248693439" ID="ID_916776491" MODIFIED="1346249526871" TEXT="Offer to move table">
<font NAME="SansSerif" SIZE="14"/>
<node COLOR="#111111" CREATED="1346248699370" ID="ID_1684994680" MODIFIED="1346252084515" TEXT="If yes, move table and show product"/>
<node COLOR="#111111" CREATED="1346248725406" ID="ID_10610841" MODIFIED="1346252095940" TEXT="If no, show products"/>
</node>
</node>
<node COLOR="#00b439" CREATED="1346248579841" ID="ID_227890554" MODIFIED="1346249526872" TEXT="Table is from other supplier">
<edge STYLE="bezier" WIDTH="thin"/>
<font NAME="SansSerif" SIZE="16"/>
<node COLOR="#990000" CREATED="1346248754701" ID="ID_1748068243" MODIFIED="1346249459783" TEXT="Warning, cannot do with open list">
<font NAME="SansSerif" SIZE="14"/>
</node>
</node>
<node COLOR="#00b439" CREATED="1346248860563" ID="ID_141169907" MODIFIED="1346249526873" TEXT="Table is your current table">
<edge STYLE="bezier" WIDTH="thin"/>
<font NAME="SansSerif" SIZE="16"/>
<node COLOR="#990000" CREATED="1346248869002" ID="ID_418218218" MODIFIED="1346249459784" TEXT="Do nothing!">
<font NAME="SansSerif" SIZE="14"/>
</node>
</node>
</node>
<node COLOR="#0033ff" CREATED="1346248530523" ID="ID_1378676144" MODIFIED="1346249526875" POSITION="left" TEXT="I do not have an open list">
<edge STYLE="sharp_bezier" WIDTH="8"/>
<font NAME="SansSerif" SIZE="18"/>
<node COLOR="#00b439" CREATED="1346248595465" ID="ID_313049528" MODIFIED="1346249526877" TEXT="Table is occupied by other user">
<edge STYLE="bezier" WIDTH="thin"/>
<font NAME="SansSerif" SIZE="16"/>
<node COLOR="#990000" CREATED="1346248635520" ID="ID_1246071370" MODIFIED="1346249459786" TEXT="Option: Join this table">
<font NAME="SansSerif" SIZE="14"/>
</node>
<node COLOR="#990000" CREATED="1346249385937" ID="ID_146376441" MODIFIED="1346249526878" TEXT="Option: Show products">
<font NAME="SansSerif" SIZE="14"/>
<node COLOR="#111111" CREATED="1346249395535" ID="ID_152885983" MODIFIED="1346249459786" TEXT="Show products without ordering options"/>
</node>
</node>
<node COLOR="#00b439" CREATED="1346248619233" ID="ID_1502347679" MODIFIED="1346249526883" TEXT="Table is not occupied by other user">
<edge STYLE="bezier" WIDTH="thin"/>
<font NAME="SansSerif" SIZE="16"/>
<node COLOR="#990000" CREATED="1346248629824" ID="ID_1685359507" MODIFIED="1346249459791">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
Show products with ordering options
</p>
</body>
</html>
</richcontent>
<font NAME="SansSerif" SIZE="14"/>
</node>
</node>
</node>
</node>
</map>
+9 -1
View File
@@ -1,4 +1,12 @@
jQuery -> jQuery ->
$("a[rel=popover]").popover() $("a[rel=popover]").popover()
$(".tooltip").tooltip() $(".tooltip").tooltip()
$("a[rel=tooltip]").tooltip() $("a[rel=tooltip]").tooltip()
$('input.currency').each(->
obj = $(this)
original_width = obj.width()
obj.wrap("<div class='input-prepend'>")
currency_sign = $("<span class='add-on'>&euro;</span>")
obj.before(currency_sign)
obj.css('width', (original_width - currency_sign.outerWidth())+'px')
)
+96
View File
@@ -0,0 +1,96 @@
/**
* Cookie plugin
*
* Copyright (c) 2006 Klaus Hartl (stilbuero.de)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
/**
* Create a cookie with the given name and value and other optional parameters.
*
* @example $.cookie('the_cookie', 'the_value');
* @desc Set the value of a cookie.
* @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
* @desc Create a cookie with all available options.
* @example $.cookie('the_cookie', 'the_value');
* @desc Create a session cookie.
* @example $.cookie('the_cookie', null);
* @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
* used when the cookie was set.
*
* @param String name The name of the cookie.
* @param String value The value of the cookie.
* @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
* @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
* If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
* If set to null or omitted, the cookie will be a session cookie and will not be retained
* when the the browser exits.
* @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
* @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
* @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
* require a secure protocol (like HTTPS).
* @type undefined
*
* @name $.cookie
* @cat Plugins/Cookie
* @author Klaus Hartl/klaus.hartl@stilbuero.de
*/
/**
* Get the value of a cookie with the given name.
*
* @example $.cookie('the_cookie');
* @desc Get the value of a cookie.
*
* @param String name The name of the cookie.
* @return The value of the cookie.
* @type String
*
* @name $.cookie
* @cat Plugins/Cookie
* @author Klaus Hartl/klaus.hartl@stilbuero.de
*/
jQuery.cookie = function(name, value, options) {
if (typeof value != 'undefined') { // name and value given, set cookie
options = options || {};
if (value === null) {
value = '';
options.expires = -1;
}
var expires = '';
if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
var date;
if (typeof options.expires == 'number') {
date = new Date();
date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
} else {
date = options.expires;
}
expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
}
// CAUTION: Needed to parenthesize options.path and options.domain
// in the following expressions, otherwise they evaluate to undefined
// in the packed version for some reason...
var path = options.path ? '; path=' + (options.path) : '';
var domain = options.domain ? '; domain=' + (options.domain) : '';
var secure = options.secure ? '; secure' : '';
document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
} else { // only name given, get cookie
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
};
+52 -33
View File
@@ -59,8 +59,8 @@ root.Quser=
row.append($('<td class="currency"></td>').html(Qrammer.currency(order.total_amount))) row.append($('<td class="currency"></td>').html(Qrammer.currency(order.total_amount)))
foot.append('<tr><td></td><td class="currency"><strong>'+Qrammer.currency(res.total_amount)+'</strong></td></tr>') foot.append('<tr><td></td><td class="currency"><strong>'+Qrammer.currency(res.total_amount)+'</strong></td></tr>')
) )
order_selected_products: ()-> order_selected_products: (h)->
h = {} h ||= {}
for product_id, info of window.active_products_list for product_id, info of window.active_products_list
h['products['+product_id+']'] = info.number h['products['+product_id+']'] = info.number
$.post('/user/order_selected_products', h, ((res) -> Quser.handle_response(res)), 'json') $.post('/user/order_selected_products', h, ((res) -> Quser.handle_response(res)), 'json')
@@ -92,48 +92,67 @@ root.Quser=
table.show() table.show()
load_active_list_products: -> load_active_list_products: ->
$.get('/user/list_products.json', (res) -> Quser.populate_products_table('/user/list_products.json', true)
window.products = res load_table_products: (table_id, occupied)->
Quser.populate_products_table('/user/list_products_for_table.json?table_id='+table_id, !occupied)
populate_products_table: (src, include_order_buttons)->
$.get(src, (res) ->
body = $('#products-table tbody') body = $('#products-table tbody')
for category, products of window.products for category, products of res
body.append('<tr><td colspan="4"><h4>'+category+'<h4></td></tr>') body.append('<tr><td colspan="4"><h4>'+category+'<h4></td></tr>')
for product in products for product in products
row = $('<tr></tr>') row = $('<tr></tr>')
button = $('<button class="btn btn-mini btn-primary">Add</button>')
callback = ((prod) ->
->
product_count_holder = $('#order-product-count-'+prod._id)
count = parseInt(product_count_holder.text())
product_count_holder.text(1)
Quser.add_product(prod, count)
)(product)
button.click(callback)
row.append('<td>'+product.name+'</td>') row.append('<td>'+product.name+'</td>')
order_product_count = $('<span id="order-product-count-'+product._id+'" class="order-product-count">1</span>') if include_order_buttons
order_count_minus = $('<span class="btn btn-info btn-mini">-</span>') button = $('<button class="btn btn-mini btn-primary">Add</button>')
order_count_minus.click(-> callback = ((prod) ->
val_holder = $(this).siblings('.order-product-count') ->
val = parseInt(val_holder.text()) product_count_holder = $('#order-product-count-'+prod._id)
val_holder.text(val - 1) if val > 1 count = parseInt(product_count_holder.text())
) product_count_holder.text(1)
order_count_plus = $('<span class="btn btn-info btn-mini">+</span>') Quser.add_product(prod, count)
order_count_plus.click(-> )(product)
val_holder = $(this).siblings('.order-product-count') button.click(callback)
val = parseInt(val_holder.text()) order_product_count = $('<span id="order-product-count-'+product._id+'" class="order-product-count">1</span>')
val_holder.text(val + 1) order_count_minus = $('<span class="btn btn-info btn-mini">-</span>')
) order_count_minus.click(->
row.append($('<td class="order-count-cell"></td>').append(order_count_minus).append('&nbsp;').append(order_product_count).append('&nbsp;').append(order_count_plus)) val_holder = $(this).siblings('.order-product-count')
val = parseInt(val_holder.text())
val_holder.text(val - 1) if val > 1
)
order_count_plus = $('<span class="btn btn-info btn-mini">+</span>')
order_count_plus.click(->
val_holder = $(this).siblings('.order-product-count')
val = parseInt(val_holder.text())
val_holder.text(val + 1)
)
row.append($('<td class="order-count-cell"></td>').append(order_count_minus).append('&nbsp;').append(order_product_count).append('&nbsp;').append(order_count_plus))
row.append('<td>'+Qrammer.currency(product.price)+'</td>') row.append('<td>'+Qrammer.currency(product.price)+'</td>')
row.append($('<td></td>').append(button)) row.append($('<td></td>').append(button)) if include_order_buttons
body.append(row) body.append(row)
) )
actions_for_table: (table)->
actions_for_table: (table_id)-> table = JSON.parse(table) if typeof(table) == 'string'
$.get('/user/table_info.json?table_id='+table_id, (res)-> $.get('/user/table_info.json?table_id='+table.table_id, (res)->
if res.occupied if res.occupied
alert('Table is occupied') alert('Table is occupied')
else else
$.post('/user/create_list.json', {table_id: table_id}, (res)-> Quser.handle_response(res)) if res.other_supplier
#TODO cannot do something with other supplier when list is active
else if res.current_table_id && res.current_table_id == table.table_id
#nothing has changed, show product list
window.location = '/user/list_products'
else if res.current_table_id && res.current_table_id != table.table_id
#TODO Offer to move table
$.post('/user/move_table', {table_id: table.table_id}, (res2)->
if res2.occupied
alert('Cannot move to occupied table')
else
window.location = '/user/list_products'
)
else
#$.post('/user/create_list.json', {table_id: table.table_id}, (res)-> Quser.handle_response(res))
window.location = '/user/list_products_for_table?table_id='+table.table_id
, 'json') , 'json')
add_product: (product, count) -> add_product: (product, count) ->
count ||= 1 count ||= 1
+3
View File
@@ -0,0 +1,3 @@
form
input.currency
text-align: right
@@ -79,3 +79,5 @@ table
list-style: none list-style: none
margin: 0 margin: 0
padding: 0 padding: 0
li
float: left
@@ -17,9 +17,16 @@ body
height: 44px height: 44px
background-repeat: no-repeat background-repeat: no-repeat
width: 45px width: 45px
background-image: image-url('icons/section-table.png') //background-image: image-url('icons/section-table.png')
.table-number .table-link
margin-top: -45px margin-top: -45px
.table-number
position: absolute
top: 0
line-height: 44px
width: 45px
font-size: 42px
text-align: center
.action-button-container .action-button-container
margin-right: -20px margin-right: -20px
&.section-tables-active &.section-tables-active
+4 -4
View File
@@ -14,9 +14,9 @@ class ApplicationController < ActionController::Base
end end
end end
def check_active_list_state def check_active_list_state
if session[:active_list_id] if current_user.try(:active_list_id)
unless active_list.active? unless active_list.active?
session[:active_list_id] = nil current_user.list_is_closed!
redirect_to user_root_path, alert: t('messages.the_list_has_been_closed', list: List.model_name.human) redirect_to user_root_path, alert: t('messages.the_list_has_been_closed', list: List.model_name.human)
end end
end end
@@ -24,8 +24,8 @@ class ApplicationController < ActionController::Base
end end
def active_list def active_list
return nil unless session[:active_list_id].present? return nil unless current_user.try(:active_list_id).present?
@active_list ||= List.find(session[:active_list_id]) @active_list ||= List.find(current_user.active_list_id)
end end
alias :active_list_object :active_list alias :active_list_object :active_list
helper_method :active_list_object helper_method :active_list_object
+11 -6
View File
@@ -1,8 +1,5 @@
class DashboardController < ApplicationController class DashboardController < ApplicationController
before_filter :check_active_list_state, except: :home
def home def home
end end
@@ -16,9 +13,17 @@ class DashboardController < ApplicationController
end end
def supplier_home # GET /select_qr_image
redirect_to active_orders_supplier_path(Supplier.first) # GET /select_qr_image.png
# GET /select_qr_image.svg
def table_qr_image
@table = Table.find(params[:table_id])
code = {table_id: @table.id}.to_json
respond_to do |format|
format.html
format.svg { render :qrcode => code, :level => :l, :unit => 10, table_number: @table.number }
format.png { render qrcode: code, table_number: @table.number }
end
end end
end end
@@ -1,5 +1,6 @@
module Suppliers module Suppliers
class ApplicationController < ::ApplicationController class ApplicationController < ::ApplicationController
before_filter :authenticate_supplier!
layout 'tablet' layout 'tablet'
end end
@@ -0,0 +1,87 @@
module Suppliers
class ProductCategoriesController < Suppliers::ApplicationController
# GET /product_categories
# GET /product_categories.json
def index
@product_categories = current_supplier.product_categories
respond_to do |format|
format.html # index.html.erb
format.json { render json: @product_categories }
end
end
# GET /product_categories/1
# GET /product_categories/1.json
def show
@product_category = ProductCategory.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: @product_category }
end
end
# GET /product_categories/new
# GET /product_categories/new.json
def new
@product_category = ProductCategory.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: @product_category }
end
end
# GET /product_categories/1/edit
def edit
@product_category = ProductCategory.find(params[:id])
end
# POST /product_categories
# POST /product_categories.json
def create
@product_category = ProductCategory.new(params[:product_category])
@product_category.supplier = current_supplier
respond_to do |format|
if @product_category.save
format.html { redirect_to [:suppliers, @product_category], notice: t('action.create.successfull', model: ProductCategory.model_name.human) }
format.json { render json: @product_category, status: :created, location: @product_category }
else
format.html { render action: "new" }
format.json { render json: @product_category.errors, status: :unprocessable_entity }
end
end
end
# PUT /product_categories/1
# PUT /product_categories/1.json
def update
@product_category = ProductCategory.find(params[:id])
respond_to do |format|
if @product_category.update_attributes(params[:product_category])
format.html { redirect_to [:suppliers, @product_category], notice: t('action.update.successfull', model: ProductCategory.model_name.human) }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @product_category.errors, status: :unprocessable_entity }
end
end
end
# DELETE /product_categories/1
# DELETE /product_categories/1.json
def destroy
@product_category = ProductCategory.find(params[:id])
@product_category.destroy
respond_to do |format|
format.html { redirect_to suppliers_product_categories_url, notice: t('action.destroy.successfull', model: ProductCategory.model_name.human) }
format.json { head :no_content }
end
end
end
end
@@ -0,0 +1,88 @@
module Suppliers
class ProductsController < Suppliers::ApplicationController
# GET /products
# GET /products.json
def index
@products = current_supplier.products
respond_to do |format|
format.html # index.html.erb
format.json { render json: @products }
end
end
# GET /products/1
# GET /products/1.json
def show
@product = Product.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: @product }
end
end
# GET /products/new
# GET /products/new.json
def new
@product = Product.new
@product.product_category_id = params[:product_category_id].presence
respond_to do |format|
format.html # new.html.erb
format.json { render json: @product }
end
end
# GET /products/1/edit
def edit
@product = Product.find(params[:id])
end
# POST /products
# POST /products.json
def create
@product = Product.new(params[:product])
@product.supplier = current_supplier
respond_to do |format|
if @product.save
format.html { redirect_to [:suppliers, @product], notice: t('action.create.successfull', model: Product.model_name.human) }
format.json { render json: @product, status: :created, location: @product }
else
format.html { render action: "new" }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
# PUT /products/1
# PUT /products/1.json
def update
@product = Product.find(params[:id])
respond_to do |format|
if @product.update_attributes(params[:product])
format.html { redirect_to [:suppliers, @product], notice: t('action.update.successfull', model: Product.model_name.human) }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
@product = Product.find(params[:id])
@product.destroy
respond_to do |format|
format.html { redirect_to suppliers_products_url, notice: t('action.destroy.successfull', model: Product.model_name.human) }
format.json { head :no_content }
end
end
end
end
-13
View File
@@ -82,19 +82,6 @@ class TablesController < ApplicationController
end end
end end
# GET /tables/qrcode
# GET /tables/qrcode.png
# GET /tables/qrcode.svg
def qrcode
@table = Table.find(params[:id])
code = @table.id
respond_to do |format|
format.html
format.svg { render :qrcode => code, :level => :l, :unit => 10 }
format.png { render qrcode: code }
end
end
private private
def set_relation_options def set_relation_options
+52 -10
View File
@@ -9,6 +9,7 @@ class UserController < ApplicationController
end end
# POST /user/create_list {table_id: 1234} # POST /user/create_list {table_id: 1234}
#DEPRICATED
def create_list def create_list
@table = Table.find(params[:table_id]) @table = Table.find(params[:table_id])
if @table.occupied? if @table.occupied?
@@ -17,11 +18,8 @@ class UserController < ApplicationController
format.json { render json: js_alert(t('messages.table_is_occupied'))} format.json { render json: js_alert(t('messages.table_is_occupied'))}
end end
else else
@list = List.new(table: @table, supplier_id: @table.supplier_id) if @list = List.from_table( @table, current_user )
@list.add_user current_user end
#@list.add_user(current_user)
@list.save
session[:active_list_id] = @list.id
respond_to do |format| respond_to do |format|
format.html { redirect_to user_list_products_path } format.html { redirect_to user_list_products_path }
format.json { render json: js_notice('table_created')} format.json { render json: js_notice('table_created')}
@@ -31,9 +29,15 @@ class UserController < ApplicationController
def table_info def table_info
@table = Table.find(params[:table_id]) @table = Table.find(params[:table_id])
res = {}
res[:ocupied] = @table.occupied?
if list.present?
res[:other_supplier] = true if list.supplier_id != @table.supplier_id
res[:current_table_id] = list.table_id
end
respond_to do |format| respond_to do |format|
format.json do format.json do
render json: {occupied: @table.occupied?} render json: res
end end
end end
end end
@@ -56,6 +60,22 @@ class UserController < ApplicationController
end end
end end
def list_products_for_table
@table = Table.find(params[:table_id])
respond_to do |format|
format.html do
render layout: 'phone'
end
format.json do
products = @table.supplier.products
products.include_relation(:product_categories)
products.sort_by!{|p| p.product_category.try(:position) || 90000}
h = products.inject({}){|h, p| n = p.product_category.try(:name) || 'other'; h[n] ||= []; h[n] << p; h}
render json: h
end
end
end
# GET /user/current_list.json # GET /user/current_list.json
# Information about the currently active list # Information about the currently active list
# This information includes detailed order information # This information includes detailed order information
@@ -97,7 +117,7 @@ class UserController < ApplicationController
respond_to do |format| respond_to do |format|
format.json do format.json do
if !list.try(:active?) if !list.try(:active?)
session[:active_list_id] = nil current_user.list_is_closed!
render json: {list_active: false} render json: {list_active: false}
return return
else else
@@ -144,8 +164,8 @@ class UserController < ApplicationController
# GET /user/list_history/:list_id # GET /user/list_history/:list_id
def history_list def history_list
@list = List.find(params[:list_id]) @list = List.find(params[:list_id])
if params[:list_closed].present? && session[:active_list_id] == @list.id if params[:list_closed].present? && current_user.active_list_id == @list.id
session[:active_list_id] = nil current_user.list_is_closed!
flash.now[:notice] = t('messages.the_list_has_been_closed', list: List.model_name.human) flash.now[:notice] = t('messages.the_list_has_been_closed', list: List.model_name.human)
end end
redirect_to user_root_path, alert: t('messages.illegal_history_list_attempt') and return unless @list.user_ids.include?(current_user.id) redirect_to user_root_path, alert: t('messages.illegal_history_list_attempt') and return unless @list.user_ids.include?(current_user.id)
@@ -154,7 +174,19 @@ class UserController < ApplicationController
def order_selected_products def order_selected_products
@list = list if list.present?
@list = list
else
@table = Table.find(params[:table_id])
if @table.occupied?
#TODO handle placint order on occupied table
else
if @list = List.from_table( @table, current_user )
else
#TODO handle second list creation for user
end
end
end
respond_to do |format| respond_to do |format|
format.html do format.html do
redirect_to(root_path, alert: t('messages.cannot_order_on_non_active_list')) and return unless @list.active? redirect_to(root_path, alert: t('messages.cannot_order_on_non_active_list')) and return unless @list.active?
@@ -168,4 +200,14 @@ class UserController < ApplicationController
end end
end end
end end
def move_table
return unless list.present?
@table = Table.find(params[:table_id])
if @table.occupied?
render json: {occupied: true}
else
list.move_to_table @table
render json: {occupied: false}
end
end
end end
+21 -1
View File
@@ -26,8 +26,28 @@ module ApplicationHelper
'Qwaiter' 'Qwaiter'
end end
# Helper for displaying HTML currency values
def currency(amount, opts={})
amount ||= 0.0
amount = amount.to_f
#prefix = case company.currency
# when 'GBP' then '&pound;&nbsp;'
# when 'USD' then '$&nbsp;'
# else '&euro;&nbsp;'
# end
prefix = '&euro;&nbsp;'
return (prefix + ("%.2f" % amount)).html_safe
# Rails native is ambiguous since is uses translation hashes
options = {:unit => '&euro;'}.merge(opts)
number_to_currency(amount, options)
end
def list_open? def list_open?
session[:active_list_id].present? active_list_id.present?
end
def active_list_id
current_user.try(:active_list_id)
end end
def no_content_given(model) def no_content_given(model)
+5
View File
@@ -0,0 +1,5 @@
class Administrator
include SimplyStored::Couch
include Devise::Orm::SimplyStored
devise :database_authenticatable, :rememberable #, :recoverable, :rememberable, :trackable, :registerable
end
+18
View File
@@ -8,6 +8,7 @@ class List
has_many :orders, dependent: :destroy has_many :orders, dependent: :destroy
belongs_to :table belongs_to :table
belongs_to :supplier belongs_to :supplier
belongs_to :section
has_and_belongs_to_many :users, storing_keys: true has_and_belongs_to_many :users, storing_keys: true
validates :table_id, presence: true validates :table_id, presence: true
@@ -15,6 +16,16 @@ class List
view :by_supplier_id_and_id, key: [:supplier_id, :_id] view :by_supplier_id_and_id, key: [:supplier_id, :_id]
def self.from_table table, user
return if user.has_active_list?
list = new table: table, supplier_id: table.supplier_id, section_id: table.section_id
list.add_user user
list.save
user.active_list_id = list.id
user.save
list
end
def close! def close!
orders.map(&:close!) orders.map(&:close!)
self.state = 'closed' self.state = 'closed'
@@ -48,6 +59,13 @@ class List
@order @order
end end
def move_to_table to_table
TableMove.create list: self, from_table_id: table_id, to_table: to_table
self.table = to_table
self.section_id = to_table.section_id
save
end
def as_json def as_json
super.merge(table_number: table_number) super.merge(table_number: table_number)
end end
+1
View File
@@ -6,6 +6,7 @@ class Order
belongs_to :list belongs_to :list
belongs_to :user belongs_to :user
belongs_to :supplier belongs_to :supplier
belongs_to :section
has_many :product_orders, dependent: :destroy has_many :product_orders, dependent: :destroy
#has_many :products, through: :product_orders #has_many :products, through: :product_orders
+2
View File
@@ -7,6 +7,8 @@ class ProductCategory
belongs_to :supplier belongs_to :supplier
has_many :products has_many :products
attr_protected :supplier_id
validates :position, numericality: true validates :position, numericality: true
validates :supplier_id, presence: true validates :supplier_id, presence: true
end end
+2
View File
@@ -7,6 +7,8 @@ class Section
belongs_to :supplier belongs_to :supplier
has_many :tables has_many :tables
has_many :lists
has_many :orders
attr_protected :supplier_id attr_protected :supplier_id
+2 -1
View File
@@ -23,7 +23,8 @@ class Table
}|, reduce_function: '_sum' }|, reduce_function: '_sum'
def occupied? def occupied?
not self.class.database.view(self.class.active_lists(key: id, reduce: true)).zero? return @is_occupied if instance_variable_defined?(:'@is_occupied')
@is_occupied = !self.class.database.view(self.class.active_lists(key: id, reduce: true)).zero?
end end
def name def name
+6
View File
@@ -0,0 +1,6 @@
class TableMove
include SimplyStored::Couch
belongs_to :list
belongs_to :from_table, class_name: 'Table'
belongs_to :to_table, class_name: 'Table'
end
+11
View File
@@ -1,10 +1,21 @@
class User class User
include SimplyStored::Couch include SimplyStored::Couch
include Devise::Orm::SimplyStored include Devise::Orm::SimplyStored
property :active_list_id
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :registerable devise :database_authenticatable, :recoverable, :rememberable, :trackable, :registerable
has_and_belongs_to_many :lists, storing_keys: false has_and_belongs_to_many :lists, storing_keys: false
has_many :orders has_many :orders
validates_uniqueness_of :email validates_uniqueness_of :email
def list_is_closed!
self.active_list_id = nil
save
end
def has_active_list?
active_list_id.present?
end
end end
+1 -1
View File
@@ -1,4 +1,4 @@
.page-header= title 'Select Qr code' .page-header= title 'Select Qr code'
ul#qr-list ul#qr-list
- for table in @tables - for table in @tables
li= link_to_function image_tag(url_for(qrcode_table_path(table, format: :png))), %|Quser.actions_for_table('#{table.id}')| li= link_to_function image_tag(url_for(table_qr_image_path(table_id: table.id, format: :png))), %|Quser.actions_for_table({table_id: '#{table.id}'})|
+69
View File
@@ -0,0 +1,69 @@
doctype html
html lang="en"
head
meta charset="utf-8"
meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"
meta name="viewport" content="width=device-width, initial-scale=1.0"
title= content_for?(:title) ? yield(:title) : application_title
= csrf_meta_tags
/! Le HTML5 shim, for IE6-8 support of HTML elements
/[if lt IE 9]
= javascript_include_tag "http://html5shim.googlecode.com/svn/trunk/html5.js"
= stylesheet_link_tag "application", :media => "all"
link href="images/apple-touch-icon-144x144.png" rel="apple-touch-icon-precomposed" sizes="144x144"
link href="images/apple-touch-icon-114x114.png" rel="apple-touch-icon-precomposed" sizes="114x114"
link href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon-precomposed" sizes="72x72"
link href="images/apple-touch-icon.png" rel="apple-touch-icon-precomposed"
link href="images/favicon.ico" rel="shortcut icon"
body
.navbar.navbar-fixed-top
.navbar-inner
.container
a.btn.btn-navbar data-target=".nav-collapse" data-toggle="collapse"
span.icon-bar
span.icon-bar
span.icon-bar
a.brand href=root_path = application_title
.container.nav-collapse
ul.nav
li= link_to User.model_name.human_plural, users_path
li= link_to Supplier.model_name.human_plural, suppliers_path
li= link_to Table.model_name.human_plural, tables_path
li= link_to Product.model_name.human_plural, products_path
li= link_to List.model_name.human_plural, lists_path
li= link_to Order.model_name.human_plural, orders_path
li= link_to ProductCategory.model_name.human_plural, product_categories_path
.container
.content
- if flash[:alert].present?
.alert.alert-error
a.close data-dismiss="alert" &#215;
div= flash[:alert]
- if flash[:notice].present?
.alert.alert-success
a.close data-dismiss="alert" &#215;
div= flash[:notice]
.row
.span9
= yield
.span3
.well.sidebar-nav
h3= application_title
ul.nav.nav-list
li.nav-header Links
li= link_to "Home", root_path
li= link_to "Companytools", 'http://www.companytools.nl/'
= yield :sidebar
footer
p &copy; Companytools 2012
/!
Javascripts
\==================================================
/! Placed at the end of the document so the pages load faster
= javascript_include_tag "application"
= yield :footer
+2 -9
View File
@@ -16,8 +16,6 @@ html lang="en"
link href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon-precomposed" sizes="72x72" link href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon-precomposed" sizes="72x72"
link href="images/apple-touch-icon.png" rel="apple-touch-icon-precomposed" link href="images/apple-touch-icon.png" rel="apple-touch-icon-precomposed"
link href="images/favicon.ico" rel="shortcut icon" link href="images/favicon.ico" rel="shortcut icon"
javascript:
var active_list_id = #{session[:active_list_id] ? "'#{session[:active_list_id]}'" : 'null'};
body body
.navbar.navbar-fixed-top .navbar.navbar-fixed-top
@@ -30,13 +28,8 @@ html lang="en"
a.brand href=root_path = application_title a.brand href=root_path = application_title
.container.nav-collapse .container.nav-collapse
ul.nav ul.nav
li= link_to User.model_name.human_plural, users_path li= link_to User.model_name.human_plural, user_root_path
li= link_to Supplier.model_name.human_plural, suppliers_path li= link_to Supplier.model_name.human_plural, supplier_root_path
li= link_to Table.model_name.human_plural, tables_path
li= link_to Product.model_name.human_plural, products_path
li= link_to List.model_name.human_plural, lists_path
li= link_to Order.model_name.human_plural, orders_path
li= link_to ProductCategory.model_name.human_plural, product_categories_path
.container .container
+4 -3
View File
@@ -18,7 +18,10 @@ html lang="en"
link href="images/apple-touch-icon.png" rel="apple-touch-icon-precomposed" link href="images/apple-touch-icon.png" rel="apple-touch-icon-precomposed"
link href="images/favicon.ico" rel="shortcut icon" link href="images/favicon.ico" rel="shortcut icon"
javascript: javascript:
var active_list_id = #{session[:active_list_id] ? "'#{session[:active_list_id]}'" : 'null'}; // Dummy holder when Qmobile object is not supplied by the mobile phone
var QMobile = {
scanQr: function(){window.location = '/select_qrcode'}
}
body body
.navbar.navbar-fixed-top .navbar.navbar-fixed-top
@@ -31,8 +34,6 @@ html lang="en"
a.brand href=user_root_path = application_title a.brand href=user_root_path = application_title
.container.nav-collapse .container.nav-collapse
ul.nav#top-navigation-list ul.nav#top-navigation-list
- if list_open?
li= link_to 'Move table', '#'
li= link_to 'View history', user_list_history_path li= link_to 'View history', user_list_history_path
.container .container
+9 -3
View File
@@ -17,8 +17,6 @@ html lang="en"
link href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon-precomposed" sizes="72x72" link href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon-precomposed" sizes="72x72"
link href="images/apple-touch-icon.png" rel="apple-touch-icon-precomposed" link href="images/apple-touch-icon.png" rel="apple-touch-icon-precomposed"
link href="images/favicon.ico" rel="shortcut icon" link href="images/favicon.ico" rel="shortcut icon"
javascript:
var active_list_id = #{session[:active_list_id] ? "'#{session[:active_list_id]}'" : 'null'};
body body
.navbar.navbar-fixed-top.navbar-inverse .navbar.navbar-fixed-top.navbar-inverse
@@ -32,12 +30,20 @@ html lang="en"
ul.nav#top-navigation-list ul.nav#top-navigation-list
li= link_to t('supplier.menu.active_orders', orders: Order.model_name.human_plural), supplier_active_orders_path li= link_to t('supplier.menu.active_orders', orders: Order.model_name.human_plural), supplier_active_orders_path
li= link_to t('supplier.menu.active_lists', lists: List.model_name.human_plural), supplier_active_lists_path li= link_to t('supplier.menu.active_lists', lists: List.model_name.human_plural), supplier_active_lists_path
li= link_to ProductCategory.model_name.human_plural, suppliers_product_categories_path
li= link_to Product.model_name.human_plural, suppliers_products_path
li= link_to Section.model_name.human_plural, suppliers_sections_path li= link_to Section.model_name.human_plural, suppliers_sections_path
li= link_to Table.model_name.human_plural, suppliers_tables_path li= link_to Table.model_name.human_plural, suppliers_tables_path
ul.nav.pull-right
li.dropdown
a.dropdown-toggle href="#" data-toggle="dropdown"
= current_supplier.email
b.caret
ul.dropdown-menu
li= link_to 'Logout', destroy_supplier_session_path, method: :delete
.container.nav-collapse .container.nav-collapse
.container .container
.content .content
- if flash[:alert].present? - if flash[:alert].present?
.alert.alert-error .alert.alert-error
+1 -1
View File
@@ -11,7 +11,7 @@
.control-group class=(@product_category.errors[:supplier_id].any? ? 'error' : nil) .control-group class=(@product_category.errors[:supplier_id].any? ? 'error' : nil)
= f.label :supplier_id, Supplier.model_name.human, class: 'control-label' = f.label :supplier_id, Supplier.model_name.human, class: 'control-label'
.controls .controls
= f.select :supplier_id, options_for_select(@suppliers.map{|a| [a.name, a.id]}), include_blank: nil = f.collection_select :supplier_id, @suppliers, :id, :name, include_blank: nil
.form-actions .form-actions
= f.submit nil, class: 'btn btn-primary' = f.submit nil, class: 'btn btn-primary'
' '
+7 -7
View File
@@ -1,5 +1,5 @@
- model_class = ProductCategory - model_class = ProductCategory
div.page-header= title :index, model_class .page-header= title :index, model_class
- if @product_categories.any? - if @product_categories.any?
table.table.table-striped table.table.table-striped
thead thead
@@ -7,16 +7,16 @@ div.page-header= title :index, model_class
th= model_class.human_attribute_name(:name) th= model_class.human_attribute_name(:name)
th= model_class.human_attribute_name(:position) th= model_class.human_attribute_name(:position)
th= Supplier.model_name.human th= Supplier.model_name.human
th= model_class.human_attribute_name(:created_at) th.timestamp= model_class.human_attribute_name(:created_at)
th=t 'helpers.actions' th.actions=t 'helpers.actions'
tbody tbody
- @product_categories.each do |product_category| - @product_categories.each do |product_category|
tr tr
td= link_to product_category.name, product_category td.link= link_to product_category.name, product_category
td= product_category.position td= product_category.position
td= link_to_if product_category.supplier.present?, product_category.supplier.try(:name), product_category.supplier td.link= link_to_if product_category.supplier.present?, product_category.supplier.try(:name), product_category.supplier
td=l product_category.created_at, format: :short td.timestamp=l product_category.created_at, format: :short
td td.actions
= link_to t('helpers.links.edit'), [:edit, product_category], class: 'btn btn-mini' = link_to t('helpers.links.edit'), [:edit, product_category], class: 'btn btn-mini'
' '
= link_to t("helpers.links.destroy"), product_category, method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-mini btn-danger' = link_to t("helpers.links.destroy"), product_category, method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-mini btn-danger'
+8 -8
View File
@@ -1,5 +1,5 @@
- model_class = Product - model_class = Product
div.page-header= title :index, model_class .page-header= title :index, model_class
- if @products.any? - if @products.any?
table.table.table-striped table.table.table-striped
thead thead
@@ -9,18 +9,18 @@ div.page-header= title :index, model_class
th= model_class.human_attribute_name(:price) th= model_class.human_attribute_name(:price)
th= ProductCategory.model_name.human th= ProductCategory.model_name.human
th= Supplier.model_name.human th= Supplier.model_name.human
th= model_class.human_attribute_name(:created_at) th.timestamp= model_class.human_attribute_name(:created_at)
th=t 'helpers.actions' th.actions=t 'helpers.actions'
tbody tbody
- @products.each do |product| - @products.each do |product|
tr tr
td= link_to product.name, product td.link= link_to product.name, product
td= product.code td= product.code
td= product.price td= product.price
td= link_to_if product.product_category.present?, product.product_category.try(:name), product.product_category td.link= link_to_if product.product_category.present?, product.product_category.try(:name), product.product_category
td= link_to_if product.supplier.present?, product.supplier.try(:name), product.supplier td.link= link_to_if product.supplier.present?, product.supplier.try(:name), product.supplier
td=l product.created_at, format: :short td.timestamp=l product.created_at, format: :short
td td.actions
= link_to t('helpers.links.edit'), [:edit, product], class: 'btn btn-mini' = link_to t('helpers.links.edit'), [:edit, product], class: 'btn btn-mini'
' '
= link_to t("helpers.links.destroy"), product, method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-mini btn-danger' = link_to t("helpers.links.destroy"), product, method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-mini btn-danger'
@@ -0,0 +1,14 @@
= form_for [:suppliers, @product_category], html: {class: 'form-horizontal' } do |f|
= render 'error_messages', target: @product_category
.control-group class=(@product_category.errors[:name].any? ? 'error' : nil)
= f.label :name, class: 'control-label'
.controls
= f.text_field :name, class: 'text_field'
.control-group class=(@product_category.errors[:position].any? ? 'error' : nil)
= f.label :position, class: 'control-label'
.controls
= f.text_field :position, class: 'text_field'
.form-actions
= f.submit nil, class: 'btn btn-primary'
'
= link_to t("helpers.links.cancel"), suppliers_product_categories_path, class: 'btn'
@@ -0,0 +1,4 @@
- model_class = ProductCategory
.page-header
= title :edit, model_class
= render 'form'
@@ -0,0 +1,24 @@
- model_class = ProductCategory
.page-header= title :index, model_class
- if @product_categories.any?
table.table.table-striped
thead
tr
th= model_class.human_attribute_name(:name)
th= model_class.human_attribute_name(:position)
th.timestamp= model_class.human_attribute_name(:created_at)
th.actions=t 'helpers.actions'
tbody
- @product_categories.each do |product_category|
tr
td.link= link_to product_category.name, [:suppliers, product_category]
td= product_category.position
td.timestamp=l product_category.created_at, format: :short
td.actions
= link_to t('helpers.links.edit'), [:edit, :suppliers, product_category], class: 'btn btn-mini'
'
= link_to t("helpers.links.destroy"), [:suppliers, product_category], method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-mini btn-danger'
- else
= no_content_given model_class
= link_to t("helpers.links.new"), new_suppliers_product_category_path, class: 'btn btn-primary'
@@ -0,0 +1,4 @@
- model_class = ProductCategory
.page-header
= title :new, model_class
= render 'form'
@@ -0,0 +1,18 @@
- model_class = ProductCategory
.page-header= title :show, @product_category
dl.dl-horizontal.show-list
dt= model_class.human_attribute_name(:name)
dd= @product_category.name
dt= model_class.human_attribute_name(:position)
dd= @product_category.position
.form-actions
= link_to t("helpers.links.back"), suppliers_product_categories_path, class: 'btn'
'
= link_to t('helpers.links.edit'), [:edit, :suppliers, @product_category], class: 'btn'
'
= link_to t("helpers.links.destroy"), [:suppliers, @product_category], method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-danger'
- content_for :row do
- @products = @product_category.products
= render template: 'suppliers/products/index'
@@ -0,0 +1,22 @@
= form_for [:suppliers, @product], html: {class: 'form-horizontal' } do |f|
= render 'error_messages', target: @product
.control-group class=(@product.errors[:name].any? ? 'error' : nil)
= f.label :name, class: 'control-label'
.controls
= f.text_field :name, class: 'text_field'
.control-group class=(@product.errors[:code].any? ? 'error' : nil)
= f.label :code, class: 'control-label'
.controls
= f.text_field :code, class: 'text_field'
.control-group class=(@product.errors[:price].any? ? 'error' : nil)
= f.label :price, class: 'control-label'
.controls
= f.text_field :price, class: ['text_field', :currency]
.control-group class=(@product.errors[:product_category_id].any? ? 'error' : nil)
= f.label :product_category_id, ProductCategory.model_name.human, class: 'control-label'
.controls
= f.collection_select :product_category_id, current_supplier.product_categories, :id, :name, include_blank: ''
.form-actions
= f.submit nil, class: 'btn btn-primary'
'
= link_to t("helpers.links.cancel"), suppliers_products_path, class: 'btn'
@@ -0,0 +1,4 @@
- model_class = Product
.page-header
= title :edit, model_class
= render 'form'
@@ -0,0 +1,28 @@
- model_class = Product
.page-header= title :index, model_class
- if @products.any?
table.table.table-striped
thead
tr
th= model_class.human_attribute_name(:name)
th= model_class.human_attribute_name(:code)
th.currency= model_class.human_attribute_name(:price)
th= ProductCategory.model_name.human
th.timestamp= model_class.human_attribute_name(:created_at)
th.actions=t 'helpers.actions'
tbody
- @products.each do |product|
tr
td.link= link_to product.name, product
td= product.code
td.currency=currency product.price
td.link= link_to_if product.product_category.present?, product.product_category.try(:name), [:suppliers, product.product_category]
td.timestamp=l product.created_at, format: :short
td.actions
= link_to t('helpers.links.edit'), [:edit, :suppliers, product], class: 'btn btn-mini'
'
= link_to t("helpers.links.destroy"), [:suppliers, product], method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-mini btn-danger'
- else
= no_content_given model_class
= link_to t("helpers.links.new"), new_suppliers_product_path(product_category_id: @product_category.try(:id)), class: 'btn btn-primary'
@@ -0,0 +1,4 @@
- model_class = Product
.page-header
= title :new, model_class
= render 'form'
@@ -0,0 +1,20 @@
- model_class = Product
.page-header= title :show, @product
dl.dl-horizontal.show-list
dt= model_class.human_attribute_name(:name)
dd= @product.name
dt= model_class.human_attribute_name(:code)
dd= @product.code
dt= model_class.human_attribute_name(:price)
dd= @product.price
- if @product.product_category.present?
dt= ProductCategory.model_name.human
dd= link_to @product.product_category.name, @product.product_category
.form-actions
= link_to t("helpers.links.back"), suppliers_products_path, class: 'btn'
'
= link_to t('helpers.links.edit'), [:edit, :suppliers, @product], class: 'btn'
'
= link_to t("helpers.links.destroy"), [:suppliers, @product], method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-danger'
+4 -2
View File
@@ -16,7 +16,8 @@
- for table in @section.tables - for table in @section.tables
.section-table.hide{ id="section-table-#{table.id}" data-position-x=table.position_x data-position-y=table.position_y data-table-id=table.id} .section-table.hide{ id="section-table-#{table.id}" data-position-x=table.position_x data-position-y=table.position_y data-table-id=table.id}
.pull-right.action-button-container .pull-right.action-button-container
= link_to table.number, [:suppliers, table], class: 'btn btn-mini table-number' = link_to :i, [:suppliers, table], class: 'btn btn-mini table-link'
.table-number = table.number
.span3 .span3
h3= t('table.has_no_section') h3= t('table.has_no_section')
.well.section-tables-container.section-tables-inactive .well.section-tables-container.section-tables-inactive
@@ -24,7 +25,8 @@
.section-table{ id="section-table-#{table.id}" data-position-x=table.position_x data-position-y=table.position_y data-table-id=table.id} .section-table{ id="section-table-#{table.id}" data-position-x=table.position_x data-position-y=table.position_y data-table-id=table.id}
.pull-right.action-button-container .pull-right.action-button-container
button.btn.btn-primary.btn-mini onClick="Qsupplier.move_table_to_active_section('#{table.id}')" + button.btn.btn-primary.btn-mini onClick="Qsupplier.move_table_to_active_section('#{table.id}')" +
= link_to table.number, [:suppliers, table], class: 'btn btn-mini table-number' = link_to :i, [:suppliers, table], class: 'btn btn-mini table-link'
.table-number = table.number
.clearfix .clearfix
- content_for :footer do - content_for :footer do
javascript: javascript:
+4 -7
View File
@@ -1,10 +1,7 @@
ul.nav.nav-tabs.nav-stacked ul.nav.nav-tabs.nav-stacked
li
button.btn.btn-primary onClick="QMobile.scanQr()" Scan Qr
- if list_open? - if list_open?
li= link_to 'Place order', user_list_products_path(supplier_id: active_list.supplier.id) li= link_to 'Place order', user_list_products_path
li= link_to 'Active list', user_active_list_path li= link_to 'Show active list', user_active_list_path
- else
li= link_to 'Join table with Qr scan', '/select_qrcode'
li
button.btn.btn-primary onClick="QMobile.scanQr()" Scan Qr
li= link_to 'Subscribe to list', '#' li= link_to 'Subscribe to list', '#'
li= link_to 'Check out menu', '#'
+3 -1
View File
@@ -1,2 +1,4 @@
.page-header= title 'User list history' .page-header= title 'User list history'
p Todo ul
- for list in @lists
li= link_to l(list.created_at, format: :short), user_history_list_path(list_id: list.id)
-1
View File
@@ -4,7 +4,6 @@
span#list-needs-help-button span#list-needs-help-button
table#products-table.table.table-striped.table-hover table#products-table.table.table-striped.table-hover
tbody tbody
-# content_for :sidebar do
table#active-order-table.table.table-striped.hide table#active-order-table.table.table-striped.hide
thead thead
tr tr
@@ -0,0 +1,27 @@
.page-header
h4= t('user.show_products.title', products: Product.model_name.human_plural)
table#products-table.table.table-striped.table-hover
tbody
table#active-order-table.table.table-striped.hide
thead
tr
th Product
th #
th.currency Total
th
tbody
tfoot
tr
td colspan=2
button class="btn btn-primary" onClick="Quser.order_selected_products({table_id: '#{@table.id}'})" Bestellen
|&nbsp;
button class="btn btn btn-warning" onClick="Qrammer.clear_active_list()" Clear
td.currency
strong#active-order-total
td
- content_for :footer do
javascript:
jQuery(function(){
Quser.load_table_products('#{@table.id}', #{@table.occupied? ? 'true' : 'false'});
})
+1
View File
@@ -77,3 +77,4 @@ module Qrammer
config.assets.version = '1.0' config.assets.version = '1.0'
end end
end end
require 'rqrcode-rails3'
+1
View File
@@ -38,6 +38,7 @@ Qrammer::Application.configure do
# Do not compress assets # Do not compress assets
config.assets.compress = false config.assets.compress = false
config.assets.logger = Logger.new('/dev/null')
# Expands the lines which load the assets # Expands the lines which load the assets
config.assets.debug = true config.assets.debug = true
+2 -2
View File
@@ -191,7 +191,7 @@ Devise.setup do |config|
# Configure sign_out behavior. # Configure sign_out behavior.
# Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope). # Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope).
# The default is true, which means any logout action will sign out all active scopes. # The default is true, which means any logout action will sign out all active scopes.
# config.sign_out_all_scopes = true config.sign_out_all_scopes = false
# ==> Navigation configuration # ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like # Lists the formats that should be treated as navigational. Formats like
@@ -205,7 +205,7 @@ Devise.setup do |config|
# config.navigational_formats = ["*/*", :html] # config.navigational_formats = ["*/*", :html]
# The default HTTP method used to sign out a resource. Default is :delete. # The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete config.sign_out_via = [:delete, :get]
# ==> OmniAuth # ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting # Add a new OmniAuth provider. Check the wiki for more information on setting
+13 -8
View File
@@ -1,13 +1,11 @@
Qrammer::Application.routes.draw do Qrammer::Application.routes.draw do
devise_for :users devise_for :users
devise_for :suppliers devise_for :suppliers
authenticate :supplier do devise_for :administrators
#authenticate :administrator do
resources :users resources :users
resources :tables do resources :tables
member do
get :qrcode
end
end
resources :orders do resources :orders do
member do member do
post :is_being_processed post :is_being_processed
@@ -18,7 +16,7 @@ Qrammer::Application.routes.draw do
resources :lists resources :lists
resources :products resources :products
resources :product_categories resources :product_categories
end #end
get '/supplier' => 'supplier#home', as: :supplier_root get '/supplier' => 'supplier#home', as: :supplier_root
get '/supplier/active_orders' => 'supplier#active_orders', as: :supplier_active_orders get '/supplier/active_orders' => 'supplier#active_orders', as: :supplier_active_orders
@@ -38,9 +36,11 @@ Qrammer::Application.routes.draw do
post '/user/list_needs_payment' => 'user#list_needs_payment', as: :user_list_needs_payment post '/user/list_needs_payment' => 'user#list_needs_payment', as: :user_list_needs_payment
match '/user/create_list' => 'user#create_list', as: :user_create_list match '/user/create_list' => 'user#create_list', as: :user_create_list
get '/user/list_products' => 'user#list_products', as: :user_list_products get '/user/list_products' => 'user#list_products', as: :user_list_products
get '/user/list_products_for_table' => 'user#list_products_for_table', as: :user_list_products_for_table
match '/user/list_history' => 'user#list_history', as: :user_list_history match '/user/list_history' => 'user#list_history', as: :user_list_history
match '/user/list_history/:list_id' => 'user#history_list', as: :user_history_list match '/user/list_history/:list_id' => 'user#history_list', as: :user_history_list
post '/user/order_selected_products' => 'user#order_selected_products', as: :user_order_selected_products post '/user/order_selected_products' => 'user#order_selected_products', as: :user_order_selected_products
post '/user/move_table' => 'user#move_table', as: :user_move_table
get '/user/table_info' => 'user#table_info', as: :user_table_info get '/user/table_info' => 'user#table_info', as: :user_table_info
match '/show_products' => 'dashboard#show_products', as: :user_products match '/show_products' => 'dashboard#show_products', as: :user_products
@@ -48,10 +48,15 @@ Qrammer::Application.routes.draw do
namespace :suppliers, path: '/supplier' do namespace :suppliers, path: '/supplier' do
resources :sections resources :sections
resources :tables resources :tables
resources :products
resources :product_categories
root to: 'sections#index' root to: 'sections#index'
end end
match "/:action", controller: 'dashboard' #DEMO & DEVELOPMENT
get '/select_qrcode' => 'dashboard#select_qrcode'
get '/table_qr_image' => 'dashboard#table_qr_image', as: :table_qr_image
#match "/:action", controller: 'dashboard'
# The priority is based upon order of creation: # The priority is based upon order of creation:
# first created -> highest priority. # first created -> highest priority.
+31
View File
@@ -0,0 +1,31 @@
require 'action_controller'
require 'rqrcode'
require 'rqrcode-rails3/size_calculator.rb'
require 'rqrcode-rails3/renderers/svg.rb'
module RQRCode
Mime::Type.register "image/svg+xml", :svg unless Mime::Type.lookup_by_extension(:svg)
Mime::Type.register "image/png", :png unless Mime::Type.lookup_by_extension(:png)
extend SizeCalculator
ActionController::Renderers.add :qrcode do |string, options|
format = self.request.format.symbol
size = options[:size] || RQRCode.minimum_qr_size_from_string(string)
level = options[:level] || :h
qrcode = RQRCode::QRCode.new(string, :size => size, :level => level)
svg = RQRCode::Renderers::SVG::render(qrcode, options)
data = \
if format == :png
image = MiniMagick::Image.read(svg) { |i| i.format "svg" }
image.format "png"
png = image.to_blob
else
svg
end
self.response_body = render_to_string(:text => data, :template => nil)
end
end
+109
View File
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="318.89764"
height="318.89764"
id="svg2991"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="table_qr_image.svg">
<defs
id="defs3">
<filter
id="filter3789"
inkscape:label="Cutout"
x="0"
y="0"
width="1"
height="1"
inkscape:menu="Shadows and Glows"
inkscape:menu-tooltip="Drop shadow under the cut-out of the shape"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3791"
in="SourceAlpha"
stdDeviation="4.2" />
<feOffset
id="feOffset3793"
dy="5"
dx="5"
result="result91" />
<feComposite
id="feComposite3795"
in2="result91"
operator="out"
in="SourceGraphic" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.3773478"
inkscape:cx="107.03459"
inkscape:cy="78.51279"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="mm"
inkscape:window-width="1440"
inkscape:window-height="856"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0" />
<metadata
id="metadata2995">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,141.73227)">
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:semi-expanded;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffb213;fill-opacity:1;stroke:none;font-family:BankGothic Md BT;-inkscape-font-specification:BankGothic Md BT Semi-Expanded"
x="80.589668"
y="-90.740822"
id="text4695"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4697"
x="80.589668"
y="-90.740822">Qwaiter</tspan></text>
<g
id="g3838"
transform="matrix(0.49177687,0,0,0.49177687,49.419301,-77.511451)">#qrcode
</g>
<text
xml:space="preserve"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
x="277.10553"
y="160.40939"
id="text3799"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3801"
x="277.10553"
y="160.40939">#table_number</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

+50
View File
@@ -0,0 +1,50 @@
module RQRCode
module Renderers
class SVG
class << self
# Render the SVG from the qrcode string provided from the RQRCode gem
# Options:
# offset - Padding around the QR Code (e.g. 10)
# unit - How many pixels per module (Default: 11)
# fill - Background color (e.g "ffffff" or :white)
# color - Foreground color for the code (e.g. "000000" or :black)
def render(qrcode, options={})
offset = options[:offset].to_i || 0
color = options[:color] || "000"
unit = options[:unit] || 11
# height and width dependent on offset and QR complexity
dimension = (qrcode.module_count*unit) + (2*offset)
xml_tag = %{<?xml version="1.0" standalone="yes"?>}
open_tag = %{<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" width="#{dimension}" height="#{dimension}">}
close_tag = "</svg>"
result = []
qrcode.modules.each_index do |c|
tmp = []
qrcode.modules.each_index do |r|
y = c*unit + offset
x = r*unit + offset
next unless qrcode.is_dark(c, r)
tmp << %{<rect width="#{unit}" height="#{unit}" x="#{x}" y="#{y}" style="fill:##{color}"/>}
end
result << tmp.join("\n")
end
if options[:fill]
result.unshift %{<rect width="#{dimension}" height="#{dimension}" x="0" y="0" style="fill:##{options[:fill]}"/>}
end
svg = [xml_tag, open_tag, result, close_tag].flatten.join("\n")
svg = File.read(File.expand_path('../../qr_container.svg', __FILE__))
svg.gsub!(/#table_number/, options[:table_number].to_s)
svg.gsub!(/#qrcode/, result.join("\n"))
svg
end
end
end
end
end
+40
View File
@@ -0,0 +1,40 @@
module RQRCode
module SizeCalculator
# size - seems to follow this logic
# # | input | modules
# | size | created
#-------|-------|--------
# 1 | 7 | 21
# 2 | 14 | 25 (+4)
# 3 | 24 | 29 -
# 4 | 34 | 33 -
# 5 | 44 | 37 -
# 6 | 58 | 41 -
# 7 | 64 | 45 -
# 8 | 84 | 49 -
# 9 | 98 | 53 -
# 10 | 119 | 57 -
# 11 | 137 | 61 -
# 12 | 155 | 65 -
# 13 | 177 | 69 -
# 14 | 194 | 73 -
QR_CHAR_SIZE_VS_SIZE = [7, 14, 24, 34, 44, 58, 64, 84, 98, 119, 137, 155, 177, 194]
def minimum_qr_size_from_string(string)
QR_CHAR_SIZE_VS_SIZE.each_with_index do |size, index|
return (index + 1) if string.size < size
end
# If it's particularly big, we'll try and create codes until it accepts
i = QR_CHAR_SIZE_VS_SIZE.size
begin
i += 1
RQRCode::QRCode.new(string, :size => i)
return i
rescue RQRCode::QRCodeRunTimeError
retry
end
end
end
end
+30
View File
@@ -2,6 +2,18 @@ Stories:
- Person moves to different table - Person moves to different table
- Person tries to create list on occupied table - Person tries to create list on occupied table
- Person walks away without paying - Person walks away without paying
- Person checks out menu through qr scan
- Other persion occupies the table by placing an order
- Person tries to order something
-> Is informed that the table is not occupied
-> redirect to user_root_path
- Person1 check out the products by scanning the Qr of an empty table
- Person2 checks out the products by scanning the Qr of the same table
- Person2 places order and gets the list
- Person has an active list and creates an order
- Supplier closes the list
- Person submits order
-> Cannot orde on closed list
Person actions: Person actions:
When list is open: When list is open:
@@ -42,3 +54,21 @@ Wireless wachtwoord via qr code
handle closed list on list update for user list handle closed list on list update for user list
Scan qr button:
If table is not occupied:
- if there is an open list
- if the scan is of the same supplier
- Offer to move table
- else
- Cannot perform action with open list
- else
View menu direct
- When order is placed:
- create list
- add order to list
- table is assigned to person (occupied)
If table is occupied:
- Offer to Join