From 8213bae2c68cf30a4cad67a6469337367f350140 Mon Sep 17 00:00:00 2001 From: Benjamin ter Kuile Date: Wed, 29 Aug 2012 17:42:04 +0200 Subject: [PATCH] end of day commit --- Gemfile | 2 +- Gemfile.lock | 4 +- Qr scan.mm | 67 +++++++++++ app/assets/javascripts/bootstrap.js.coffee | 10 +- app/assets/javascripts/jquery.cookie.js | 96 +++++++++++++++ app/assets/javascripts/quser.js.coffee | 85 ++++++++------ app/assets/stylesheets/forms.css.sass | 3 + app/assets/stylesheets/structure.css.sass | 2 + .../stylesheets/tablet/structure.css.sass | 11 +- app/controllers/application_controller.rb | 8 +- app/controllers/dashboard_controller.rb | 17 ++- .../suppliers/application_controller.rb | 1 + .../product_categories_controller.rb | 87 ++++++++++++++ .../suppliers/products_controller.rb | 88 ++++++++++++++ app/controllers/tables_controller.rb | 13 --- app/controllers/user_controller.rb | 62 ++++++++-- app/helpers/application_helper.rb | 22 +++- app/models/administrator.rb | 5 + app/models/list.rb | 18 +++ app/models/order.rb | 1 + app/models/product_category.rb | 2 + app/models/section.rb | 2 + app/models/table.rb | 3 +- app/models/table_move.rb | 6 + app/models/user.rb | 11 ++ app/views/dashboard/select_qrcode.html.slim | 2 +- app/views/layouts/administrator.html.slim | 69 +++++++++++ app/views/layouts/application.html.slim | 11 +- app/views/layouts/phone.html.slim | 7 +- app/views/layouts/tablet.html.slim | 12 +- app/views/product_categories/_form.html.slim | 2 +- app/views/product_categories/index.html.slim | 14 +-- app/views/products/index.html.slim | 16 +-- .../product_categories/_form.html.slim | 14 +++ .../product_categories/edit.html.slim | 4 + .../product_categories/index.html.slim | 24 ++++ .../product_categories/new.html.slim | 4 + .../product_categories/show.html.slim | 18 +++ app/views/suppliers/products/_form.html.slim | 22 ++++ app/views/suppliers/products/edit.html.slim | 4 + app/views/suppliers/products/index.html.slim | 28 +++++ app/views/suppliers/products/new.html.slim | 4 + app/views/suppliers/products/show.html.slim | 20 ++++ app/views/suppliers/sections/show.html.slim | 6 +- app/views/user/home.html.slim | 11 +- app/views/user/list_history.html.slim | 4 +- app/views/user/list_products.html.slim | 1 - .../user/list_products_for_table.html.slim | 27 +++++ config/application.rb | 1 + config/environments/development.rb | 1 + config/initializers/devise.rb | 4 +- config/routes.rb | 21 ++-- lib/rqrcode-rails3.rb | 31 +++++ lib/rqrcode-rails3/qr_container.svg | 109 ++++++++++++++++++ lib/rqrcode-rails3/renderers/svg.rb | 50 ++++++++ lib/rqrcode-rails3/size_calculator.rb | 40 +++++++ stories | 30 +++++ 57 files changed, 1109 insertions(+), 128 deletions(-) create mode 100644 Qr scan.mm create mode 100644 app/assets/javascripts/jquery.cookie.js create mode 100644 app/assets/stylesheets/forms.css.sass create mode 100644 app/controllers/suppliers/product_categories_controller.rb create mode 100644 app/controllers/suppliers/products_controller.rb create mode 100644 app/models/administrator.rb create mode 100644 app/models/table_move.rb create mode 100644 app/views/layouts/administrator.html.slim create mode 100644 app/views/suppliers/product_categories/_form.html.slim create mode 100644 app/views/suppliers/product_categories/edit.html.slim create mode 100644 app/views/suppliers/product_categories/index.html.slim create mode 100644 app/views/suppliers/product_categories/new.html.slim create mode 100644 app/views/suppliers/product_categories/show.html.slim create mode 100644 app/views/suppliers/products/_form.html.slim create mode 100644 app/views/suppliers/products/edit.html.slim create mode 100644 app/views/suppliers/products/index.html.slim create mode 100644 app/views/suppliers/products/new.html.slim create mode 100644 app/views/suppliers/products/show.html.slim create mode 100644 app/views/user/list_products_for_table.html.slim create mode 100644 lib/rqrcode-rails3.rb create mode 100644 lib/rqrcode-rails3/qr_container.svg create mode 100644 lib/rqrcode-rails3/renderers/svg.rb create mode 100644 lib/rqrcode-rails3/size_calculator.rb 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 = $('') - 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 = $('') + 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 + +