From 17a54723ec751e2b4936e39359754d78565087c9 Mon Sep 17 00:00:00 2001 From: benji Date: Fri, 18 Sep 2015 21:05:32 +0200 Subject: [PATCH 01/22] Remove index and destroy on userscontroller --- app/controllers/users_controller.rb | 10 ---------- app/views/layouts/_header.html.haml | 6 ------ app/views/users/_user.html.haml | 8 -------- app/views/users/index.html.haml | 12 ------------ config/routes.rb | 2 +- spec/controllers/users_controller_spec.rb | 15 --------------- 6 files changed, 1 insertion(+), 52 deletions(-) delete mode 100644 app/views/users/_user.html.haml delete mode 100644 app/views/users/index.html.haml diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 11dc714..f0c276a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -18,16 +18,6 @@ class UsersController < ApplicationController end end - def index - @users = User.members - end - - def destroy - @user.destroy - flash[:success] = "Succesfully removed user" - redirect_to users_path - end - def edit_dagschotel @dagschotel = @user.dagschotel diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 04abf65..ba226dc 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -28,12 +28,6 @@ %li= link_to "List", products_path %li= link_to "Add product" , new_product_path %li= link_to "Add stock", new_stock_path - %li.dropdown - %a.dropdown-toggle{"aria-expanded" => "false", "data-toggle" => "dropdown", href: "#", role: "button"} - Users - %span.caret - %ul.dropdown-menu{role: "menu"} - %li= link_to "List" , users_path %li.dropdown %a.dropdown-toggle{"data-toggle" => "dropdown", href: "#"} Logged in as #{current_user.name} diff --git a/app/views/users/_user.html.haml b/app/views/users/_user.html.haml deleted file mode 100644 index 1e7f1cc..0000000 --- a/app/views/users/_user.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -%tr - %td= user.id - %td= image_tag user.avatar(:small) - %td= user.name - %td= euro(user.debt) - - if current_user.admin? - %td - = link_to "Delete", user_path(user), method: :delete, class: "btn btn-danger", data: { confirm: "Are you sure?" } diff --git a/app/views/users/index.html.haml b/app/views/users/index.html.haml deleted file mode 100644 index b8f4e11..0000000 --- a/app/views/users/index.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -.row.users - .col-md-8.col-md-offset-2 - %h1 All users - = render partial: 'flash' - %table#users-table.table.table-striped - %tr - %th ID - %th - %th Nickname - %th Debt - %th - = render @users diff --git a/config/routes.rb b/config/routes.rb index d56ffdd..ddeb240 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,7 +18,7 @@ Rails.application.routes.draw do end end - resources :users, only: [:show, :edit, :update, :index, :destroy] do + resources :users, only: [:show, :edit, :update] do resources :orders, only: [:new, :create, :destroy] member do get 'quickpay' => 'users#quickpay' diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 8dcd250..7a982f4 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -51,21 +51,6 @@ describe UsersController, type: :controller do end end - describe 'GET index' do - before :each do - get :index - end - - it 'should load an array of all users' do - expect(assigns(:users)).to eq([@user]) - end - - it 'should render the correct template' do - expect(response).to render_template(:index) - expect(response).to have_http_status(200) - end - end - describe 'GET edit_dagschotel' do it 'should render the page' do get :edit_dagschotel, id: @user From 7cc16748bf29e75be7e2df1d8a95792a26fdf702 Mon Sep 17 00:00:00 2001 From: benji Date: Fri, 18 Sep 2015 21:11:45 +0200 Subject: [PATCH 02/22] remove unnecessary fields from devise --- .../20150918190548_remove_devise_fields_from_users.rb | 9 +++++++++ db/schema.rb | 7 +------ 2 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20150918190548_remove_devise_fields_from_users.rb diff --git a/db/migrate/20150918190548_remove_devise_fields_from_users.rb b/db/migrate/20150918190548_remove_devise_fields_from_users.rb new file mode 100644 index 0000000..ebbfe72 --- /dev/null +++ b/db/migrate/20150918190548_remove_devise_fields_from_users.rb @@ -0,0 +1,9 @@ +class RemoveDeviseFieldsFromUsers < ActiveRecord::Migration + def change + remove_column :users, :sign_in_count, :integer + remove_column :users, :current_sign_in_at, :datetime + remove_column :users, :last_sign_in_at, :datetime + remove_column :users, :current_sign_in_ip, :string + remove_column :users, :last_sign_in_ip, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 01c418f..f8a275a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150917165758) do +ActiveRecord::Schema.define(version: 20150918190548) do create_table "delayed_jobs", force: :cascade do |t| t.integer "priority", default: 0, null: false @@ -66,11 +66,6 @@ ActiveRecord::Schema.define(version: 20150917165758) do t.datetime "created_at" t.datetime "updated_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false - t.datetime "current_sign_in_at" - t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" t.boolean "admin" t.integer "dagschotel_id" t.string "avatar_file_name" From ec2b51672714a46f287297b309a7d75ec5c05b8b Mon Sep 17 00:00:00 2001 From: benji Date: Sat, 19 Sep 2015 11:05:43 +0200 Subject: [PATCH 03/22] Refactor views --- app/views/application/_flash.html.haml | 10 +++++----- app/views/devise/sessions/new.html.haml | 6 ++---- app/views/devise/shared/_links.html.haml | 16 ---------------- app/views/layouts/_header.html.haml | 18 +++++------------- app/views/layouts/_session_button.html.haml | 5 +++++ app/views/layouts/application.html.haml | 2 ++ app/views/order_items/_order_item.html.haml | 2 +- app/views/orders/_price.html.haml | 2 +- app/views/orders/new.html.haml | 2 +- app/views/orders/overview.html.haml | 4 +--- app/views/products/edit.html.haml | 2 +- app/views/products/index.html.haml | 3 +-- app/views/products/new.html.haml | 2 +- app/views/products_list/_product_row.html.haml | 8 ++++---- app/views/products_list/listview.html.haml | 3 +-- app/views/stocks/new.html.haml | 2 +- .../{_new_order.html.haml => _user.html.haml} | 0 app/views/users/edit.html.haml | 1 - app/views/users/edit_dagschotel.html.haml | 5 ++--- app/views/users/show.html.haml | 1 - app/views/welcome/index.html.haml | 5 +---- 21 files changed, 35 insertions(+), 64 deletions(-) delete mode 100644 app/views/devise/shared/_links.html.haml create mode 100644 app/views/layouts/_session_button.html.haml rename app/views/users/{_new_order.html.haml => _user.html.haml} (100%) diff --git a/app/views/application/_flash.html.haml b/app/views/application/_flash.html.haml index bb03eaf..8f535e8 100644 --- a/app/views/application/_flash.html.haml +++ b/app/views/application/_flash.html.haml @@ -1,26 +1,26 @@ #flash - if flash[:error] .alert.alert-danger.alert-dismissable - %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} × + %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", type: "button"} × %strong Error! = flash[:error] - if flash[:success] .alert.alert-success.alert-dismissable - %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} × + %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", type: "button"} × %strong Success! = raw flash[:success] - if flash[:notice] .alert.alert-info.alert-dismissable - %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} × + %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", type: "button"} × %strong Notice! = flash[:notice] - if flash[:warning] .alert.alert-warning.alert-dismissable - %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} × + %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", type: "button"} × %strong Warning! = flash[:warning] - if flash[:alert] .alert.alert-danger.alert-dismissable - %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} × + %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", type: "button"} × %strong Error! = flash[:alert] diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 0ca4b84..23c04c6 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,10 +1,8 @@ -%h2 Sign in -= render partial: 'flash' += content_for :title, "Sign in" .sign-in - = f_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| + = f_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| = f.text_field :name = f.password_field :password - if devise_mapping.rememberable? = f.check_box :remember_me = f.submit "Sign in" -= render "devise/shared/links" diff --git a/app/views/devise/shared/_links.html.haml b/app/views/devise/shared/_links.html.haml deleted file mode 100644 index 4168feb..0000000 --- a/app/views/devise/shared/_links.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -- unless controller_name == 'sessions' - = link_to "Log in", new_session_path(resource_name) - %br/ -- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' - = link_to "Forgot your password?", new_password_path(resource_name) - %br/ -- if devise_mapping.confirmable? && controller_name != 'confirmations' - = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) - %br/ -- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' - = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) - %br/ -- if devise_mapping.omniauthable? - - resource_class.omniauth_providers.each do |provider| - = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), class: "btn btn-large btn-primary" - %br/ diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index ba226dc..5b96e95 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -2,7 +2,7 @@ .container-fluid / Brand and toggle get grouped for better mobile display .navbar-header - %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} + %button.navbar-toggle{data: { target: ".navbar-collapse", toggle: "collapse" } } %span.icon-bar %span.icon-bar %span.icon-bar @@ -11,11 +11,7 @@ - unless current_user && current_user.koelkast? .collapse.navbar-collapse .hidden-xs.navbar-form.navbar-right - .form-group - - if user_signed_in? - = link_to "Logout", destroy_user_session_path, class: "btn btn-default form-control" - - else - = link_to "Login", omniauth_authorize_path("user", "zeuswpi"), class: "btn btn-success form-control" + = render 'layouts/session_button' %ul.nav.navbar-nav.navbar-right %li= mail_to "tab@zeus.ugent.be", "Send feedback" - if user_signed_in? @@ -33,13 +29,9 @@ Logged in as #{current_user.name} %b.caret %ul.dropdown-menu - %li= link_to "Edit avatar", edit_user_path(current_user) + %li= link_to "Edit profile", edit_user_path(current_user) %li %p.navbar-text - Debt: #{euro(current_user.debt)} + Balance: #{euro_from_cents(current_user.debt)} .visible-xs.navbar-form - .form-group - - if user_signed_in? - = button_to "Logout", destroy_user_session_path, class: "btn btn-default form-control", method: :delete - - else - = link_to "Login", omniauth_authorize_path("user", "zeuswpi"), class: "btn btn-success form-control" + = render 'layouts/session_button' diff --git a/app/views/layouts/_session_button.html.haml b/app/views/layouts/_session_button.html.haml new file mode 100644 index 0000000..51211b8 --- /dev/null +++ b/app/views/layouts/_session_button.html.haml @@ -0,0 +1,5 @@ +.form-group + - if user_signed_in? + = link_to "Logout", destroy_user_session_path, class: "btn btn-default form-control" + - else + = link_to "Login", omniauth_authorize_path("user", "zeuswpi"), class: "btn btn-success form-control" diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index b1fa7ef..0f7a414 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -9,6 +9,8 @@ %body = render 'layouts/header' .container + %h2= yield :title + = render partial: 'flash' = yield = render 'layouts/footer' = debug(params) if Rails.env.development? diff --git a/app/views/order_items/_order_item.html.haml b/app/views/order_items/_order_item.html.haml index 63281a3..97c01aa 100644 --- a/app/views/order_items/_order_item.html.haml +++ b/app/views/order_items/_order_item.html.haml @@ -1,5 +1,5 @@ .col-md-3.form_products - %div{class: "thumbnail#{' out-of-stock' if product.stock.zero?}"} + %div.thumbnail{ class: ('out-of-stock' if product.stock.zero?) } .form_row.center .form_row_image = image_tag product.avatar diff --git a/app/views/orders/_price.html.haml b/app/views/orders/_price.html.haml index 51bef93..1939d7a 100644 --- a/app/views/orders/_price.html.haml +++ b/app/views/orders/_price.html.haml @@ -2,6 +2,6 @@ %strong Total price .input-group %span.input-group-addon € - = content_tag :span, "", id: "order_price", class: "input-group-addon" + %span#order_price.input-group-addon %span.input-group-btn = f.submit "Order!", class: "btn btn-primary big-form-button", skip_wrapper: true diff --git a/app/views/orders/new.html.haml b/app/views/orders/new.html.haml index 26df01a..555be8c 100644 --- a/app/views/orders/new.html.haml +++ b/app/views/orders/new.html.haml @@ -1,5 +1,5 @@ %h3 - Order for #{@user.name} (Huidige schuld: #{euro(@user.debt)}) + Order for #{@user.name} .row = f_form_for [@user, @order] do |f| = f.error_messages diff --git a/app/views/orders/overview.html.haml b/app/views/orders/overview.html.haml index 5327de7..6ce871b 100644 --- a/app/views/orders/overview.html.haml +++ b/app/views/orders/overview.html.haml @@ -1,4 +1,2 @@ -= render partial: 'flash' .row - - @users.each do |user| - = render 'users/new_order', user: user + = render @users diff --git a/app/views/products/edit.html.haml b/app/views/products/edit.html.haml index 4089201..26ece76 100644 --- a/app/views/products/edit.html.haml +++ b/app/views/products/edit.html.haml @@ -1,2 +1,2 @@ -%h1 Update product += content_for :title, "Update product" = render "form" diff --git a/app/views/products/index.html.haml b/app/views/products/index.html.haml index 936e312..2434c33 100644 --- a/app/views/products/index.html.haml +++ b/app/views/products/index.html.haml @@ -1,3 +1,2 @@ -%h1 All products -= render partial: 'flash' += content_for :title, "All products" = render 'products/index' diff --git a/app/views/products/new.html.haml b/app/views/products/new.html.haml index 82f9b11..9761184 100644 --- a/app/views/products/new.html.haml +++ b/app/views/products/new.html.haml @@ -1,2 +1,2 @@ -%h1 New product += content_for :title, "New product" = render "form" diff --git a/app/views/products_list/_product_row.html.haml b/app/views/products_list/_product_row.html.haml index f0aa11d..92d2cc6 100644 --- a/app/views/products_list/_product_row.html.haml +++ b/app/views/products_list/_product_row.html.haml @@ -1,9 +1,9 @@ -%tr{:id => "products_row_#{dom_id(product)}"} - %td= image_tag product.avatar(:small) +%tr{id: "products_row_#{dom_id(product)}"} + %td= link_to image_tag(product.avatar(:small)), edit_product_path(product) %td= product.name %td= euro(product.price) %td= product.stock %td - %span{:class => "glyphicon #{product.deleted ? "glyphicon-check" : "glyphicon-unchecked"}"} + %span{class: "glyphicon #{product.deleted ? "glyphicon-check" : "glyphicon-unchecked"}"} %td= product.calories - %td= button_to "Edit", edit_product_path(product), method: :get, class: "btn btn-default", remote: true + %td= link_to "Edit", edit_product_path(product), class: "btn btn-default", remote: true diff --git a/app/views/products_list/listview.html.haml b/app/views/products_list/listview.html.haml index a98332c..e165162 100644 --- a/app/views/products_list/listview.html.haml +++ b/app/views/products_list/listview.html.haml @@ -1,8 +1,7 @@ += content_for :title, "Products" #products-errors .row.products .col-md-8.col-md-offset-2 - %h1 Products - = render partial: 'flash' = link_to "Add Stock", new_stock_path, class: "btn btn-default" %table#products-table.table.table-striped %tr diff --git a/app/views/stocks/new.html.haml b/app/views/stocks/new.html.haml index 64b1229..35ea34a 100644 --- a/app/views/stocks/new.html.haml +++ b/app/views/stocks/new.html.haml @@ -1,6 +1,6 @@ += content_for :title, "Add stock" .row .col-md-6.col-md-offset-3 - %h2 Add stock = f_form_for @stock do |f| = render 'stocks/errors' = f.fields_for :stock_entries do |se_field| diff --git a/app/views/users/_new_order.html.haml b/app/views/users/_user.html.haml similarity index 100% rename from app/views/users/_new_order.html.haml rename to app/views/users/_user.html.haml diff --git a/app/views/users/edit.html.haml b/app/views/users/edit.html.haml index e132c46..e0884e3 100644 --- a/app/views/users/edit.html.haml +++ b/app/views/users/edit.html.haml @@ -1,4 +1,3 @@ -= render 'flash' .row = render 'sidebar' .col-sm-9 diff --git a/app/views/users/edit_dagschotel.html.haml b/app/views/users/edit_dagschotel.html.haml index 154245a..e25e53b 100644 --- a/app/views/users/edit_dagschotel.html.haml +++ b/app/views/users/edit_dagschotel.html.haml @@ -1,3 +1,2 @@ -%h3 - Choose new Dagschotel - = render 'products/index' += content_for :title, "Choose new Dagschotel" += render 'products/index' diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index c2b56f5..5469c20 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,4 +1,3 @@ -= render partial: 'flash' .row = render 'sidebar' #user_info.col-sm-9 diff --git a/app/views/welcome/index.html.haml b/app/views/welcome/index.html.haml index b317596..8537b15 100644 --- a/app/views/welcome/index.html.haml +++ b/app/views/welcome/index.html.haml @@ -1,7 +1,4 @@ -%h2 Login -= render 'flash' += content_for :title, "Login" If this is the first time you log in, an account will be created for you. %div - %br/ = link_to "Sign in with Zeus WPI account.", omniauth_authorize_path("user", "zeuswpi"), class: "btn btn-large btn-primary" - %br/ From 64038358723a99471303cdd3964ed68a6aaf59a6 Mon Sep 17 00:00:00 2001 From: benji Date: Sun, 20 Sep 2015 21:21:18 +0200 Subject: [PATCH 04/22] Use barcode to create new order --- app/assets/images/logo.png | Bin 0 -> 3354 bytes app/assets/javascripts/orders.js | 110 +++++++++--------- app/assets/stylesheets/orders.css.scss | 110 ++++++++++-------- app/controllers/orders_controller.rb | 7 +- app/controllers/products_controller.rb | 6 +- app/models/ability.rb | 2 + app/models/product.rb | 1 + app/models/user.rb | 16 ++- app/views/layouts/_header.html.haml | 2 +- app/views/orders/_price.html.haml | 7 -- app/views/orders/_products_modal.html.haml | 19 +++ app/views/orders/new.html.haml | 52 +++++++-- config/routes.rb | 6 +- .../20150919091214_add_barcode_to_products.rb | 5 + db/schema.rb | 3 +- spec/factories/products.rb | 1 + 16 files changed, 222 insertions(+), 125 deletions(-) create mode 100644 app/assets/images/logo.png delete mode 100644 app/views/orders/_price.html.haml create mode 100644 app/views/orders/_products_modal.html.haml create mode 100644 db/migrate/20150919091214_add_barcode_to_products.rb diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..102f3a311d9f26941c9a984622d9d875656747ed GIT binary patch literal 3354 zcmV+#4dwEQP)V5lH$2X+Tg0q??uJ^R*Pc{Cbnwd=V*IJ`V8?@CwmsTmC) zA0Mx-t_G(8yy<4M85r`$nN5MgNNF$_DGf$SgTY8?Fj5)}MoNQ`(qJ%B8Vp8CgOSo; zFj5+flm>&5QjYWUb6avFWkwi}$AA9(u_ZTBW+Wq5tJP>UvL!cC<^nMC`}gm*-krek z<>lr1`8lg+y1BX0eI#h@$z)=S-5FB8y}f1iK(pEG{{CKZz@I;VE*6XGzr4P_g7lA% zkNBN_R(}C5E-uI=!Y{By@$>TX(zsF-#df>J)NMaN|A_yDT-%?z^7rv4zJC46{ie5^ zpzerVUtgDlLxV#GV|HXiU0q$x=W{H^+bIF>i?`OQ;sQ;vA`m(d0ibfzmu5st>^a?# z4Wk~1mlR3zQk?NZ=P2Dgyxh5;Uafupff>3#S zdg_561OE>@Ifwj0`=o@s-RVRNqzl$^xmQ>b!LoI=jCp<>(B$^ zY124`xswtrms`Sp@DX|p4Hz(v=y+hV}Sa>u+rB~+Mh3JV_GWHJ#!%KJXYNC2XqJGbWjsS|%Pn z>KLPvQrF$4aCiA`GIC^?ZO??1x<)@%r)FGX`K~^}GV5c+$<NT7aWDM+cVA`d3DjFjrtjIm3+9n{=EhoBh}is5e8 zI<6SvMR{6j-U%-roSDsLQEW=}+HLHirlV#34q~@e_pRQc#5XpgFDcdCZAUxFwscLu z0lyfUw5_?F&*x=%lMX3kWVao$1M3nUkC%V{{;i$aNe0L@N)aiLQa!s3MvBk!oHDdx z`*(MDbet@4S{2X#k4T+xx6S+JKxS&Y$AkjJiX>y_zmw5f9VW-La=uq)DzwtOD6$oewNX2|m?5U7AM(y^>7pqj6gOxy7mPfMboPms#UaBK71`?Z;k`2>hcAIW*5WlU= zimF~>5{Ztb7b$*7FS9amQSjJNT|mj<+Ssw#ctT5TNT_=}2d|+ZQi>Jpd_GU(andx7 z$74Kdj9Y>v(bHDkZCURW#eH)0RtIrUoEj-ZOmH_Z>Cl%>T=4%hLuK_Uj1=Ubx!!jU z?zXzug^dB(Aeen@C(QLxrAed=OfDR}NQzqUC}vQa$U#?wB@PL_$G_9N+f2oj#;5=_ zvB20Q4$VdF6*0w_FTP7rE~kniUKU=(Nb7OF9i|}`4u9-cao{3dHz@YK-i2#d-0d{^ zcd80iVL(aBta{wbNXeNiEhqRrU76V3&L~0~f3eGL(@Rpu05N2c%-wdBH?$LXNJYvR zS=qjllAMh}x7_WN>3Bs_CJ>g8F|wVR4=Ky&>>!Oo6gzR<Y zRu2-m+bPeC<#Q;#M#>mB_13gdr%72=q=U}6+nlo+IaHSID~A|0tkg3tWJ9)2%6-#r z5Op`lOEe#i+_tF7DP?!itJCEr-P#tYCE6t=__iM*9>jCE1J7HioF7Yh`4^{k6FEI9 zJ725CQd^|N=4`U-hUG!j>2#{=-(a*HTHjKJI$j?@3LS&}UIjq2*$hu5OG46B@%v35 zF{Xf*R9^G=0jjo16DYupH8qy^1>mifc$0tzNL()BaRW+wxty0Fv=;X zs%y(pBTUXgq<2aA^z?K(E#~Qn1NEP!dOVV*`=LbYNL&6wKY_;mEk#ijB&D{Ws0tO@ z1Nfz_1`WI2t}I-vRxLJ(RqEke{z6@Sz0}AML-+NT<0evWHXD=oIpYi(;dB^j=msgR z6SyHae6cAwHIR*18n5F|_}<}Qq*MlcJ5GEKwhd>gmdmC4$OUtp^?I$MxWKToze;uR z)d!laHGB#lHy+jPlpKB8rUx*%936fLjG47R8rEUfn`?z&YQ>NsqF2BeA9n<9IDr~j zlaG)N4t*dc^DG%BanBiwxk$S2NTE=1POGVE#Yh>8luWS%0i$*9J!LIax$y!;@4-Ep zfRty0Qw3*ov93q&#AK4`=+%9?Iq0G&WS1`1te4mq)*cQC87Tv~!X(pCesIQNnuc^p zN+tXZUf$l`nk$k>eKXh0-G7T;=Wz5CA5)*UGHKP?b5q|DF&d3HCYM`)@pZ2IFPZo9 zwsl0Iytl#BcNQ8PYS*zId{RO%!_EsFr!7*73xH%e>5L9kDyLHb7}In@$ICmxA2-FN znFgge=>nYqLYIf$>GY&;gb0>QK3SiB+LU}tuF0(Q^&}i-CI2x5bUb(j6A$lOU`Z(e zi&I7?Wylo!b`x&u8_g725zru?Xqsml6a>gE zPR;ASFoR3pL}*94`JhnbYz$v9=~c+a_pH{~VFMat1=#3y}%;u#|)R|wT?{3bF{G|@3;?Ssrly1vew zZ^ZAl)i8pCmynUu>6Bm8fyqfz zIA)~OkpxWyVxIPhq$}xSBrvZ=P0$g_9skR#Mkn2^tZxR5E>n(68)QUp>a8VSXlm#W z$UNxsc~TUPnRGmA7Y4eebRR`BneC98A?LDZ|C&VzE%+ zC1hlRBgRHbWlnIUTGRqr2QNc{IM|txk*!?vV5AJ?5a*A5g^iYQdU$vMu>^rqywvma zb6dAT8YvYdrS6!zE)Iyb-EQyi@0rvbkH=8io6V-hu7AT*?7Y@x42MGwtq)#`x?Zp8 z$XqR8Nf-1AZY?)b8bV3g6`1-q7%2?~Bc;JeX)qWm4Ms|X!ANN^QW^|KN`t{jX)sb6 k3`R parseInt(cell.max)) { - cell.value = parseInt(cell.max); - } else { - cell.value = 0; + + $("#products_modal button").click(function() { + increment_product($(this).data("product")) + }) + + $("#from_barcode_form").on("ajax:before", function(xhr, settings) { + // Stuff you wanna do after sending form + }).on("ajax:success", function(data, status, xhr) { + if (status != null) { + increment_product(status["id"]) } + }).on("ajax:error", function(xhr, status, error) { + // Display an error or something, whatever + }).on("ajax:complete", function(xhr, status) { + $("#from_barcode_form")[0].reset(); + // Do more stuff + }) + + $('tr.order_item_wrapper').hide(); + $('tr.order_item_wrapper').filter(function() { + return parseInt($(this).find('[type=number]').val()) > 0; + }).show(); + + $('tr.order_item_wrapper input[type=number]').change(function() { + tr = $(this).closest('tr.order_item_wrapper') + $(tr).toggle(parseInt($(this).val()) > 0); + $(tr).find("td").last().html(((parseInt($(tr).data("price")) * parseInt($(this).val())) / 100.0).toFixed(2)) + recalculate(); + }) + + recalculate = function() { + /* Total Price */ + array = $('tr.order_item_wrapper').map(function() { + return parseInt($(this).data("price")) * parseInt($(this).find("input[type=number]").val()); + }) + sum = 0; + array.each(function(i, el) { sum += el; }); + $("#current_order .total_price").html((sum / 100.0).toFixed(2)); + + /* Message when no product has been choosen */ + $("#current_order #empty").toggle(!($('tr.order_item_wrapper input[type=number]').filter(function() { + return parseInt($(this).val()) > 0; + }).length)); } - disIfNec(row) - if (useRecalculate) { - recalculate() - } -}; -disIfNec = function(row) { - counter = parseInt($(row).find('.row_counter').val()) - $(row).find('.btn-dec').prop('disabled', counter === 0) - $(row).find('.btn-inc').prop('disabled', counter === parseInt($(row).find('.row_counter').attr('max'))) -}; - -recalculate = function() { - sum = 0 - $('.row_counter').each(function(i, value) { sum += parseInt($(value).val()) * parseInt($(value).data('price')) }) - return $('#order_price').html((sum / 100.0).toFixed(2)) -}; - -increment = function(button, n) { - row = $(button).closest('.form_row') - - // Fix the counter - counter = $(row).find('.row_counter') - value = parseInt(counter.val()) - if (isNaN(value)) { - value = 0 - } - counter.val(value + n) - - updateInput(row[0]) + recalculate(); } $(document).ready(ready); diff --git a/app/assets/stylesheets/orders.css.scss b/app/assets/stylesheets/orders.css.scss index 2583efa..b7f19f6 100644 --- a/app/assets/stylesheets/orders.css.scss +++ b/app/assets/stylesheets/orders.css.scss @@ -2,60 +2,78 @@ // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ -.big-form-button { - height: 65px; - width: 65px; -} - -.form-control.big-form-field { - height: 65px; - text-align: center; -} - -.form_row_image { - height: 100px; - width: 100px; - margin-left: auto; - margin-right: auto; - position: relative; - img { - position: absolute; - top: 0; - bottom: 0; - left: 0; - top: 0; - margin: auto; +.barcode-wrapper { + font-size: 300%; + input { + width: 100%; } } -.form_row input::-webkit-outer-spin-button, -.form_row input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; /* <-- Apparently some margin are still there even though it's hidden */ -} - -.form_row .btn-lg { - padding: 10px 10px; -} - -.form_row .caption { - h4 { - position: relative; - span { +#products_modal { + .modal-header { + h4 { + display: inline-block; + } + input { + margin-right: 20px; + } + } + .col-md-2 { + margin-bottom: 20px; + } + button.product { + width: 100%; + p { overflow: hidden; text-overflow: ellipsis; - max-width: 100%; - display: inline-block; - padding-right: 50px; } - small { - margin-left: -45px; - position: absolute; - top: 5px; + img { + max-width: 100%; + margin-left: auto; + margin-right: auto; + display: block; } } } -#order_price { - width: 50px; + +#current_order { + text-align: left; + border: 1px solid; + padding: 10px; + div.center { + margin-bottom: 20px; + } + .order_item_wrapper { + input { + border: none; + width: auto; + } + } + table { + border-top: 1px dashed; + border-collapse: separate; + margin-bottom: 15px; + td:first-child { + width: 40px; + input { + text-align: right; + width: 40px; + } + } + tr:last-child td { + border-top: 1px dashed; + padding-top: 10px; + margin-top: 10px; + } + tr.margin { + height: 10px; + } + td.euro { + text-align: right; + &::before { + content: "€"; + } + } + } } diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index e9df136..8511919 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -3,10 +3,8 @@ class OrdersController < ApplicationController load_and_authorize_resource :order, through: :user, shallow: true def new - products = (@user.products.for_sale.select("products.*", "sum(order_items.count) as count").group(:product_id).order("count desc") | Product.for_sale) - products.each do |p| - @order.order_items.build(product: p) - end + @products = Product.all.for_sale.order(:name) + @order.products << @products end def create @@ -14,6 +12,7 @@ class OrdersController < ApplicationController flash[:success] = "#{@order.to_sentence} ordered. Enjoy it!" redirect_to root_path else + @products = Product.all.for_sale.order(:name) render 'new' end end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index ab472b4..956d5cf 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -31,9 +31,13 @@ class ProductsController < ApplicationController respond_with @product end + def from_barcode + render json: Product.find_by_barcode(params.require(:barcode)) + end + private def product_params - params.require(:product).permit(:name, :price, :avatar, :category, :stock, :calories, :deleted) + params.require(:product).permit(:name, :price, :avatar, :category, :stock, :calories, :deleted, :barcode) end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 28dac54..513a6af 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -4,6 +4,8 @@ class Ability def initialize(user) return unless user + can :from_barcode, Product + if user.admin? can :manage, :all elsif user.koelkast? diff --git a/app/models/product.rb b/app/models/product.rb index 4e8dd22..93abee4 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -28,6 +28,7 @@ class Product < ActiveRecord::Base validates :price_cents, presence: true, numericality: { only_integer: true, greater_than: 0 } validates :stock, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :calories, numericality: { only_integer: true, allow_nil: true, greater_than_or_equal_to: 0 } + validates :barcode, presence: true, uniqueness: true scope :for_sale, -> { where deleted: false } diff --git a/app/models/user.rb b/app/models/user.rb index b98deb9..6459040 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -44,7 +44,19 @@ class User < ActiveRecord::Base end end - def debt - 42.15 + def balance + @balance || begin + headers = { + "Authorization" => "Token token=#{Rails.application.secrets.tab_api_key}", + "Content-type" => "application/json" + } + result = HTTParty.get(File.join(Rails.application.config.api_url, "users", "#{name}.json"), headers: headers) + + if result.code == 200 + JSON.parse(result.body)["balance"] + else + 0 + end + end end end diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 5b96e95..534e840 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -32,6 +32,6 @@ %li= link_to "Edit profile", edit_user_path(current_user) %li %p.navbar-text - Balance: #{euro_from_cents(current_user.debt)} + Balance: #{euro_from_cents(current_user.balance)} .visible-xs.navbar-form = render 'layouts/session_button' diff --git a/app/views/orders/_price.html.haml b/app/views/orders/_price.html.haml deleted file mode 100644 index 1939d7a..0000000 --- a/app/views/orders/_price.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.col-md-3.form_total - %strong Total price - .input-group - %span.input-group-addon € - %span#order_price.input-group-addon - %span.input-group-btn - = f.submit "Order!", class: "btn btn-primary big-form-button", skip_wrapper: true diff --git a/app/views/orders/_products_modal.html.haml b/app/views/orders/_products_modal.html.haml new file mode 100644 index 0000000..0b493c2 --- /dev/null +++ b/app/views/orders/_products_modal.html.haml @@ -0,0 +1,19 @@ +#products_modal.modal{ tabindex: -1 } + .modal-dialog.modal-lg + .modal-content + .modal-header + %button.close{ data: { dismiss: :modal } } + %span × + %h4.modal-title Kies een product + .col-xs-3.pull-right + %input#product_search.form-control{ placeholder: "Search" } + .modal-body + .container-fluid + - @products.each do |product| + .col-md-2{ data: { name: product.name } } + %button.btn.btn-default.product{ data: { product: product.id, dismiss: :modal } } + %p= product.name + = image_tag product.avatar(:dagschotel), class: "center" + .modal-footer + %button.btn.btn-default{ data: { dismiss: :modal } } + Close diff --git a/app/views/orders/new.html.haml b/app/views/orders/new.html.haml index 555be8c..a7c076b 100644 --- a/app/views/orders/new.html.haml +++ b/app/views/orders/new.html.haml @@ -1,9 +1,43 @@ -%h3 - Order for #{@user.name} -.row - = f_form_for [@user, @order] do |f| - = f.error_messages - .col-md-12 - = f.fields_for :order_items do |op_field| - = render op_field.object, f: op_field, product: op_field.object.product - = render 'orders/price', f: f +.center + .row + .col-md-6.col-md-offset-1.barcode-wrapper + %h1 Order for #{@user.name} + = form_tag from_barcode_products_path, id: "from_barcode_form", remote: true do + %input.center-block{ type: :number, name: :barcode, autofocus: true } + = "- OR -" + %button.btn.btn-default.center-block{ data: { toggle: :modal, target: "#products_modal" } } + Select Product Without Barcode + .col-md-4.col-md-offset-1 + -# Huidige schuld: #{euro_from_cents @user.balance} + #current_order + .div.center + = image_tag "logo.png" + = form_for [@user, @order] do |f| + %table + %tr.margin + = f.fields_for :order_items do |ff| + %tr.order_item_wrapper{ data: { product: ff.object.product.id, price: ff.object.product.price_cents } } + %td + = ff.number_field :count + = ff.fields_for :product do |fff| + / Needed for haml + %td + x + %td + %span= ff.object.product.name + %td.euro + = euro_from_cents(ff.object.product.price_cents * ff.object.count) + %tr#empty + %td + %td + %em Empty Order. + %tr.margin + %tr + %td + %td + %td.text-right + Total: + %td.total_price.euro + = f.submit "Order!", class: "btn btn-primary form-control" += render 'products_modal' +- p @order.errors diff --git a/config/routes.rb b/config/routes.rb index ddeb240..7cf5e89 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,7 +27,11 @@ Rails.application.routes.draw do end end - resources :products, only: [:new, :create, :index, :edit, :update] + resources :products, only: [:new, :create, :index, :edit, :update] do + collection do + post 'barcode' => 'products#from_barcode', as: :from_barcode + end + end resources :stocks, only: [:new, :create] get 'overview' => 'orders#overview', as: "orders" diff --git a/db/migrate/20150919091214_add_barcode_to_products.rb b/db/migrate/20150919091214_add_barcode_to_products.rb new file mode 100644 index 0000000..6952895 --- /dev/null +++ b/db/migrate/20150919091214_add_barcode_to_products.rb @@ -0,0 +1,5 @@ +class AddBarcodeToProducts < ActiveRecord::Migration + def change + add_column :products, :barcode, :string, null: false, default: "" + end +end diff --git a/db/schema.rb b/db/schema.rb index f8a275a..6c24368 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150918190548) do +ActiveRecord::Schema.define(version: 20150919091214) do create_table "delayed_jobs", force: :cascade do |t| t.integer "priority", default: 0, null: false @@ -60,6 +60,7 @@ ActiveRecord::Schema.define(version: 20150918190548) do t.integer "stock", default: 0, null: false t.integer "calories" t.boolean "deleted", default: false + t.string "barcode", default: "", null: false end create_table "users", force: :cascade do |t| diff --git a/spec/factories/products.rb b/spec/factories/products.rb index 16dfb06..5748ee6 100644 --- a/spec/factories/products.rb +++ b/spec/factories/products.rb @@ -28,6 +28,7 @@ FactoryGirl.define do stock { 30 + rand(30) } calories { rand 20 } avatar { Identicon.data_url_for name } + sequence :barcode factory :invalid_product do name nil From a28c89469ef2d6c5cf3e5797e339a007e37bd668 Mon Sep 17 00:00:00 2001 From: benji Date: Mon, 21 Sep 2015 08:23:43 +0200 Subject: [PATCH 05/22] Edit abiltiy test --- app/models/ability.rb | 4 +++- spec/models/ability_spec.rb | 23 +++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 513a6af..416474c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -9,7 +9,9 @@ class Ability if user.admin? can :manage, :all elsif user.koelkast? - can :manage, Order + can :manage, Order do |order| + !order.try(:user).try(:private) + end can :quickpay, User else can :read, :all diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 67473a6..c480786 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -5,37 +5,44 @@ describe User do subject(:ability){ Ability.new(user) } let(:user) { nil} + # Admin describe 'as admin' do let(:user) { create :admin } - it{ should be_able_to(:manage, Product.new) } it{ should be_able_to(:manage, Order.new) } + it{ should be_able_to(:manage, OrderItem.new) } + it{ should be_able_to(:manage, Product.new) } it{ should be_able_to(:manage, Stock.new) } it{ should be_able_to(:manage, User.new) } end + # Normal User describe 'as normal user' do let(:user) { create :user } - it{ should be_able_to(:read, Product.new) } - it{ should_not be_able_to(:manage, Product.new) } - it{ should be_able_to(:create, Order.new(user: user)) } it{ should be_able_to(:delete, Order.new(user: user, created_at: (Rails.application.config.call_api_after - 1.minutes).ago)) } it{ should_not be_able_to(:delete, Order.new(user: user, created_at: 10.minutes.ago)) } - it{ should_not be_able_to(:manage, Order.new) } + it{ should_not be_able_to(:create, Order.new) } + it{ should_not be_able_to(:update, Order.new) } - it{ should_not be_able_to(:manage, Stock.new) } + it{ should be_able_to(:read, Product.new) } + it{ should_not be_able_to(:delete, Product.new) } + it{ should_not be_able_to(:update, Product.new) } + + it{ should_not be_able_to(:create, Stock.new) } it{ should be_able_to(:manage, user) } - it{ should_not be_able_to(:manage, User.new) } + it{ should_not be_able_to(:create, User.new) } + it{ should_not be_able_to(:update, User.new) } end describe 'as koelkast' do let(:user) { create :koelkast } it{ should_not be_able_to(:manage, Product.new) } - it{ should be_able_to(:manage, Order.new) } + it{ should be_able_to(:manage, Order.new, user: create(:user)) } + it{ should_not be_able_to(:create, build(:order, user: create(:user, private: true))) } it{ should_not be_able_to(:manage, Stock.new) } it{ should_not be_able_to(:manage, User.new) } end From e9d89c1e953d7ef4142762161f56e43f00b70c2e Mon Sep 17 00:00:00 2001 From: benji Date: Mon, 21 Sep 2015 08:40:15 +0200 Subject: [PATCH 06/22] Edit product test --- app/models/product.rb | 6 +-- spec/models/product_spec.rb | 81 +++++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/app/models/product.rb b/app/models/product.rb index 93abee4..e62bba3 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -24,7 +24,7 @@ class Product < ActiveRecord::Base enum category: %w(food beverages other) - validates :name, presence: true + validates :name, presence: true, uniqueness: true validates :price_cents, presence: true, numericality: { only_integer: true, greater_than: 0 } validates :stock, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :calories, numericality: { only_integer: true, allow_nil: true, greater_than_or_equal_to: 0 } @@ -40,8 +40,4 @@ class Product < ActiveRecord::Base if value.is_a? String then value.sub!(',', '.') end self.price_cents = (value.to_f * 100).to_int end - - def take_out_of_sale! - update_attribute :deleted, true - end end diff --git a/spec/models/product_spec.rb b/spec/models/product_spec.rb index 36e3bee..476a4fc 100644 --- a/spec/models/product_spec.rb +++ b/spec/models/product_spec.rb @@ -26,23 +26,38 @@ describe Product do expect(@product).to be_valid end - describe 'validations' do - it 'name should be present' do - @product.name = '' - expect(@product).to_not be_valid - end + ############ + # FIELDS # + ############ - describe 'price' do - it 'should be positive' do - @product.price = -5 + describe 'fields' do + describe 'name' do + it 'should be present' do + @product.name = nil expect(@product).to_not be_valid end - it 'should be saved correctly' do - @product.price = 1.20 - @product.save - expect(@product.reload.price).to eq(1.20) - expect(@product.reload.price_cents).to eq(120) + it 'shold be unique' do + expect(build :product, name: @product.name).to_not be_valid + end + end + + describe 'price_cents' do + it 'should be present' do + @product.price_cents = nil + expect(@product).to_not be_valid + end + + it 'should be a number' do + @product.price_cents = "123abc" + expect(@product).to_not be_valid + end + + it 'should be strict positive' do + @product.price = -5 + expect(@product).to_not be_valid + @product.price = 0 + expect(@product).to_not be_valid end end @@ -55,11 +70,13 @@ describe Product do it 'should be positive' do @product.stock = -5 expect(@product).to_not be_valid + @product.stock = 0 + expect(@product).to be_valid end end describe 'calories' do - it 'should not be present' do + it 'does not have to be present' do @product.calories = nil expect(@product).to be_valid end @@ -70,9 +87,39 @@ describe Product do end end - it 'avatar should be present' do - @product.avatar = nil - expect(@product).to_not be_valid + describe 'barcode' do + it 'should be present' do + @product.barcode = nil + expect(@product).to_not be_valid + end + + it 'should be unique' do + expect(build :product, barcode: @product.barcode).to_not be_valid + end + end + + describe 'avatar' do + it 'should be present' do + @product.avatar = nil + expect(@product).to_not be_valid + end end end + + ############# + # METHODS # + ############# + + describe 'price' do + it 'should read the correct value' do + expect(@product.price).to eq(@product.price_cents / 100.0) + end + + it 'should write the correct value' do + @product.price = 1.5 + @product.save + expect(@product.reload.price_cents).to eq(150) + end + end + end From 2287cbeddd640601ffa5a2e740c648ca5c66982f Mon Sep 17 00:00:00 2001 From: benji Date: Mon, 21 Sep 2015 08:50:21 +0200 Subject: [PATCH 07/22] Edit order_item test --- spec/models/order_item_spec.rb | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/spec/models/order_item_spec.rb b/spec/models/order_item_spec.rb index 33e0931..a2be719 100644 --- a/spec/models/order_item_spec.rb +++ b/spec/models/order_item_spec.rb @@ -13,15 +13,20 @@ describe OrderItem do order_item = create :order_item expect(order_item).to be_valid end + ############ + # FIELDS # + ############ - describe 'validations' do + describe 'fields' do before :each do @order_item = create :order_item end - it 'product should be present' do - @order_item.product = nil - expect(@order_item).to_not be_valid + describe 'product' do + it 'should be present' do + @order_item.product = nil + expect(@order_item).to_not be_valid + end end describe 'count' do @@ -34,10 +39,21 @@ describe OrderItem do @order_item.count = -5 expect(@order_item).to_not be_valid end + + it 'should be less or equal to product stock' do + @order_item.count = @order_item.product.stock + 1 + expect(@order_item).to_not be_valid + @order_item.count = @order_item.product.stock + expect(@order_item).to be_valid + end end end - describe 'product stock' do + ############### + # CALLBACKS # + ############### + + describe 'stock change' do before :each do @product = create :product @count = rand 10 @@ -48,7 +64,7 @@ describe OrderItem do expect{ @order_item.save }.to change{ @product.stock }.by(-@count) end - it 'should increment on cancel' do + it 'should increment on destroy' do @order_item.save expect{ @order_item.destroy }.to change{ @product.stock }.by(@count) end From 4f24651b84856061407c1944ec1f08cb9aa682f5 Mon Sep 17 00:00:00 2001 From: benji Date: Mon, 21 Sep 2015 08:54:50 +0200 Subject: [PATCH 08/22] Annotate --- app/models/product.rb | 1 + app/models/user.rb | 5 ----- spec/factories/products.rb | 1 + spec/factories/users.rb | 5 ----- spec/models/product_spec.rb | 1 + spec/models/user_spec.rb | 5 ----- 6 files changed, 3 insertions(+), 15 deletions(-) diff --git a/app/models/product.rb b/app/models/product.rb index e62bba3..1c81f38 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -15,6 +15,7 @@ # stock :integer default("0"), not null # calories :integer # deleted :boolean default("f") +# barcode :string default(""), not null # class Product < ActiveRecord::Base diff --git a/app/models/user.rb b/app/models/user.rb index 6459040..609c531 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,11 +6,6 @@ # created_at :datetime # updated_at :datetime # remember_created_at :datetime -# sign_in_count :integer default("0"), not null -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string -# last_sign_in_ip :string # admin :boolean # dagschotel_id :integer # avatar_file_name :string diff --git a/spec/factories/products.rb b/spec/factories/products.rb index 5748ee6..5d84eeb 100644 --- a/spec/factories/products.rb +++ b/spec/factories/products.rb @@ -15,6 +15,7 @@ # stock :integer default("0"), not null # calories :integer # deleted :boolean default("f") +# barcode :string default(""), not null # require 'faker' diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 3859e70..0225022 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -6,11 +6,6 @@ # created_at :datetime # updated_at :datetime # remember_created_at :datetime -# sign_in_count :integer default("0"), not null -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string -# last_sign_in_ip :string # admin :boolean # dagschotel_id :integer # avatar_file_name :string diff --git a/spec/models/product_spec.rb b/spec/models/product_spec.rb index 476a4fc..24f7acc 100644 --- a/spec/models/product_spec.rb +++ b/spec/models/product_spec.rb @@ -15,6 +15,7 @@ # stock :integer default("0"), not null # calories :integer # deleted :boolean default("f") +# barcode :string default(""), not null # describe Product do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d68a59a..2c28ed4 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -6,11 +6,6 @@ # created_at :datetime # updated_at :datetime # remember_created_at :datetime -# sign_in_count :integer default("0"), not null -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string -# last_sign_in_ip :string # admin :boolean # dagschotel_id :integer # avatar_file_name :string From b608d2b5c1622e2454ce1a02d6fec4cad19d09b8 Mon Sep 17 00:00:00 2001 From: benji Date: Mon, 21 Sep 2015 15:54:56 +0200 Subject: [PATCH 09/22] user test --- spec/models/user_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2c28ed4..9ed782f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -27,4 +27,23 @@ describe User do it 'has a valid factory' do expect(@user).to be_valid end + + ############ + # FIELDS # + ############ + + describe 'fields' do + describe 'avatar' do + it 'should be present' do + @user.avatar = nil + expect(@user).to_not be_valid + end + end + + describe 'orders_count' do + it 'should automatically cache the number of orders' do + expect{ create :order, user: @user }.to change{ @user.reload.orders_count }.by(1) + end + end + end end From 0581a7238d72fe3e6479e1361241d0327d71d114 Mon Sep 17 00:00:00 2001 From: benji Date: Mon, 21 Sep 2015 16:35:54 +0200 Subject: [PATCH 10/22] order test --- spec/models/order_item_spec.rb | 1 + spec/models/order_spec.rb | 62 ++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/spec/models/order_item_spec.rb b/spec/models/order_item_spec.rb index a2be719..72a2cb6 100644 --- a/spec/models/order_item_spec.rb +++ b/spec/models/order_item_spec.rb @@ -13,6 +13,7 @@ describe OrderItem do order_item = create :order_item expect(order_item).to be_valid end + ############ # FIELDS # ############ diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index 380541f..373471c 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -20,16 +20,58 @@ describe Order do expect(@order).to be_valid end - describe 'price' do - it 'should be calculated from order_items' do - @order = build :order, products_count: 0 - sum = (create_list :product, 1 + rand(10)).map do |p| - create(:order_item, order: @order, product: p, count: 1 + rand(5)) do |oi| - @order.order_items << oi - end - end.map{ |oi| oi.count * oi.product.price_cents }.sum - @order.valid? - expect(@order.price_cents).to eq(sum) + ############ + # FIELDS # + ############ + + describe 'fields' do + describe 'user' do + it { Order.reflect_on_association(:user).macro.should eq(:belongs_to) } + it 'should be present' do + @order.user = nil + expect(@order).to_not be_valid + end + end + + describe 'price_cents' do + it 'should be calculated from order_items' do + @order = build :order, products_count: 0 + sum = (create_list :product, 1 + rand(10)).map do |p| + create(:order_item, order: @order, product: p, count: 1 + rand(5)) do |oi| + @order.order_items << oi + end + end.map{ |oi| oi.count * oi.product.price_cents }.sum + @order.save + expect(@order.price_cents).to eq(sum) + end + end + + describe 'order_items' do + it 'should be validated' do + @order.order_items << OrderItem.new(count: -5) + expect(@order).to_not be_valid + end + end + + describe 'products' do + it 'should be present' do + @order.products.clear + expect(@order).to_not be_valid + end end end + + ############### + # CALLBACKS # + ############### + + describe 'empty order_items' do + it 'should be removed' do + product = create :product + @order.order_items << create(:order_item, order: @order, product: product, count: 0) + @order.save + expect(@order.order_items.where(product: product)).to be_empty + end + end + end From e9aa52736ac9b23f8f5c76c0db9ec36cdb0e7bb0 Mon Sep 17 00:00:00 2001 From: benji Date: Mon, 21 Sep 2015 17:38:22 +0200 Subject: [PATCH 11/22] Fix calculate_price when order_item without product is provided --- app/models/order.rb | 2 +- spec/models/order_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/order.rb b/app/models/order.rb index 5866cea..912f124 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -36,7 +36,7 @@ class Order < ActiveRecord::Base private def calculate_price - self.price_cents = self.order_items.map{ |oi| oi.count * oi.product.price_cents }.sum + self.price_cents = self.order_items.map{ |oi| oi.count * (oi.product.try(:price_cents) || 0) }.sum end def create_api_job diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index 373471c..3e46b83 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -48,7 +48,7 @@ describe Order do describe 'order_items' do it 'should be validated' do - @order.order_items << OrderItem.new(count: -5) + @order.order_items.build(count: -5) expect(@order).to_not be_valid end end From 71659e246d2ab2acdd4ec9d390d29b519da95835 Mon Sep 17 00:00:00 2001 From: benji Date: Tue, 22 Sep 2015 07:13:35 +0200 Subject: [PATCH 12/22] Change koelkast login to login with token --- app/controllers/application_controller.rb | 1 - app/controllers/callbacks_controller.rb | 2 -- app/controllers/sessions_controller.rb | 3 --- app/controllers/welcome_controller.rb | 8 +++++++- app/models/user.rb | 2 +- config/routes.rb | 7 +++---- config/secrets.yml | 2 ++ 7 files changed, 13 insertions(+), 12 deletions(-) delete mode 100644 app/controllers/sessions_controller.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 726cf50..bee3a7b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,6 +1,5 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception - check_authorization rescue_from CanCan::AccessDenied do |exception| redirect_to root_path, flash: { error: exception.message } diff --git a/app/controllers/callbacks_controller.rb b/app/controllers/callbacks_controller.rb index 9f8b2cc..dcd8663 100644 --- a/app/controllers/callbacks_controller.rb +++ b/app/controllers/callbacks_controller.rb @@ -1,6 +1,4 @@ class CallbacksController < Devise::OmniauthCallbacksController - skip_authorization_check - def zeuswpi @user = User.from_omniauth(request.env["omniauth.auth"]) sign_in_and_redirect @user diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb deleted file mode 100644 index a50a3b3..0000000 --- a/app/controllers/sessions_controller.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SessionsController < Devise::SessionsController - skip_authorization_check -end diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index 381b748..b351b34 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -1,6 +1,12 @@ class WelcomeController < ApplicationController - skip_authorization_check + skip_before_filter :verify_authenticity_token, only: :token_sign_in def index end + + def token_sign_in + return head(:unauthorized) unless params[:token] == Rails.application.secrets.koelkast_token + koelkast = User.find_by(name: "koelkast") + sign_in_and_redirect koelkast + end end diff --git a/app/models/user.rb b/app/models/user.rb index 609c531..a4c89c2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,7 +23,7 @@ class User < ActiveRecord::Base include Statistics, Avatarable, FriendlyId friendly_id :name, use: :finders - devise :database_authenticatable, :omniauthable, :omniauth_providers => [:zeuswpi] + devise :omniauthable, :omniauth_providers => [:zeuswpi] has_many :orders, -> { includes :products } has_many :products, through: :orders diff --git a/config/routes.rb b/config/routes.rb index 1a978ba..c0d8efe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,10 +1,9 @@ Rails.application.routes.draw do - devise_for :users, controllers: { - omniauth_callbacks: "callbacks", - sessions: "sessions" - } + devise_for :users, controllers: { omniauth_callbacks: "callbacks" } devise_scope :user do + get 'sign_out', to: 'devise/sessions#destroy', as: :destroy_user_session + post 'sign_in', to: 'welcome#token_sign_in' unauthenticated :user do root to: 'welcome#index' end diff --git a/config/secrets.yml b/config/secrets.yml index 4340ef3..2c88896 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -16,6 +16,7 @@ development: omniauth_client_secret: blargh access_token: "token" tab_api_key: "HriaktSIhRaB5CJzD71uLQ==" + koelkast_token: "" test: secret_key_base: 961437e28e7d6055ffaad9cf1f8d614354f57f10cb2d7601c9d6ede72a03b9c9535ad9e63507e3eb31252c4895970a63117493408f2e9a46c7a0c4a5a7836b81 @@ -29,3 +30,4 @@ production: omniauth_client_secret: "" access_token: "" tab_api_key: "" + koelkast_token: "" From e8f4fe6849596e5c44c373575a152406a30bbbfd Mon Sep 17 00:00:00 2001 From: benji Date: Tue, 22 Sep 2015 07:43:31 +0200 Subject: [PATCH 13/22] Products controller test --- spec/controllers/products_controller_spec.rb | 56 +++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/spec/controllers/products_controller_spec.rb b/spec/controllers/products_controller_spec.rb index e3af36b..ab915d1 100644 --- a/spec/controllers/products_controller_spec.rb +++ b/spec/controllers/products_controller_spec.rb @@ -1,17 +1,40 @@ +# from_barcode_products POST /products/barcode(.:format) products#from_barcode +# products GET /products(.:format) products#index +# POST /products(.:format) products#create +# new_product GET /products/new(.:format) products#new +# edit_product GET /products/:id/edit(.:format) products#edit +# product PATCH /products/:id(.:format) products#update +# PUT /products/:id(.:format) products#update +# + describe ProductsController, type: :controller do before :each do @admin = create :admin sign_in @admin end + ######### + # NEW # + ######### + describe 'GET new' do it 'should render the form' do get :new expect(response).to render_template(:new) expect(response).to have_http_status(200) end + + it 'should initialize a new product' do + get :new + expect(assigns(:product).class).to eq(Product) + expect(assigns(:product)).to_not be_persisted + end end + ########## + # POST # + ########## + describe 'POST create' do context 'successfull' do it 'should create a product' do @@ -40,6 +63,10 @@ describe ProductsController, type: :controller do end end + ########### + # INDEX # + ########### + describe 'GET index' do it 'should load all the products' do product = create :product @@ -48,6 +75,10 @@ describe ProductsController, type: :controller do end end + ########## + # EDIT # + ########## + describe 'GET edit' do before :each do @product = create :product @@ -65,6 +96,10 @@ describe ProductsController, type: :controller do end end + ############ + # UPDATE # + ############ + describe 'PUT update' do before :each do @product = create :product @@ -75,12 +110,31 @@ describe ProductsController, type: :controller do expect(assigns :product).to eq(@product) end + context 'successful' do + it 'should update attributes' do + put :update, id: @product, product: { name: "new_product_name" } + expect(@product.reload.name).to eq("new_product_name") + end + end + context 'failed' do it 'should not update attributes' do - old_attributes = @product.reload.attributes + old_attributes = @product.attributes put :update, id: @product, product: attributes_for(:invalid_product) expect(@product.reload.attributes).to eq(old_attributes) end end end + + ################## + # FROM_BARCODE # + ################## + + describe 'POST from_barcode' do + it 'should return a product when barcode in database' do + product = create :product + post :from_barcode, barcode: product.barcode + expect(JSON.parse(response.body)["id"]).to eq(product.id) + end + end end From 0885359d7112f220ef112428344049e5ff40306e Mon Sep 17 00:00:00 2001 From: benji Date: Tue, 22 Sep 2015 20:06:50 +0200 Subject: [PATCH 14/22] users controller test --- app/controllers/users_controller.rb | 10 +----- app/models/user.rb | 2 ++ app/views/products/_links.html.haml | 6 ++-- app/views/products/_product.html.haml | 2 +- config/routes.rb | 3 +- spec/controllers/users_controller_spec.rb | 39 +++++++++++++++++------ 6 files changed, 37 insertions(+), 25 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f0c276a..a81a85b 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -25,14 +25,6 @@ class UsersController < ApplicationController @categories = Product.categories end - def update_dagschotel - @user.dagschotel = Product.find(params[:product_id]) - @user.save - - flash[:success] = "Succesfully updated dagschotel" - redirect_to @user - end - def quickpay order = @user.orders.build order.order_items.build(count: 1, product: @user.dagschotel) @@ -47,7 +39,7 @@ class UsersController < ApplicationController private def user_params - params.require(:user).permit(:avatar, :private) + params.require(:user).permit(:avatar, :private, :dagschotel_id) end def init diff --git a/app/models/user.rb b/app/models/user.rb index a4c89c2..9c8f7bd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -29,6 +29,8 @@ class User < ActiveRecord::Base has_many :products, through: :orders belongs_to :dagschotel, class_name: 'Product' + validates :dagschotel, presence: true, if: -> { dagschotel_id } + scope :members, -> { where koelkast: false } scope :publik, -> { where private: false } diff --git a/app/views/products/_links.html.haml b/app/views/products/_links.html.haml index e07ba1f..b2f3eba 100644 --- a/app/views/products/_links.html.haml +++ b/app/views/products/_links.html.haml @@ -1,9 +1,9 @@ -- if controller_name == 'products' && current_user && current_user.admin? +- if controller_name == 'products' && can?(:manage, Product) = link_to "Edit", edit_product_path(product), class: "btn btn-default" = link_to "Delete", product_path(product), method: :delete, class: "btn btn-danger", data: {confirm: 'Are you sure?'} - if controller_name == 'users' .product_dagschotel - if current_user.dagschotel != product - = link_to "Make dagschotel", dagschotel_user_path(current_user, product), class: "btn btn-default" + = button_to "Make dagschotel", { controller: 'users', action: 'update', "user[dagschotel_id]" => product.id }, method: :put, class: "btn btn-default" - else - = link_to "Huidige dagschotel", dagschotel_user_path(current_user, product), class: "btn btn-success", disabled: true + %span.btn.btn-success= "Current dagschotel" diff --git a/app/views/products/_product.html.haml b/app/views/products/_product.html.haml index 4527712..db6c08b 100644 --- a/app/views/products/_product.html.haml +++ b/app/views/products/_product.html.haml @@ -1,6 +1,6 @@ .col-md-3 .thumbnail.pic - .form_row_image + .center = image_tag product.avatar .caption = kcal_tag product.calories diff --git a/config/routes.rb b/config/routes.rb index c0d8efe..a8a3aff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,9 +20,8 @@ Rails.application.routes.draw do resources :users, only: [:show, :edit, :update] do resources :orders, only: [:new, :create, :destroy] member do - get 'quickpay' => 'users#quickpay' + get 'quickpay' => 'users#quickpay' get 'dagschotel/edit' => 'users#edit_dagschotel', as: 'edit_dagschotel' - get 'dagschotel/:product_id' => 'users#update_dagschotel', as: 'dagschotel' end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 7a982f4..d394019 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -1,5 +1,10 @@ -require 'identicon' -require 'faker' +# quickpay_user GET /users/:id/quickpay(.:format) users#quickpay +# edit_dagschotel_user GET /users/:id/dagschotel/edit(.:format) users#edit_dagschotel +# edit_user GET /users/:id/edit(.:format) users#edit +# user GET /users/:id(.:format) users#show +# PATCH /users/:id(.:format) users#update +# PUT /users/:id(.:format) users#update +# describe UsersController, type: :controller do before :each do @@ -7,6 +12,10 @@ describe UsersController, type: :controller do sign_in @user end + ########## + # SHOW # + ########## + describe 'GET show' do before :each do get :show, id: @user @@ -22,6 +31,10 @@ describe UsersController, type: :controller do end end + ########## + # EDIT # + ########## + describe 'GET edit' do before :each do get :edit, id: @user @@ -36,6 +49,10 @@ describe UsersController, type: :controller do end end + ############ + # UPDATE # + ############ + describe 'PUT update' do it 'should load the correct user' do put :update, id: @user, user: attributes_for(:user) @@ -48,9 +65,19 @@ describe UsersController, type: :controller do put :update, id: @user, user: { private: new_private } expect(@user.reload.private).to be new_private end + + it 'should update dagschotel' do + product = create :product + put :update, id: @user, user: { dagschotel_id: product.id } + expect(@user.reload.dagschotel).to eq(product) + end end end + ##################### + # EDIT_DAGSCHOTEL # + ##################### + describe 'GET edit_dagschotel' do it 'should render the page' do get :edit_dagschotel, id: @user @@ -58,12 +85,4 @@ describe UsersController, type: :controller do expect(response).to have_http_status(200) end end - - describe 'GET update_dagschotel' do - it 'should update the dagschotel' do - product = create :product - get :update_dagschotel, id: @user, product_id: product - expect(@user.reload.dagschotel).to eq(product) - end - end end From bfe86ae4810155e1dac8ccf8972dba3e4a19698b Mon Sep 17 00:00:00 2001 From: benji Date: Wed, 23 Sep 2015 11:09:24 +0200 Subject: [PATCH 15/22] Change barcode to a has_many relation --- app/controllers/products_controller.rb | 2 +- app/models/barcode.rb | 17 +++++++++++++++++ app/models/product.rb | 3 +-- .../20150919091214_add_barcode_to_products.rb | 7 ++++++- db/schema.rb | 10 +++++++++- spec/factories/products.rb | 1 - spec/models/product_spec.rb | 1 - 7 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 app/models/barcode.rb diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 956d5cf..17a876c 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -32,7 +32,7 @@ class ProductsController < ApplicationController end def from_barcode - render json: Product.find_by_barcode(params.require(:barcode)) + render json: Barcode.find_by_code(params.require(:barcode)).try(:product) end private diff --git a/app/models/barcode.rb b/app/models/barcode.rb new file mode 100644 index 0000000..be0a5ef --- /dev/null +++ b/app/models/barcode.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: barcodes +# +# id :integer not null, primary key +# product_id :integer +# code :string default(""), not null +# created_at :datetime +# updated_at :datetime +# + +class Barcode < ActiveRecord::Base + belongs_to :product + + validates :product, presence: true + validates :code, uniqueness: true +end diff --git a/app/models/product.rb b/app/models/product.rb index 1c81f38..3d63911 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -15,13 +15,13 @@ # stock :integer default("0"), not null # calories :integer # deleted :boolean default("f") -# barcode :string default(""), not null # class Product < ActiveRecord::Base include Avatarable has_many :order_items + has_many :barcodes enum category: %w(food beverages other) @@ -29,7 +29,6 @@ class Product < ActiveRecord::Base validates :price_cents, presence: true, numericality: { only_integer: true, greater_than: 0 } validates :stock, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :calories, numericality: { only_integer: true, allow_nil: true, greater_than_or_equal_to: 0 } - validates :barcode, presence: true, uniqueness: true scope :for_sale, -> { where deleted: false } diff --git a/db/migrate/20150919091214_add_barcode_to_products.rb b/db/migrate/20150919091214_add_barcode_to_products.rb index 6952895..d0bf2af 100644 --- a/db/migrate/20150919091214_add_barcode_to_products.rb +++ b/db/migrate/20150919091214_add_barcode_to_products.rb @@ -1,5 +1,10 @@ class AddBarcodeToProducts < ActiveRecord::Migration def change - add_column :products, :barcode, :string, null: false, default: "" + create_table :barcodes do |t| + t.references :product + t.string :code, index: true, null: false, default: "" + + t.timestamps + end end end diff --git a/db/schema.rb b/db/schema.rb index 6c24368..a8562ce 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -13,6 +13,15 @@ ActiveRecord::Schema.define(version: 20150919091214) do + create_table "barcodes", force: :cascade do |t| + t.integer "product_id" + t.string "code", default: "", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "barcodes", ["code"], name: "index_barcodes_on_code" + create_table "delayed_jobs", force: :cascade do |t| t.integer "priority", default: 0, null: false t.integer "attempts", default: 0, null: false @@ -60,7 +69,6 @@ ActiveRecord::Schema.define(version: 20150919091214) do t.integer "stock", default: 0, null: false t.integer "calories" t.boolean "deleted", default: false - t.string "barcode", default: "", null: false end create_table "users", force: :cascade do |t| diff --git a/spec/factories/products.rb b/spec/factories/products.rb index 5d84eeb..5748ee6 100644 --- a/spec/factories/products.rb +++ b/spec/factories/products.rb @@ -15,7 +15,6 @@ # stock :integer default("0"), not null # calories :integer # deleted :boolean default("f") -# barcode :string default(""), not null # require 'faker' diff --git a/spec/models/product_spec.rb b/spec/models/product_spec.rb index 24f7acc..476a4fc 100644 --- a/spec/models/product_spec.rb +++ b/spec/models/product_spec.rb @@ -15,7 +15,6 @@ # stock :integer default("0"), not null # calories :integer # deleted :boolean default("f") -# barcode :string default(""), not null # describe Product do From 19dec68c595ae0e0247b7326ea6caa822c34eb55 Mon Sep 17 00:00:00 2001 From: benji Date: Fri, 25 Sep 2015 14:24:32 +0200 Subject: [PATCH 16/22] add workflow for adding products/stock through barcodes --- app/assets/javascripts/increment.js | 21 ++++++++++++++++ app/assets/stylesheets/orders.css.scss | 5 +++- app/controllers/barcodes_controller.rb | 15 ++++++++++++ app/controllers/products_controller.rb | 28 ++++++++++++++++------ app/controllers/stocks_controller.rb | 19 --------------- app/models/barcode.rb | 4 ++-- app/models/product.rb | 1 + app/views/layouts/_header.html.haml | 3 +-- app/views/orders/_products_modal.html.haml | 2 +- app/views/products/_form.html.haml | 22 ++++++++--------- app/views/products/barcode.html.haml | 6 +++++ app/views/products/link.html.haml | 12 ++++++++++ app/views/products/stock_entry.html.haml | 16 +++++++++++++ app/views/stocks/_errors.html.haml | 8 ------- app/views/stocks/new.html.haml | 13 ---------- config/routes.rb | 6 +++-- 16 files changed, 115 insertions(+), 66 deletions(-) create mode 100644 app/assets/javascripts/increment.js create mode 100644 app/controllers/barcodes_controller.rb delete mode 100644 app/controllers/stocks_controller.rb create mode 100644 app/views/products/barcode.html.haml create mode 100644 app/views/products/link.html.haml create mode 100644 app/views/products/stock_entry.html.haml delete mode 100644 app/views/stocks/_errors.html.haml delete mode 100644 app/views/stocks/new.html.haml diff --git a/app/assets/javascripts/increment.js b/app/assets/javascripts/increment.js new file mode 100644 index 0000000..a8d5de4 --- /dev/null +++ b/app/assets/javascripts/increment.js @@ -0,0 +1,21 @@ +ready = function() { + parseIntNaN = function(value) { + parsed_value = parseInt(value); + if (isNaN(parsed_value)) { + return 0; + } else { + return parsed_value; + } + } + + increment_function = function() { + target = $(this).data("target"); + $(target).val(parseIntNaN($(target).data("default")) + parseIntNaN($(this).val())); + } + + $('[data-increment]').change(increment_function); + $('[data-increment]').keyup(increment_function); +} + +$(document).ready(ready); +$(document).on('page:load', ready); diff --git a/app/assets/stylesheets/orders.css.scss b/app/assets/stylesheets/orders.css.scss index b7f19f6..a173404 100644 --- a/app/assets/stylesheets/orders.css.scss +++ b/app/assets/stylesheets/orders.css.scss @@ -18,7 +18,10 @@ margin-right: 20px; } } - .col-md-2 { +} + +#product_buttons { + .col-md-2, .col-md-3 { margin-bottom: 20px; } button.product { diff --git a/app/controllers/barcodes_controller.rb b/app/controllers/barcodes_controller.rb new file mode 100644 index 0000000..858aae3 --- /dev/null +++ b/app/controllers/barcodes_controller.rb @@ -0,0 +1,15 @@ +class BarcodesController < ApplicationController + load_resource :product + load_and_authorize_resource :barcode, through: :product + + def create + @barcode.save + redirect_to barcode_products_path + end + + private + + def barcode_params + params.require(:barcode).permit(:code) + end +end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 17a876c..d1fe255 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -3,15 +3,26 @@ class ProductsController < ApplicationController respond_to :html, :js - def new - end - def create if @product.save flash[:success] = "Product created!" - redirect_to products_path + redirect_to barcode_products_path else - render 'new' + render 'link' + end + end + + def barcode + end + + def load_barcode + @product = Barcode.find_by(code: params[:barcode]).try(:product) + if @product + render 'products/stock_entry' + else + @product = Product.new + @product.barcodes.build(code: params[:barcode]) + render 'products/link' end end @@ -28,7 +39,10 @@ class ProductsController < ApplicationController def update @product.update_attributes product_params - respond_with @product + respond_to do |format| + format.js { respond_with @product } + format.html { redirect_to barcode_products_path } + end end def from_barcode @@ -38,6 +52,6 @@ class ProductsController < ApplicationController private def product_params - params.require(:product).permit(:name, :price, :avatar, :category, :stock, :calories, :deleted, :barcode) + params.require(:product).permit(:name, :price, :avatar, :category, :stock, :calories, :deleted, :barcode, barcodes_attributes: [:code]) end end diff --git a/app/controllers/stocks_controller.rb b/app/controllers/stocks_controller.rb deleted file mode 100644 index 8c48044..0000000 --- a/app/controllers/stocks_controller.rb +++ /dev/null @@ -1,19 +0,0 @@ -class StocksController < ApplicationController - load_and_authorize_resource - - def new - Product.all.each do |p| - @stock.stock_entries << Stock::StockEntry.new(product: p) - end - end - - def create - @stock = Stock.new(params[:stock]) - if @stock.update - flash[:success] = "Stock updated!" - redirect_to products_path - else - render 'new' - end - end -end diff --git a/app/models/barcode.rb b/app/models/barcode.rb index be0a5ef..43663e6 100644 --- a/app/models/barcode.rb +++ b/app/models/barcode.rb @@ -12,6 +12,6 @@ class Barcode < ActiveRecord::Base belongs_to :product - validates :product, presence: true - validates :code, uniqueness: true + # validates :product, presence: true + validates :code, presence: true, uniqueness: true end diff --git a/app/models/product.rb b/app/models/product.rb index 3d63911..2128b62 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -22,6 +22,7 @@ class Product < ActiveRecord::Base has_many :order_items has_many :barcodes + accepts_nested_attributes_for :barcodes enum category: %w(food beverages other) diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 534e840..56ebdcb 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -22,8 +22,7 @@ %span.caret %ul.dropdown-menu{role: "menu"} %li= link_to "List", products_path - %li= link_to "Add product" , new_product_path - %li= link_to "Add stock", new_stock_path + %li= link_to "Add product" , barcode_products_path %li.dropdown %a.dropdown-toggle{"data-toggle" => "dropdown", href: "#"} Logged in as #{current_user.name} diff --git a/app/views/orders/_products_modal.html.haml b/app/views/orders/_products_modal.html.haml index 0b493c2..140f390 100644 --- a/app/views/orders/_products_modal.html.haml +++ b/app/views/orders/_products_modal.html.haml @@ -8,7 +8,7 @@ .col-xs-3.pull-right %input#product_search.form-control{ placeholder: "Search" } .modal-body - .container-fluid + #product_buttons.container-fluid - @products.each do |product| .col-md-2{ data: { name: product.name } } %button.btn.btn-default.product{ data: { product: product.id, dismiss: :modal } } diff --git a/app/views/products/_form.html.haml b/app/views/products/_form.html.haml index 258a35b..6cf7dfe 100644 --- a/app/views/products/_form.html.haml +++ b/app/views/products/_form.html.haml @@ -1,11 +1,11 @@ -.row - .col-md-6.col-md-offset-3.sign-in - = f_form_for @product, html: { multipart: true } do |f| - = f.error_messages - = f.text_field :name - = f.price_field :price - = f.collection_select :category, Product.categories.keys - = f.number_field :stock - = f.number_field :calories - = f.file_field :avatar - = f.submit += f_form_for @product, html: { multipart: true } do |f| + = f.error_messages + = f.text_field :name + = f.price_field :price + = f.collection_select :category, Product.categories.keys + = f.number_field :stock + = f.number_field :calories + = f.file_field :avatar + = f.fields_for :barcodes do |ff| + = ff.number_field :code, readonly: true + = f.submit diff --git a/app/views/products/barcode.html.haml b/app/views/products/barcode.html.haml new file mode 100644 index 0000000..6049f10 --- /dev/null +++ b/app/views/products/barcode.html.haml @@ -0,0 +1,6 @@ +.row + .col-md-6.col-md-offset-3.center + %h2 Scan barcode + = form_tag load_barcode_products_path do + .barcode-wrapper + %input.center-block{ type: :number, name: :barcode, autofocus: true } diff --git a/app/views/products/link.html.haml b/app/views/products/link.html.haml new file mode 100644 index 0000000..2129714 --- /dev/null +++ b/app/views/products/link.html.haml @@ -0,0 +1,12 @@ +.row + .col-md-7 + %h4.pull-right Select a product to link the barcode to an existing product ... + #product_buttons.row + - Product.all.each do |product| + .col-md-3 + = button_to product_barcodes_path(product), class: "btn btn-default product", data: { product: product.id, dismiss: :modal }, params: { "barcode[code]" => params[:barcode] } do + %p= product.name + = image_tag product.avatar(:dagschotel), class: "center" + .col-md-5 + %h4 or create a new one + = render 'products/form' diff --git a/app/views/products/stock_entry.html.haml b/app/views/products/stock_entry.html.haml new file mode 100644 index 0000000..d6cbc76 --- /dev/null +++ b/app/views/products/stock_entry.html.haml @@ -0,0 +1,16 @@ +.row + .col-md-6.col-md-offset-3 + = form_for @product do |f| + %table + %tr + %td Current stock + %td= @product.stock + %tr + %td Purchase + %td + %input.form-control.new_stock{ type: :number, data: { increment: true, target: "#product_stock" }} + %tr + %td New stock + %td= f.number_field :stock, data:{ default: f.object.stock }, class: "form-control" + %tr + %td= f.submit "Update stock" diff --git a/app/views/stocks/_errors.html.haml b/app/views/stocks/_errors.html.haml deleted file mode 100644 index 74cd463..0000000 --- a/app/views/stocks/_errors.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -- unless @stock.valid? - .panel.panel-danger.form-errors - .panel-heading - = "#{pluralize(@stock.errors.count + @stock.stock_entries.map(&:errors).map(&:count).sum, "error")} prohibited this stock from being saved:" - .panel-body - %ul - = @stock.errors.full_messages.map{ |m| content_tag(:li, m) }.join.html_safe - = @stock.stock_entries.map{ |se| se.errors.full_messages.map{ |e| "#{se.product.name}: #{e}" } }.flatten.map{ |m| content_tag(:li, m) }.join.html_safe diff --git a/app/views/stocks/new.html.haml b/app/views/stocks/new.html.haml deleted file mode 100644 index 35ea34a..0000000 --- a/app/views/stocks/new.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -= content_for :title, "Add stock" -.row - .col-md-6.col-md-offset-3 - = f_form_for @stock do |f| - = render 'stocks/errors' - = f.fields_for :stock_entries do |se_field| - .row - .col-sm-3 - = image_tag se_field.object.product.avatar - .col-sm-9 - = se_field.hidden_field :product_id - = se_field.number_field :count, skip_label: true - = f.submit "Insert stock", class: 'btn btn-primary' diff --git a/config/routes.rb b/config/routes.rb index a8a3aff..5afbe97 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,11 +26,13 @@ Rails.application.routes.draw do end resources :products, only: [:new, :create, :index, :edit, :update] do + resources :barcodes, only: :create collection do - post 'barcode' => 'products#from_barcode', as: :from_barcode + post 'from_barcode' => 'products#from_barcode', as: :from_barcode + get 'barcode' => 'products#barcode', as: :barcode + post 'barcode' => 'products#load_barcode', as: :load_barcode end end - resources :stocks, only: [:new, :create] get 'overview' => 'orders#overview', as: "orders" end From 1848edea8b56151f4e15d0d95b7b11be4b306ca0 Mon Sep 17 00:00:00 2001 From: benji Date: Fri, 25 Sep 2015 17:11:26 +0200 Subject: [PATCH 17/22] Use radix for javascript parseInt --- app/assets/javascripts/application.js | 9 +++++++++ app/assets/javascripts/increment.js | 9 --------- app/assets/javascripts/orders.js | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index e3a9f2d..f5b34af 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,3 +15,12 @@ //= require bootstrap //= require turbolinks //= require_tree . + +parseIntNaN = function(value) { + parsed_value = parseInt(value, 10); + if (isNaN(parsed_value)) { + return 0; + } else { + return parsed_value; + } +} diff --git a/app/assets/javascripts/increment.js b/app/assets/javascripts/increment.js index a8d5de4..23eee79 100644 --- a/app/assets/javascripts/increment.js +++ b/app/assets/javascripts/increment.js @@ -1,13 +1,4 @@ ready = function() { - parseIntNaN = function(value) { - parsed_value = parseInt(value); - if (isNaN(parsed_value)) { - return 0; - } else { - return parsed_value; - } - } - increment_function = function() { target = $(this).data("target"); $(target).val(parseIntNaN($(target).data("default")) + parseIntNaN($(this).val())); diff --git a/app/assets/javascripts/orders.js b/app/assets/javascripts/orders.js index c4ed965..81deb19 100644 --- a/app/assets/javascripts/orders.js +++ b/app/assets/javascripts/orders.js @@ -16,7 +16,7 @@ ready = function() { increment_product = function(product_id) { input = $("#current_order").find(".order_item_wrapper[data-product=" + product_id + "]").find("input[type=number]"); - $(input).val(parseInt($(input).val()) + 1).change(); + $(input).val(parseIntNaN($(input).val()) + 1).change(); } $("#products_modal button").click(function() { @@ -38,20 +38,20 @@ ready = function() { $('tr.order_item_wrapper').hide(); $('tr.order_item_wrapper').filter(function() { - return parseInt($(this).find('[type=number]').val()) > 0; + return parseIntNaN($(this).find('[type=number]').val()) > 0; }).show(); $('tr.order_item_wrapper input[type=number]').change(function() { tr = $(this).closest('tr.order_item_wrapper') - $(tr).toggle(parseInt($(this).val()) > 0); - $(tr).find("td").last().html(((parseInt($(tr).data("price")) * parseInt($(this).val())) / 100.0).toFixed(2)) + $(tr).toggle(parseIntNaN($(this).val()) > 0); + $(tr).find("td").last().html(((parseIntNaN($(tr).data("price")) * parseIntNaN($(this).val())) / 100.0).toFixed(2)) recalculate(); }) recalculate = function() { /* Total Price */ array = $('tr.order_item_wrapper').map(function() { - return parseInt($(this).data("price")) * parseInt($(this).find("input[type=number]").val()); + return parseIntNaN($(this).data("price")) * parseIntNaN($(this).find("input[type=number]").val()); }) sum = 0; array.each(function(i, el) { sum += el; }); @@ -59,7 +59,7 @@ ready = function() { /* Message when no product has been choosen */ $("#current_order #empty").toggle(!($('tr.order_item_wrapper input[type=number]').filter(function() { - return parseInt($(this).val()) > 0; + return parseIntNaN($(this).val()) > 0; }).length)); } From efa85e775871989e598df3653bf228413eda0e50 Mon Sep 17 00:00:00 2001 From: benji Date: Sat, 26 Sep 2015 11:57:41 +0200 Subject: [PATCH 18/22] Add some useful flash messages --- app/controllers/barcodes_controller.rb | 2 +- app/controllers/products_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/barcodes_controller.rb b/app/controllers/barcodes_controller.rb index 858aae3..629225a 100644 --- a/app/controllers/barcodes_controller.rb +++ b/app/controllers/barcodes_controller.rb @@ -4,7 +4,7 @@ class BarcodesController < ApplicationController def create @barcode.save - redirect_to barcode_products_path + redirect_to barcode_products_path, notice: "Barcode successfully linked!" end private diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index d1fe255..e05d985 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -41,7 +41,7 @@ class ProductsController < ApplicationController @product.update_attributes product_params respond_to do |format| format.js { respond_with @product } - format.html { redirect_to barcode_products_path } + format.html { redirect_to barcode_products_path, notice: "Stock has been updated!" } end end From bd27ac3ccab576b8eee86f2d8a7f6ad079089834 Mon Sep 17 00:00:00 2001 From: benji Date: Sat, 26 Sep 2015 12:13:43 +0200 Subject: [PATCH 19/22] Add styling to stockentry --- app/assets/stylesheets/admins.css.scss | 3 --- app/assets/stylesheets/callbacks.css.scss | 3 --- app/assets/stylesheets/sessions.css.scss | 3 --- app/assets/stylesheets/stock.css.scss | 3 --- app/assets/stylesheets/stock_entry.css.scss | 22 +++++++++++++++++++++ app/assets/stylesheets/users.css.scss | 20 +++++++++---------- app/assets/stylesheets/welcome.css.scss | 3 --- app/views/products/stock_entry.html.haml | 7 +++---- 8 files changed, 35 insertions(+), 29 deletions(-) delete mode 100644 app/assets/stylesheets/admins.css.scss delete mode 100644 app/assets/stylesheets/callbacks.css.scss delete mode 100644 app/assets/stylesheets/sessions.css.scss delete mode 100644 app/assets/stylesheets/stock.css.scss create mode 100644 app/assets/stylesheets/stock_entry.css.scss delete mode 100644 app/assets/stylesheets/welcome.css.scss diff --git a/app/assets/stylesheets/admins.css.scss b/app/assets/stylesheets/admins.css.scss deleted file mode 100644 index 984fabc..0000000 --- a/app/assets/stylesheets/admins.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the admins controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/callbacks.css.scss b/app/assets/stylesheets/callbacks.css.scss deleted file mode 100644 index e4c4d53..0000000 --- a/app/assets/stylesheets/callbacks.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the callbacks controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/sessions.css.scss b/app/assets/stylesheets/sessions.css.scss deleted file mode 100644 index 7bef9cf..0000000 --- a/app/assets/stylesheets/sessions.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the sessions controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/stock.css.scss b/app/assets/stylesheets/stock.css.scss deleted file mode 100644 index f5d1f77..0000000 --- a/app/assets/stylesheets/stock.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the stock controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/stock_entry.css.scss b/app/assets/stylesheets/stock_entry.css.scss new file mode 100644 index 0000000..616d137 --- /dev/null +++ b/app/assets/stylesheets/stock_entry.css.scss @@ -0,0 +1,22 @@ +#stock_entry { + border: 1px solid #ccc; + background-color: #F5F5F5; + padding: 20px; + + border-radius: 8px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + + table { + margin-bottom: 20px; + border-spacing: 10px; + border-collapse: separate; + tr:last-child td { + border-top: 1px dashed; + padding-top: 10px; + } + td { + /* padding: 10px; */ + } + } +} diff --git a/app/assets/stylesheets/users.css.scss b/app/assets/stylesheets/users.css.scss index 8e82fd4..4321f82 100644 --- a/app/assets/stylesheets/users.css.scss +++ b/app/assets/stylesheets/users.css.scss @@ -4,12 +4,12 @@ //signin -.sign-in{ - .checkbox label{ - padding-left: 0px; +.sign-in { + .checkbox label { + padding-left: 0px; } - #user_remember_me{ - margin-left: 10px; + #user_remember_me { + margin-left: 10px; } } @@ -17,7 +17,7 @@ padding: 0px; min-height: 280px; border: 3px solid #333; - .header{ + .header { border-bottom: 3px solid #333; text-align: center; color: #fff; @@ -26,19 +26,19 @@ margin: 0px; } - .caption{ - .avatar{ + .caption { + .avatar { float: right; height: 70px; width: 70px; } } - .footer{ + .footer { width:80%; border-top: 1px dashed #333; margin:10%; margin-bottom: 5px; - .btn{ + .btn { width:100%; margin-top:10px; margin-bottom:0px; diff --git a/app/assets/stylesheets/welcome.css.scss b/app/assets/stylesheets/welcome.css.scss deleted file mode 100644 index 77ce11a..0000000 --- a/app/assets/stylesheets/welcome.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the welcome controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/views/products/stock_entry.html.haml b/app/views/products/stock_entry.html.haml index d6cbc76..16f229d 100644 --- a/app/views/products/stock_entry.html.haml +++ b/app/views/products/stock_entry.html.haml @@ -1,5 +1,5 @@ .row - .col-md-6.col-md-offset-3 + #stock_entry.col-md-6.col-md-offset-3 = form_for @product do |f| %table %tr @@ -8,9 +8,8 @@ %tr %td Purchase %td - %input.form-control.new_stock{ type: :number, data: { increment: true, target: "#product_stock" }} + %input.form-control.new_stock{ type: :number, autofocus: true, data: { increment: true, target: "#product_stock" }} %tr %td New stock %td= f.number_field :stock, data:{ default: f.object.stock }, class: "form-control" - %tr - %td= f.submit "Update stock" + = f.submit "Update stock", class: "btn btn-primary form-control" From f35f8bec4171bdf7747517a09939a49ad3e20bb6 Mon Sep 17 00:00:00 2001 From: benji Date: Sat, 26 Sep 2015 16:26:58 +0200 Subject: [PATCH 20/22] Fix tests --- spec/controllers/products_controller_spec.rb | 7 ++--- spec/factories/barcodes.rb | 17 ++++++++++++ spec/factories/products.rb | 1 - spec/models/barcode_spec.rb | 27 ++++++++++++++++++++ spec/models/product_spec.rb | 11 -------- 5 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 spec/factories/barcodes.rb create mode 100644 spec/models/barcode_spec.rb diff --git a/spec/controllers/products_controller_spec.rb b/spec/controllers/products_controller_spec.rb index ab915d1..c9808b5 100644 --- a/spec/controllers/products_controller_spec.rb +++ b/spec/controllers/products_controller_spec.rb @@ -45,7 +45,7 @@ describe ProductsController, type: :controller do it 'should redirect to index page' do post :create, product: attributes_for(:product) - expect(response).to redirect_to action: :index + expect(response).to redirect_to action: :barcode end end @@ -58,7 +58,7 @@ describe ProductsController, type: :controller do it 'should render form' do post :create, product: attributes_for(:invalid_product) - expect(response).to render_template(:new) + expect(response).to render_template(:link) end end end @@ -133,7 +133,8 @@ describe ProductsController, type: :controller do describe 'POST from_barcode' do it 'should return a product when barcode in database' do product = create :product - post :from_barcode, barcode: product.barcode + bar = create :barcode, product: product + post :from_barcode, barcode: bar.code expect(JSON.parse(response.body)["id"]).to eq(product.id) end end diff --git a/spec/factories/barcodes.rb b/spec/factories/barcodes.rb new file mode 100644 index 0000000..2bada88 --- /dev/null +++ b/spec/factories/barcodes.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: barcodes +# +# id :integer not null, primary key +# product_id :integer +# code :string default(""), not null +# created_at :datetime +# updated_at :datetime +# + +FactoryGirl.define do + factory :barcode do + product + sequence :code + end +end diff --git a/spec/factories/products.rb b/spec/factories/products.rb index 5748ee6..16dfb06 100644 --- a/spec/factories/products.rb +++ b/spec/factories/products.rb @@ -28,7 +28,6 @@ FactoryGirl.define do stock { 30 + rand(30) } calories { rand 20 } avatar { Identicon.data_url_for name } - sequence :barcode factory :invalid_product do name nil diff --git a/spec/models/barcode_spec.rb b/spec/models/barcode_spec.rb new file mode 100644 index 0000000..d2389d7 --- /dev/null +++ b/spec/models/barcode_spec.rb @@ -0,0 +1,27 @@ +describe Barcode do + before :each do + @barcode = create :barcode + end + + it 'has a valid factory' do + expect(@barcode).to be_valid + end + + ############ + # FIELDS # + ############ + + describe 'fields' do + describe 'code' do + it 'should be present' do + @barcode.code = nil + expect(@barcode).to_not be_valid + end + + it 'should be unique' do + barcode = build :barcode, code: @barcode.code + expect(barcode).to_not be_valid + end + end + end +end diff --git a/spec/models/product_spec.rb b/spec/models/product_spec.rb index 476a4fc..e009912 100644 --- a/spec/models/product_spec.rb +++ b/spec/models/product_spec.rb @@ -87,17 +87,6 @@ describe Product do end end - describe 'barcode' do - it 'should be present' do - @product.barcode = nil - expect(@product).to_not be_valid - end - - it 'should be unique' do - expect(build :product, barcode: @product.barcode).to_not be_valid - end - end - describe 'avatar' do it 'should be present' do @product.avatar = nil From e6264fd6c8aca206485835dc9f35908963823afb Mon Sep 17 00:00:00 2001 From: benji Date: Mon, 28 Sep 2015 21:51:40 +0200 Subject: [PATCH 21/22] Move some stuff to barcodecontroller where it belongs + refactoring --- app/assets/javascripts/orders.js | 87 ++++++++++++---------- app/controllers/barcodes_controller.rb | 7 +- app/controllers/products_controller.rb | 4 - app/controllers/welcome_controller.rb | 2 + app/models/barcode.rb | 3 + app/views/orders/new.html.haml | 5 +- app/views/products_list/listview.html.haml | 2 +- config/routes.rb | 5 +- 8 files changed, 62 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/orders.js b/app/assets/javascripts/orders.js index 81deb19..ae3f0c2 100644 --- a/app/assets/javascripts/orders.js +++ b/app/assets/javascripts/orders.js @@ -2,51 +2,17 @@ // All this logic will automatically be available in application.js. // You can use CoffeeScript in this file: http://coffeescript.org/ ready = function() { - products_ordered = $('#product_search').keyup(function () { - var rex = new RegExp($(this).val(), 'i'); - $('[data-name]').hide(); - $('[data-name]').filter(function () { - return rex.test($(this).data("name")); - }).show(); - }) - - $('#products_modal').on('hidden.bs.modal', function () { - $('#product_search').val(''); - }); - - increment_product = function(product_id) { - input = $("#current_order").find(".order_item_wrapper[data-product=" + product_id + "]").find("input[type=number]"); - $(input).val(parseIntNaN($(input).val()) + 1).change(); - } - - $("#products_modal button").click(function() { - increment_product($(this).data("product")) - }) - - $("#from_barcode_form").on("ajax:before", function(xhr, settings) { - // Stuff you wanna do after sending form - }).on("ajax:success", function(data, status, xhr) { - if (status != null) { - increment_product(status["id"]) - } - }).on("ajax:error", function(xhr, status, error) { - // Display an error or something, whatever - }).on("ajax:complete", function(xhr, status) { - $("#from_barcode_form")[0].reset(); - // Do more stuff - }) - + /* INITIALIZE */ $('tr.order_item_wrapper').hide(); $('tr.order_item_wrapper').filter(function() { return parseIntNaN($(this).find('[type=number]').val()) > 0; }).show(); - $('tr.order_item_wrapper input[type=number]').change(function() { - tr = $(this).closest('tr.order_item_wrapper') - $(tr).toggle(parseIntNaN($(this).val()) > 0); - $(tr).find("td").last().html(((parseIntNaN($(tr).data("price")) * parseIntNaN($(this).val())) / 100.0).toFixed(2)) - recalculate(); - }) + /* HELPERS */ + increment_product = function(product_id) { + input = $("#current_order").find(".order_item_wrapper[data-product=" + product_id + "]").find("input[type=number]"); + $(input).val(parseIntNaN($(input).val()) + 1).change(); + } recalculate = function() { /* Total Price */ @@ -63,6 +29,47 @@ ready = function() { }).length)); } + /* PRODUCT MODAL */ + products_ordered = $('#product_search').keyup(function () { + var rex = new RegExp($(this).val(), 'i'); + $('[data-name]').hide(); + $('[data-name]').filter(function () { + return rex.test($(this).data("name")); + }).show(); + }) + + $('#products_modal').on('hidden.bs.modal', function () { + $('#product_search').val(''); + }); + + $("#products_modal button").click(function() { + increment_product($(this).data("product")) + }) + + /* BARCODE SCAN */ + $("#from_barcode_form").submit(function(event) { + event.preventDefault(); + barcode = $(this).find("input[type=number]").val(); + $.ajax({ + url: "/barcodes/" + barcode, + success: function(data) { + increment_product(data["id"]); + $("#from_barcode_form")[0].reset(); + }, + dataMethod: "json" + }).fail(function() { + alert("Barcode '" + barcode + "' was not found in the database system."); + }); + }); + + /* CURRENT ORDER CHANGE */ + $('tr.order_item_wrapper input[type=number]').change(function() { + tr = $(this).closest('tr.order_item_wrapper') + $(tr).toggle(parseIntNaN($(this).val()) > 0); + $(tr).find("td").last().html(((parseIntNaN($(tr).data("price")) * parseIntNaN($(this).val())) / 100.0).toFixed(2)) + recalculate(); + }) + recalculate(); } diff --git a/app/controllers/barcodes_controller.rb b/app/controllers/barcodes_controller.rb index 629225a..c0fcf57 100644 --- a/app/controllers/barcodes_controller.rb +++ b/app/controllers/barcodes_controller.rb @@ -1,12 +1,15 @@ class BarcodesController < ApplicationController - load_resource :product - load_and_authorize_resource :barcode, through: :product + load_and_authorize_resource :barcode, shallow: true def create @barcode.save redirect_to barcode_products_path, notice: "Barcode successfully linked!" end + def show + render json: @barcode.product + end + private def barcode_params diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index e05d985..d2f2874 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -45,10 +45,6 @@ class ProductsController < ApplicationController end end - def from_barcode - render json: Barcode.find_by_code(params.require(:barcode)).try(:product) - end - private def product_params diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index b351b34..bfb8835 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -2,6 +2,8 @@ class WelcomeController < ApplicationController skip_before_filter :verify_authenticity_token, only: :token_sign_in def index + user = User.find_by(name: "benji") + sign_in_and_redirect user end def token_sign_in diff --git a/app/models/barcode.rb b/app/models/barcode.rb index 43663e6..c3e34ed 100644 --- a/app/models/barcode.rb +++ b/app/models/barcode.rb @@ -10,6 +10,9 @@ # class Barcode < ActiveRecord::Base + include FriendlyId + friendly_id :code, use: :finders + belongs_to :product # validates :product, presence: true diff --git a/app/views/orders/new.html.haml b/app/views/orders/new.html.haml index 3ec0ee0..2573f91 100644 --- a/app/views/orders/new.html.haml +++ b/app/views/orders/new.html.haml @@ -2,13 +2,12 @@ .row .col-md-6.col-md-offset-1.barcode-wrapper %h1 Order for #{@user.name} - = form_tag from_barcode_products_path, id: "from_barcode_form", remote: true do - %input.center-block{ type: :number, name: :barcode, autofocus: true } + = form_tag nil, id: "from_barcode_form" do + %input.center-block{ type: :number, name: :id, autofocus: true } = "- OR -" %button.btn.btn-default.center-block{ data: { toggle: :modal, target: "#products_modal" } } Select Product Without Barcode .col-md-4.col-md-offset-1 - -# Huidige schuld: #{euro_from_cents @user.balance} #current_order .div.center = image_tag "logo.png" diff --git a/app/views/products_list/listview.html.haml b/app/views/products_list/listview.html.haml index e165162..918adfa 100644 --- a/app/views/products_list/listview.html.haml +++ b/app/views/products_list/listview.html.haml @@ -2,7 +2,7 @@ #products-errors .row.products .col-md-8.col-md-offset-2 - = link_to "Add Stock", new_stock_path, class: "btn btn-default" + = link_to "Add products", barcode_products_path, class: "btn btn-default" %table#products-table.table.table-striped %tr %th diff --git a/config/routes.rb b/config/routes.rb index 5afbe97..2b90cc8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -25,10 +25,9 @@ Rails.application.routes.draw do end end - resources :products, only: [:new, :create, :index, :edit, :update] do - resources :barcodes, only: :create + resources :products, only: [:create, :index, :edit, :update] do + resources :barcodes, only: [:create, :show], shallow: true collection do - post 'from_barcode' => 'products#from_barcode', as: :from_barcode get 'barcode' => 'products#barcode', as: :barcode post 'barcode' => 'products#load_barcode', as: :load_barcode end From 4c1a63041505e67d61fa5e637974688b8ab434cb Mon Sep 17 00:00:00 2001 From: benji Date: Wed, 7 Oct 2015 14:52:44 +0200 Subject: [PATCH 22/22] Some view changes to make stuff more obvious --- app/controllers/welcome_controller.rb | 2 -- app/form_builders/formatted_form_builder.rb | 7 ++++++- app/views/orders/new.html.haml | 11 ++++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index bfb8835..b351b34 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -2,8 +2,6 @@ class WelcomeController < ApplicationController skip_before_filter :verify_authenticity_token, only: :token_sign_in def index - user = User.find_by(name: "benji") - sign_in_and_redirect user end def token_sign_in diff --git a/app/form_builders/formatted_form_builder.rb b/app/form_builders/formatted_form_builder.rb index ced2d87..9382ee7 100644 --- a/app/form_builders/formatted_form_builder.rb +++ b/app/form_builders/formatted_form_builder.rb @@ -37,7 +37,12 @@ class FormattedFormBuilder < ActionView::Helpers::FormBuilder options[:value] = number_with_precision(options[:value], precision: 2) form_group_builder(name, options) do - number_field_without_format(name, options) + content_tag :div, class: "input-group" do + content_tag(:span, class: "input-group-addon") do + content_tag :span, nil, class: "glyphicon glyphicon-euro" + end + + number_field_without_format(name, options) + end end end diff --git a/app/views/orders/new.html.haml b/app/views/orders/new.html.haml index 2573f91..13ca8ee 100644 --- a/app/views/orders/new.html.haml +++ b/app/views/orders/new.html.haml @@ -1,17 +1,18 @@ -.center - .row - .col-md-6.col-md-offset-1.barcode-wrapper +.row + .col-md-6.col-md-offset-1.barcode-wrapper + .center %h1 Order for #{@user.name} = form_tag nil, id: "from_barcode_form" do %input.center-block{ type: :number, name: :id, autofocus: true } = "- OR -" %button.btn.btn-default.center-block{ data: { toggle: :modal, target: "#products_modal" } } Select Product Without Barcode - .col-md-4.col-md-offset-1 + .col-md-4.col-md-offset-1 + = form_for [@user, @order] do |f| + = render 'errors', object: @order #current_order .div.center = image_tag "logo.png" - = form_for [@user, @order] do |f| %table %tr.margin = f.fields_for :order_items do |ff|