diff --git a/Gemfile b/Gemfile
index d986d08d..e5947040 100644
--- a/Gemfile
+++ b/Gemfile
@@ -30,7 +30,7 @@ gem 'simply_stored' , :git => 'git://github.com/bterkuile/simply_stored.git'
gem 'devise', '2.0.4'
gem 'devise_simply_stored'
-gem 'rqrcode-rails3'
+gem 'rqrcode'
gem 'mini_magick'
#gem "less-rails-bootstrap-devise", :git => 'git://github.com/bigbento/less-rails-bootstrap-devise.git'
diff --git a/Gemfile.lock b/Gemfile.lock
index 8a2f62b2..8ce3c83f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -157,8 +157,6 @@ GEM
rest-client (1.6.7)
mime-types (>= 1.16)
rqrcode (0.4.2)
- rqrcode-rails3 (0.1.5)
- rqrcode (>= 0.4.2)
rspec (2.11.0)
rspec-core (~> 2.11.0)
rspec-expectations (~> 2.11.0)
@@ -240,7 +238,7 @@ DEPENDENCIES
mini_magick
pry
rails (= 3.2.8)
- rqrcode-rails3
+ rqrcode
rspec-rails
sass-rails (~> 3.2.3)
selenium-webdriver
diff --git a/Qr scan.mm b/Qr scan.mm
new file mode 100644
index 00000000..022dd610
--- /dev/null
+++ b/Qr scan.mm
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Show products with ordering options
+
+
+
+
+
+
+
+
+
+
diff --git a/app/assets/javascripts/bootstrap.js.coffee b/app/assets/javascripts/bootstrap.js.coffee
index c9404a8e..8bca8446 100644
--- a/app/assets/javascripts/bootstrap.js.coffee
+++ b/app/assets/javascripts/bootstrap.js.coffee
@@ -1,4 +1,12 @@
jQuery ->
$("a[rel=popover]").popover()
$(".tooltip").tooltip()
- $("a[rel=tooltip]").tooltip()
\ No newline at end of file
+ $("a[rel=tooltip]").tooltip()
+ $('input.currency').each(->
+ obj = $(this)
+ original_width = obj.width()
+ obj.wrap("
")
+ currency_sign = $("€ ")
+ obj.before(currency_sign)
+ obj.css('width', (original_width - currency_sign.outerWidth())+'px')
+ )
diff --git a/app/assets/javascripts/jquery.cookie.js b/app/assets/javascripts/jquery.cookie.js
new file mode 100644
index 00000000..6df1faca
--- /dev/null
+++ b/app/assets/javascripts/jquery.cookie.js
@@ -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;
+ }
+};
\ No newline at end of file
diff --git a/app/assets/javascripts/quser.js.coffee b/app/assets/javascripts/quser.js.coffee
index 154580fc..3d1d8600 100644
--- a/app/assets/javascripts/quser.js.coffee
+++ b/app/assets/javascripts/quser.js.coffee
@@ -59,8 +59,8 @@ root.Quser=
row.append($('
').html(Qrammer.currency(order.total_amount)))
foot.append(''+Qrammer.currency(res.total_amount)+' ')
)
- order_selected_products: ()->
- h = {}
+ order_selected_products: (h)->
+ h ||= {}
for product_id, info of window.active_products_list
h['products['+product_id+']'] = info.number
$.post('/user/order_selected_products', h, ((res) -> Quser.handle_response(res)), 'json')
@@ -92,48 +92,67 @@ root.Quser=
table.show()
load_active_list_products: ->
- $.get('/user/list_products.json', (res) ->
- window.products = res
+ Quser.populate_products_table('/user/list_products.json', true)
+ 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')
- for category, products of window.products
+ for category, products of res
body.append(''+category+' ')
for product in products
row = $(' ')
- button = $('Add ')
- 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(''+product.name+' ')
- order_product_count = $('1 ')
- order_count_minus = $('- ')
- order_count_minus.click(->
- val_holder = $(this).siblings('.order-product-count')
- val = parseInt(val_holder.text())
- val_holder.text(val - 1) if val > 1
- )
- order_count_plus = $('+ ')
- order_count_plus.click(->
- val_holder = $(this).siblings('.order-product-count')
- val = parseInt(val_holder.text())
- val_holder.text(val + 1)
- )
- row.append($(' ').append(order_count_minus).append(' ').append(order_product_count).append(' ').append(order_count_plus))
+ if include_order_buttons
+ button = $('Add ')
+ 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)
+ order_product_count = $('1 ')
+ order_count_minus = $('- ')
+ order_count_minus.click(->
+ val_holder = $(this).siblings('.order-product-count')
+ val = parseInt(val_holder.text())
+ val_holder.text(val - 1) if val > 1
+ )
+ order_count_plus = $('+ ')
+ order_count_plus.click(->
+ val_holder = $(this).siblings('.order-product-count')
+ val = parseInt(val_holder.text())
+ val_holder.text(val + 1)
+ )
+ row.append($(' ').append(order_count_minus).append(' ').append(order_product_count).append(' ').append(order_count_plus))
row.append(''+Qrammer.currency(product.price)+' ')
- row.append($(' ').append(button))
+ row.append($(' ').append(button)) if include_order_buttons
body.append(row)
)
-
- actions_for_table: (table_id)->
- $.get('/user/table_info.json?table_id='+table_id, (res)->
+ actions_for_table: (table)->
+ table = JSON.parse(table) if typeof(table) == 'string'
+ $.get('/user/table_info.json?table_id='+table.table_id, (res)->
if res.occupied
alert('Table is occupied')
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')
add_product: (product, count) ->
count ||= 1
diff --git a/app/assets/stylesheets/forms.css.sass b/app/assets/stylesheets/forms.css.sass
new file mode 100644
index 00000000..0c9cb99b
--- /dev/null
+++ b/app/assets/stylesheets/forms.css.sass
@@ -0,0 +1,3 @@
+form
+ input.currency
+ text-align: right
diff --git a/app/assets/stylesheets/structure.css.sass b/app/assets/stylesheets/structure.css.sass
index 13699164..46ce60cd 100644
--- a/app/assets/stylesheets/structure.css.sass
+++ b/app/assets/stylesheets/structure.css.sass
@@ -79,3 +79,5 @@ table
list-style: none
margin: 0
padding: 0
+ li
+ float: left
diff --git a/app/assets/stylesheets/tablet/structure.css.sass b/app/assets/stylesheets/tablet/structure.css.sass
index 75dfc0e6..eab1da4c 100644
--- a/app/assets/stylesheets/tablet/structure.css.sass
+++ b/app/assets/stylesheets/tablet/structure.css.sass
@@ -17,9 +17,16 @@ body
height: 44px
background-repeat: no-repeat
width: 45px
- background-image: image-url('icons/section-table.png')
- .table-number
+ //background-image: image-url('icons/section-table.png')
+ .table-link
margin-top: -45px
+ .table-number
+ position: absolute
+ top: 0
+ line-height: 44px
+ width: 45px
+ font-size: 42px
+ text-align: center
.action-button-container
margin-right: -20px
&.section-tables-active
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index a752a327..2bc32695 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -14,9 +14,9 @@ class ApplicationController < ActionController::Base
end
end
def check_active_list_state
- if session[:active_list_id]
+ if current_user.try(:active_list_id)
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)
end
end
@@ -24,8 +24,8 @@ class ApplicationController < ActionController::Base
end
def active_list
- return nil unless session[:active_list_id].present?
- @active_list ||= List.find(session[:active_list_id])
+ return nil unless current_user.try(:active_list_id).present?
+ @active_list ||= List.find(current_user.active_list_id)
end
alias :active_list_object :active_list
helper_method :active_list_object
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index f2e427f6..81aa9d91 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -1,8 +1,5 @@
class DashboardController < ApplicationController
-
- before_filter :check_active_list_state, except: :home
def home
-
end
@@ -16,9 +13,17 @@ class DashboardController < ApplicationController
end
- def supplier_home
- redirect_to active_orders_supplier_path(Supplier.first)
+ # GET /select_qr_image
+ # 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
diff --git a/app/controllers/suppliers/application_controller.rb b/app/controllers/suppliers/application_controller.rb
index 9440529a..cee350c2 100644
--- a/app/controllers/suppliers/application_controller.rb
+++ b/app/controllers/suppliers/application_controller.rb
@@ -1,5 +1,6 @@
module Suppliers
class ApplicationController < ::ApplicationController
+ before_filter :authenticate_supplier!
layout 'tablet'
end
diff --git a/app/controllers/suppliers/product_categories_controller.rb b/app/controllers/suppliers/product_categories_controller.rb
new file mode 100644
index 00000000..b536c2f4
--- /dev/null
+++ b/app/controllers/suppliers/product_categories_controller.rb
@@ -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
diff --git a/app/controllers/suppliers/products_controller.rb b/app/controllers/suppliers/products_controller.rb
new file mode 100644
index 00000000..da8bf91c
--- /dev/null
+++ b/app/controllers/suppliers/products_controller.rb
@@ -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
diff --git a/app/controllers/tables_controller.rb b/app/controllers/tables_controller.rb
index 25d2a3c2..be3be830 100644
--- a/app/controllers/tables_controller.rb
+++ b/app/controllers/tables_controller.rb
@@ -82,19 +82,6 @@ class TablesController < ApplicationController
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
def set_relation_options
diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb
index 7c650d3f..b6153ab3 100644
--- a/app/controllers/user_controller.rb
+++ b/app/controllers/user_controller.rb
@@ -9,6 +9,7 @@ class UserController < ApplicationController
end
# POST /user/create_list {table_id: 1234}
+ #DEPRICATED
def create_list
@table = Table.find(params[:table_id])
if @table.occupied?
@@ -17,11 +18,8 @@ class UserController < ApplicationController
format.json { render json: js_alert(t('messages.table_is_occupied'))}
end
else
- @list = List.new(table: @table, supplier_id: @table.supplier_id)
- @list.add_user current_user
- #@list.add_user(current_user)
- @list.save
- session[:active_list_id] = @list.id
+ if @list = List.from_table( @table, current_user )
+ end
respond_to do |format|
format.html { redirect_to user_list_products_path }
format.json { render json: js_notice('table_created')}
@@ -31,9 +29,15 @@ class UserController < ApplicationController
def table_info
@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|
format.json do
- render json: {occupied: @table.occupied?}
+ render json: res
end
end
end
@@ -56,6 +60,22 @@ class UserController < ApplicationController
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
# Information about the currently active list
# This information includes detailed order information
@@ -97,7 +117,7 @@ class UserController < ApplicationController
respond_to do |format|
format.json do
if !list.try(:active?)
- session[:active_list_id] = nil
+ current_user.list_is_closed!
render json: {list_active: false}
return
else
@@ -144,8 +164,8 @@ class UserController < ApplicationController
# GET /user/list_history/:list_id
def history_list
@list = List.find(params[:list_id])
- if params[:list_closed].present? && session[:active_list_id] == @list.id
- session[:active_list_id] = nil
+ if params[:list_closed].present? && current_user.active_list_id == @list.id
+ current_user.list_is_closed!
flash.now[:notice] = t('messages.the_list_has_been_closed', list: List.model_name.human)
end
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
- @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|
format.html do
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
+ 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
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index ba86ea63..de9f64ba 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -26,8 +26,28 @@ module ApplicationHelper
'Qwaiter'
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 '£ '
+ # when 'USD' then '$ '
+ # else '€ '
+ # end
+ prefix = '€ '
+ return (prefix + ("%.2f" % amount)).html_safe
+
+ # Rails native is ambiguous since is uses translation hashes
+ options = {:unit => '€'}.merge(opts)
+ number_to_currency(amount, options)
+ end
def list_open?
- session[:active_list_id].present?
+ active_list_id.present?
+ end
+
+ def active_list_id
+ current_user.try(:active_list_id)
end
def no_content_given(model)
diff --git a/app/models/administrator.rb b/app/models/administrator.rb
new file mode 100644
index 00000000..3cb208f6
--- /dev/null
+++ b/app/models/administrator.rb
@@ -0,0 +1,5 @@
+class Administrator
+ include SimplyStored::Couch
+ include Devise::Orm::SimplyStored
+ devise :database_authenticatable, :rememberable #, :recoverable, :rememberable, :trackable, :registerable
+end
diff --git a/app/models/list.rb b/app/models/list.rb
index e7bdf84f..11e45d0c 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -8,6 +8,7 @@ class List
has_many :orders, dependent: :destroy
belongs_to :table
belongs_to :supplier
+ belongs_to :section
has_and_belongs_to_many :users, storing_keys: true
validates :table_id, presence: true
@@ -15,6 +16,16 @@ class List
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!
orders.map(&:close!)
self.state = 'closed'
@@ -48,6 +59,13 @@ class List
@order
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
super.merge(table_number: table_number)
end
diff --git a/app/models/order.rb b/app/models/order.rb
index 4c6a9453..a26b7759 100644
--- a/app/models/order.rb
+++ b/app/models/order.rb
@@ -6,6 +6,7 @@ class Order
belongs_to :list
belongs_to :user
belongs_to :supplier
+ belongs_to :section
has_many :product_orders, dependent: :destroy
#has_many :products, through: :product_orders
diff --git a/app/models/product_category.rb b/app/models/product_category.rb
index 3cade073..3c692e1a 100644
--- a/app/models/product_category.rb
+++ b/app/models/product_category.rb
@@ -7,6 +7,8 @@ class ProductCategory
belongs_to :supplier
has_many :products
+ attr_protected :supplier_id
+
validates :position, numericality: true
validates :supplier_id, presence: true
end
diff --git a/app/models/section.rb b/app/models/section.rb
index 08f05ad5..7329690c 100644
--- a/app/models/section.rb
+++ b/app/models/section.rb
@@ -7,6 +7,8 @@ class Section
belongs_to :supplier
has_many :tables
+ has_many :lists
+ has_many :orders
attr_protected :supplier_id
diff --git a/app/models/table.rb b/app/models/table.rb
index 5042c97a..65837e08 100644
--- a/app/models/table.rb
+++ b/app/models/table.rb
@@ -23,7 +23,8 @@ class Table
}|, reduce_function: '_sum'
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
def name
diff --git a/app/models/table_move.rb b/app/models/table_move.rb
new file mode 100644
index 00000000..41f03ec0
--- /dev/null
+++ b/app/models/table_move.rb
@@ -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
diff --git a/app/models/user.rb b/app/models/user.rb
index 25c6e760..864eb11a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,10 +1,21 @@
class User
include SimplyStored::Couch
include Devise::Orm::SimplyStored
+ property :active_list_id
+
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :registerable
has_and_belongs_to_many :lists, storing_keys: false
has_many :orders
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
diff --git a/app/views/dashboard/select_qrcode.html.slim b/app/views/dashboard/select_qrcode.html.slim
index 4dbfab79..1f427d2a 100644
--- a/app/views/dashboard/select_qrcode.html.slim
+++ b/app/views/dashboard/select_qrcode.html.slim
@@ -1,4 +1,4 @@
.page-header= title 'Select Qr code'
ul#qr-list
- 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}'})|
diff --git a/app/views/layouts/administrator.html.slim b/app/views/layouts/administrator.html.slim
new file mode 100644
index 00000000..00b96415
--- /dev/null
+++ b/app/views/layouts/administrator.html.slim
@@ -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" ×
+ div= flash[:alert]
+ - if flash[:notice].present?
+ .alert.alert-success
+ a.close data-dismiss="alert" ×
+ 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 © Companytools 2012
+ /!
+ Javascripts
+ \==================================================
+ /! Placed at the end of the document so the pages load faster
+ = javascript_include_tag "application"
+ = yield :footer
diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim
index fcdc6ee0..f1dca3e8 100644
--- a/app/views/layouts/application.html.slim
+++ b/app/views/layouts/application.html.slim
@@ -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.png" rel="apple-touch-icon-precomposed"
link href="images/favicon.ico" rel="shortcut icon"
- javascript:
- var active_list_id = #{session[:active_list_id] ? "'#{session[:active_list_id]}'" : 'null'};
body
.navbar.navbar-fixed-top
@@ -30,13 +28,8 @@ html lang="en"
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
+ li= link_to User.model_name.human_plural, user_root_path
+ li= link_to Supplier.model_name.human_plural, supplier_root_path
.container
diff --git a/app/views/layouts/phone.html.slim b/app/views/layouts/phone.html.slim
index 72777675..07b9517c 100644
--- a/app/views/layouts/phone.html.slim
+++ b/app/views/layouts/phone.html.slim
@@ -18,7 +18,10 @@ html lang="en"
link href="images/apple-touch-icon.png" rel="apple-touch-icon-precomposed"
link href="images/favicon.ico" rel="shortcut icon"
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
.navbar.navbar-fixed-top
@@ -31,8 +34,6 @@ html lang="en"
a.brand href=user_root_path = application_title
.container.nav-collapse
ul.nav#top-navigation-list
- - if list_open?
- li= link_to 'Move table', '#'
li= link_to 'View history', user_list_history_path
.container
diff --git a/app/views/layouts/tablet.html.slim b/app/views/layouts/tablet.html.slim
index 223ae452..940b97b5 100644
--- a/app/views/layouts/tablet.html.slim
+++ b/app/views/layouts/tablet.html.slim
@@ -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.png" rel="apple-touch-icon-precomposed"
link href="images/favicon.ico" rel="shortcut icon"
- javascript:
- var active_list_id = #{session[:active_list_id] ? "'#{session[:active_list_id]}'" : 'null'};
body
.navbar.navbar-fixed-top.navbar-inverse
@@ -32,12 +30,20 @@ html lang="en"
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_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 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
-
.content
- if flash[:alert].present?
.alert.alert-error
diff --git a/app/views/product_categories/_form.html.slim b/app/views/product_categories/_form.html.slim
index 2b645dee..0a89e31f 100644
--- a/app/views/product_categories/_form.html.slim
+++ b/app/views/product_categories/_form.html.slim
@@ -11,7 +11,7 @@
.control-group class=(@product_category.errors[:supplier_id].any? ? 'error' : nil)
= f.label :supplier_id, Supplier.model_name.human, class: 'control-label'
.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
= f.submit nil, class: 'btn btn-primary'
'
diff --git a/app/views/product_categories/index.html.slim b/app/views/product_categories/index.html.slim
index 1391c790..9f0566ed 100644
--- a/app/views/product_categories/index.html.slim
+++ b/app/views/product_categories/index.html.slim
@@ -1,5 +1,5 @@
- model_class = ProductCategory
-div.page-header= title :index, model_class
+.page-header= title :index, model_class
- if @product_categories.any?
table.table.table-striped
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(:position)
th= Supplier.model_name.human
- th= model_class.human_attribute_name(:created_at)
- th=t 'helpers.actions'
+ th.timestamp= model_class.human_attribute_name(:created_at)
+ th.actions=t 'helpers.actions'
tbody
- @product_categories.each do |product_category|
tr
- td= link_to product_category.name, product_category
+ td.link= link_to product_category.name, product_category
td= product_category.position
- td= link_to_if product_category.supplier.present?, product_category.supplier.try(:name), product_category.supplier
- td=l product_category.created_at, format: :short
- td
+ td.link= link_to_if product_category.supplier.present?, product_category.supplier.try(:name), product_category.supplier
+ td.timestamp=l product_category.created_at, format: :short
+ td.actions
= 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'
diff --git a/app/views/products/index.html.slim b/app/views/products/index.html.slim
index cf21bdb5..4e3898c0 100644
--- a/app/views/products/index.html.slim
+++ b/app/views/products/index.html.slim
@@ -1,5 +1,5 @@
- model_class = Product
-div.page-header= title :index, model_class
+.page-header= title :index, model_class
- if @products.any?
table.table.table-striped
thead
@@ -9,18 +9,18 @@ div.page-header= title :index, model_class
th= model_class.human_attribute_name(:price)
th= ProductCategory.model_name.human
th= Supplier.model_name.human
- th= model_class.human_attribute_name(:created_at)
- th=t 'helpers.actions'
+ th.timestamp= model_class.human_attribute_name(:created_at)
+ th.actions=t 'helpers.actions'
tbody
- @products.each do |product|
tr
- td= link_to product.name, product
+ td.link= link_to product.name, product
td= product.code
td= product.price
- td= 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=l product.created_at, format: :short
- td
+ td.link= link_to_if product.product_category.present?, product.product_category.try(:name), product.product_category
+ td.link= link_to_if product.supplier.present?, product.supplier.try(:name), product.supplier
+ td.timestamp=l product.created_at, format: :short
+ td.actions
= 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'
diff --git a/app/views/suppliers/product_categories/_form.html.slim b/app/views/suppliers/product_categories/_form.html.slim
new file mode 100644
index 00000000..d3b0e2ec
--- /dev/null
+++ b/app/views/suppliers/product_categories/_form.html.slim
@@ -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'
diff --git a/app/views/suppliers/product_categories/edit.html.slim b/app/views/suppliers/product_categories/edit.html.slim
new file mode 100644
index 00000000..b68302b0
--- /dev/null
+++ b/app/views/suppliers/product_categories/edit.html.slim
@@ -0,0 +1,4 @@
+- model_class = ProductCategory
+.page-header
+ = title :edit, model_class
+= render 'form'
diff --git a/app/views/suppliers/product_categories/index.html.slim b/app/views/suppliers/product_categories/index.html.slim
new file mode 100644
index 00000000..9084c8ce
--- /dev/null
+++ b/app/views/suppliers/product_categories/index.html.slim
@@ -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'
+
diff --git a/app/views/suppliers/product_categories/new.html.slim b/app/views/suppliers/product_categories/new.html.slim
new file mode 100644
index 00000000..4c3f76bd
--- /dev/null
+++ b/app/views/suppliers/product_categories/new.html.slim
@@ -0,0 +1,4 @@
+- model_class = ProductCategory
+.page-header
+ = title :new, model_class
+= render 'form'
diff --git a/app/views/suppliers/product_categories/show.html.slim b/app/views/suppliers/product_categories/show.html.slim
new file mode 100644
index 00000000..f358e6b3
--- /dev/null
+++ b/app/views/suppliers/product_categories/show.html.slim
@@ -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'
diff --git a/app/views/suppliers/products/_form.html.slim b/app/views/suppliers/products/_form.html.slim
new file mode 100644
index 00000000..c288b0ad
--- /dev/null
+++ b/app/views/suppliers/products/_form.html.slim
@@ -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'
diff --git a/app/views/suppliers/products/edit.html.slim b/app/views/suppliers/products/edit.html.slim
new file mode 100644
index 00000000..509b074f
--- /dev/null
+++ b/app/views/suppliers/products/edit.html.slim
@@ -0,0 +1,4 @@
+- model_class = Product
+.page-header
+ = title :edit, model_class
+= render 'form'
diff --git a/app/views/suppliers/products/index.html.slim b/app/views/suppliers/products/index.html.slim
new file mode 100644
index 00000000..fd271e93
--- /dev/null
+++ b/app/views/suppliers/products/index.html.slim
@@ -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'
+
diff --git a/app/views/suppliers/products/new.html.slim b/app/views/suppliers/products/new.html.slim
new file mode 100644
index 00000000..80f6e521
--- /dev/null
+++ b/app/views/suppliers/products/new.html.slim
@@ -0,0 +1,4 @@
+- model_class = Product
+.page-header
+ = title :new, model_class
+= render 'form'
diff --git a/app/views/suppliers/products/show.html.slim b/app/views/suppliers/products/show.html.slim
new file mode 100644
index 00000000..88c2ecb5
--- /dev/null
+++ b/app/views/suppliers/products/show.html.slim
@@ -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'
diff --git a/app/views/suppliers/sections/show.html.slim b/app/views/suppliers/sections/show.html.slim
index 300033ab..bc7ee9e8 100644
--- a/app/views/suppliers/sections/show.html.slim
+++ b/app/views/suppliers/sections/show.html.slim
@@ -16,7 +16,8 @@
- 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}
.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
h3= t('table.has_no_section')
.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}
.pull-right.action-button-container
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
- content_for :footer do
javascript:
diff --git a/app/views/user/home.html.slim b/app/views/user/home.html.slim
index 4d39a24c..1eb03e76 100644
--- a/app/views/user/home.html.slim
+++ b/app/views/user/home.html.slim
@@ -1,10 +1,7 @@
ul.nav.nav-tabs.nav-stacked
+ li
+ button.btn.btn-primary onClick="QMobile.scanQr()" Scan Qr
- if list_open?
- li= link_to 'Place order', user_list_products_path(supplier_id: active_list.supplier.id)
- li= link_to '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 'Place order', user_list_products_path
+ li= link_to 'Show active list', user_active_list_path
li= link_to 'Subscribe to list', '#'
- li= link_to 'Check out menu', '#'
diff --git a/app/views/user/list_history.html.slim b/app/views/user/list_history.html.slim
index 4fdba65e..6631f128 100644
--- a/app/views/user/list_history.html.slim
+++ b/app/views/user/list_history.html.slim
@@ -1,2 +1,4 @@
.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)
diff --git a/app/views/user/list_products.html.slim b/app/views/user/list_products.html.slim
index 62297460..811ca9bf 100644
--- a/app/views/user/list_products.html.slim
+++ b/app/views/user/list_products.html.slim
@@ -4,7 +4,6 @@
span#list-needs-help-button
table#products-table.table.table-striped.table-hover
tbody
--# content_for :sidebar do
table#active-order-table.table.table-striped.hide
thead
tr
diff --git a/app/views/user/list_products_for_table.html.slim b/app/views/user/list_products_for_table.html.slim
new file mode 100644
index 00000000..afb924b8
--- /dev/null
+++ b/app/views/user/list_products_for_table.html.slim
@@ -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
+ |
+ 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'});
+ })
+
diff --git a/config/application.rb b/config/application.rb
index 8be5c44a..7e0259a1 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -77,3 +77,4 @@ module Qrammer
config.assets.version = '1.0'
end
end
+require 'rqrcode-rails3'
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 011eb157..37a8b8eb 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -38,6 +38,7 @@ Qrammer::Application.configure do
# Do not compress assets
config.assets.compress = false
+ config.assets.logger = Logger.new('/dev/null')
# Expands the lines which load the assets
config.assets.debug = true
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 471cf6bf..9808954c 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -191,7 +191,7 @@ Devise.setup do |config|
# Configure sign_out behavior.
# 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.
- # config.sign_out_all_scopes = true
+ config.sign_out_all_scopes = false
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
@@ -205,7 +205,7 @@ Devise.setup do |config|
# config.navigational_formats = ["*/*", :html]
# 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
# Add a new OmniAuth provider. Check the wiki for more information on setting
diff --git a/config/routes.rb b/config/routes.rb
index 1fff0649..4834d7b0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,13 +1,11 @@
Qrammer::Application.routes.draw do
devise_for :users
devise_for :suppliers
- authenticate :supplier do
+ devise_for :administrators
+
+ #authenticate :administrator do
resources :users
- resources :tables do
- member do
- get :qrcode
- end
- end
+ resources :tables
resources :orders do
member do
post :is_being_processed
@@ -18,7 +16,7 @@ Qrammer::Application.routes.draw do
resources :lists
resources :products
resources :product_categories
- end
+ #end
get '/supplier' => 'supplier#home', as: :supplier_root
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
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_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/: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/move_table' => 'user#move_table', as: :user_move_table
get '/user/table_info' => 'user#table_info', as: :user_table_info
match '/show_products' => 'dashboard#show_products', as: :user_products
@@ -48,10 +48,15 @@ Qrammer::Application.routes.draw do
namespace :suppliers, path: '/supplier' do
resources :sections
resources :tables
+ resources :products
+ resources :product_categories
root to: 'sections#index'
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:
# first created -> highest priority.
diff --git a/lib/rqrcode-rails3.rb b/lib/rqrcode-rails3.rb
new file mode 100644
index 00000000..0140e8ff
--- /dev/null
+++ b/lib/rqrcode-rails3.rb
@@ -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
diff --git a/lib/rqrcode-rails3/qr_container.svg b/lib/rqrcode-rails3/qr_container.svg
new file mode 100644
index 00000000..99b5291a
--- /dev/null
+++ b/lib/rqrcode-rails3/qr_container.svg
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+ Qwaiter
+ #qrcode
+
+ #table_number
+
+
diff --git a/lib/rqrcode-rails3/renderers/svg.rb b/lib/rqrcode-rails3/renderers/svg.rb
new file mode 100644
index 00000000..33ebc731
--- /dev/null
+++ b/lib/rqrcode-rails3/renderers/svg.rb
@@ -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 = %{}
+ open_tag = %{}
+ close_tag = " "
+
+ 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 << %{ }
+ end
+ result << tmp.join("\n")
+ end
+
+ if options[:fill]
+ result.unshift %{ }
+ 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
diff --git a/lib/rqrcode-rails3/size_calculator.rb b/lib/rqrcode-rails3/size_calculator.rb
new file mode 100644
index 00000000..da8fa991
--- /dev/null
+++ b/lib/rqrcode-rails3/size_calculator.rb
@@ -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
\ No newline at end of file
diff --git a/stories b/stories
index e27558d5..ed5563b3 100644
--- a/stories
+++ b/stories
@@ -2,6 +2,18 @@ Stories:
- Person moves to different table
- Person tries to create list on occupied table
- 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:
When list is open:
@@ -42,3 +54,21 @@ Wireless wachtwoord via qr code
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
+
+