diff --git a/.capistrano/metrics b/.capistrano/metrics new file mode 100644 index 0000000..94a4335 --- /dev/null +++ b/.capistrano/metrics @@ -0,0 +1 @@ +full \ No newline at end of file diff --git a/.gitignore b/.gitignore index 77f0fc8..11b75c0 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,7 @@ # Avatars of producst public/system/products public/system/users + +.project + +/coverage diff --git a/.simplecov b/.simplecov new file mode 100644 index 0000000..2dad006 --- /dev/null +++ b/.simplecov @@ -0,0 +1,7 @@ +require 'simplecov' +require 'coveralls' + +SimpleCov.formatter = Coveralls::SimpleCov::Formatter +SimpleCov.start do + add_filter 'config/' +end diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..145612c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: ruby +rvm: + - 2.1.0 + +before_script: + - "RAILS_ENV=test bundle exec rake db:create" + - "RAILS_ENV=test bundle exec rake db:schema:load" + +script: bundle exec rake + +notifications: + slack: zeuswpi:1pHNpPMD56jXSGG1w3Ysa9rd + + email: + recipients: + - tab@zeus.ugent.be + on_success: never + on_failure: change diff --git a/Gemfile b/Gemfile index 435d153..26435ea 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '4.1.7' +gem 'rails', '4.2' # Use sqlite3 as the database for Active Record gem 'sqlite3' # Use SCSS for stylesheets @@ -29,14 +29,9 @@ gem 'spring', group: :development # add annotations of schema inside models gem 'annotate' -# Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' - # Use unicorn as the app server # gem 'unicorn' -# Use Capistrano for deployment -# gem 'capistrano-rails', group: :development # Deployment gem 'capistrano', '~> 3.1' gem 'capistrano-rails', '~> 1.1' @@ -46,9 +41,12 @@ group :production do gem 'mysql2' # Database end -# Use debugger -# gem 'debugger', group: [:development, :test] - +group :test do + gem 'capybara' + gem 'launchy' + gem "codeclimate-test-reporter", require: nil + gem 'faker', '1.4.2' +end #bootstrap gem 'bootstrap-sass', '3.2.0.0' @@ -56,9 +54,6 @@ gem 'bootstrap-sass', '3.2.0.0' #debug stuff gem 'byebug' -#passwords -gem 'bcrypt', '3.1.7' - #paginate stuff gem 'will_paginate', '3.0.7' gem 'bootstrap-will_paginate', '0.0.10' @@ -72,5 +67,12 @@ gem 'devise' # Use cancancan for authorization gem 'cancancan' -#ik wil test data maken dus dit -gem 'faker', '1.4.2' +# Safety first +gem 'paper_trail', '~> 4.0.0.beta' + +# Windoos sux +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw] + +gem 'coveralls', require: false + +gem 'omniauth-oauth2' diff --git a/Gemfile.lock b/Gemfile.lock index ae209a0..fb566ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,37 +1,48 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (4.1.7) - actionpack (= 4.1.7) - actionview (= 4.1.7) + actionmailer (4.2.0) + actionpack (= 4.2.0) + actionview (= 4.2.0) + activejob (= 4.2.0) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.7) - actionview (= 4.1.7) - activesupport (= 4.1.7) - rack (~> 1.5.2) + rails-dom-testing (~> 1.0, >= 1.0.5) + actionpack (4.2.0) + actionview (= 4.2.0) + activesupport (= 4.2.0) + rack (~> 1.6.0) rack-test (~> 0.6.2) - actionview (4.1.7) - activesupport (= 4.1.7) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.1) + actionview (4.2.0) + activesupport (= 4.2.0) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.7) - activesupport (= 4.1.7) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.1) + activejob (4.2.0) + activesupport (= 4.2.0) + globalid (>= 0.3.0) + activemodel (4.2.0) + activesupport (= 4.2.0) builder (~> 3.1) - activerecord (4.1.7) - activemodel (= 4.1.7) - activesupport (= 4.1.7) - arel (~> 5.0.0) - activesupport (4.1.7) - i18n (~> 0.6, >= 0.6.9) + activerecord (4.2.0) + activemodel (= 4.2.0) + activesupport (= 4.2.0) + arel (~> 6.0) + activesupport (4.2.0) + i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) - thread_safe (~> 0.1) + thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + addressable (2.3.7) annotate (2.6.5) activerecord (>= 2.3.0) rake (>= 0.8.7) - arel (5.0.1.20140414130214) - bcrypt (3.1.7) + arel (6.0.0) + bcrypt (3.1.10) + bcrypt (3.1.10-x64-mingw32) bootstrap-sass (3.2.0.0) sass (~> 3.2) bootstrap-will_paginate (0.0.10) @@ -41,12 +52,13 @@ GEM columnize (~> 0.8) debugger-linecache (~> 1.2) slop (~> 3.6) - cancancan (1.9.2) - capistrano (3.2.1) + cancancan (1.10.1) + capistrano (3.3.5) + capistrano-stats (~> 1.1.0) i18n rake (>= 10.0.0) sshkit (~> 1.3) - capistrano-bundler (1.1.3) + capistrano-bundler (1.1.4) capistrano (~> 3.1) sshkit (~> 1.2) capistrano-rails (1.1.2) @@ -55,19 +67,34 @@ GEM capistrano-rvm (0.1.2) capistrano (~> 3.0) sshkit (~> 1.2) + capistrano-stats (1.1.1) + capybara (2.4.4) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) climate_control (0.0.3) activesupport (>= 3.0) - cocaine (0.5.4) + cocaine (0.5.5) climate_control (>= 0.0.3, < 1.0) + codeclimate-test-reporter (0.4.7) + simplecov (>= 0.7.1, < 1.0.0) coffee-rails (4.0.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) coffee-script (2.3.0) coffee-script-source execjs - coffee-script-source (1.8.0) - colorize (0.7.3) - columnize (0.8.9) + coffee-script-source (1.9.0) + colorize (0.7.5) + columnize (0.9.0) + coveralls (0.7.11) + multi_json (~> 1.10) + rest-client (>= 1.6.8, < 2) + simplecov (~> 0.9.1) + term-ansicolor (~> 1.3) + thor (~> 0.19.1) debugger-linecache (1.2.0) devise (3.4.1) bcrypt (~> 3.0) @@ -76,102 +103,169 @@ GEM responders thread_safe (~> 0.1) warden (~> 1.2.3) + docile (1.1.5) erubis (2.7.0) - execjs (2.2.2) + execjs (2.3.0) faker (1.4.2) i18n (~> 0.5) + faraday (0.9.1) + multipart-post (>= 1.2, < 3) + ffi (1.9.6-x64-mingw32) + globalid (0.3.2) + activesupport (>= 4.1.0) + hashie (3.4.0) hike (1.2.3) - i18n (0.6.11) - jbuilder (2.2.5) + i18n (0.7.0) + jbuilder (2.2.6) activesupport (>= 3.0.0, < 5) multi_json (~> 1.2) - jquery-rails (3.1.2) - railties (>= 3.0, < 5.0) + jquery-rails (4.0.3) + rails-dom-testing (~> 1.0) + railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (1.8.1) + json (1.8.2) + jwt (1.4.1) + launchy (2.4.3) + addressable (~> 2.3) + loofah (2.0.1) + nokogiri (>= 1.5.9) mail (2.6.3) mime-types (>= 1.16, < 3) mime-types (2.4.3) - minitest (5.4.3) + mini_portile (0.6.2) + minitest (5.5.1) multi_json (1.10.1) + multi_xml (0.5.5) + multipart-post (2.0.0) mysql2 (0.3.17) net-scp (1.2.1) net-ssh (>= 2.6.5) - net-ssh (2.9.1) + net-ssh (2.9.2) + netrc (0.10.3) + nokogiri (1.6.6.2) + mini_portile (~> 0.6.0) + nokogiri (1.6.6.2-x64-mingw32) + mini_portile (~> 0.6.0) + oauth2 (1.0.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (~> 1.2) + omniauth (1.2.2) + hashie (>= 1.2, < 4) + rack (~> 1.0) + omniauth-oauth2 (1.2.0) + faraday (>= 0.8, < 0.10) + multi_json (~> 1.3) + oauth2 (~> 1.0) + omniauth (~> 1.2) orm_adapter (0.5.0) - paperclip (4.2.0) + paper_trail (4.0.0.beta2) + activerecord (>= 3.0, < 6.0) + activesupport (>= 3.0, < 6.0) + paperclip (4.2.1) activemodel (>= 3.0.0) activesupport (>= 3.0.0) cocaine (~> 0.5.3) mime-types - rack (1.5.2) - rack-test (0.6.2) + rack (1.6.0) + rack-test (0.6.3) rack (>= 1.0) - rails (4.1.7) - actionmailer (= 4.1.7) - actionpack (= 4.1.7) - actionview (= 4.1.7) - activemodel (= 4.1.7) - activerecord (= 4.1.7) - activesupport (= 4.1.7) + rails (4.2.0) + actionmailer (= 4.2.0) + actionpack (= 4.2.0) + actionview (= 4.2.0) + activejob (= 4.2.0) + activemodel (= 4.2.0) + activerecord (= 4.2.0) + activesupport (= 4.2.0) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.7) - sprockets-rails (~> 2.0) - railties (4.1.7) - actionpack (= 4.1.7) - activesupport (= 4.1.7) + railties (= 4.2.0) + sprockets-rails + rails-deprecated_sanitizer (1.0.3) + activesupport (>= 4.2.0.alpha) + rails-dom-testing (1.0.5) + activesupport (>= 4.2.0.beta, < 5.0) + nokogiri (~> 1.6.0) + rails-deprecated_sanitizer (>= 1.0.1) + rails-html-sanitizer (1.0.1) + loofah (~> 2.0) + railties (4.2.0) + actionpack (= 4.2.0) + activesupport (= 4.2.0) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (10.3.2) - rdoc (4.1.2) + rake (10.4.2) + rdoc (4.2.0) json (~> 1.4) - responders (1.1.2) - railties (>= 3.2, < 4.2) + responders (2.1.0) + railties (>= 4.2.0, < 5) + rest-client (1.7.3) + mime-types (>= 1.16, < 3.0) + netrc (~> 0.7) + rest-client (1.7.3-x64-mingw32) + ffi (~> 1.9) + mime-types (>= 1.16, < 3.0) + netrc (~> 0.7) sass (3.2.19) - sass-rails (4.0.4) + sass-rails (4.0.5) railties (>= 4.0.0, < 5.0) sass (~> 3.2.2) - sprockets (~> 2.8, < 2.12) + sprockets (~> 2.8, < 3.0) sprockets-rails (~> 2.0) sdoc (0.4.1) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) + simplecov (0.9.2) + docile (~> 1.1.0) + multi_json (~> 1.0) + simplecov-html (~> 0.9.0) + simplecov-html (0.9.0) slop (3.6.0) - spring (1.2.0) - sprockets (2.11.3) + spring (1.3.1) + sprockets (2.12.3) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.2.0) + sprockets-rails (2.2.4) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) sqlite3 (1.3.10) - sshkit (1.5.1) - colorize + sqlite3 (1.3.10-x64-mingw32) + sshkit (1.6.1) + colorize (>= 0.7.0) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) + term-ansicolor (1.3.0) + tins (~> 1.0) thor (0.19.1) thread_safe (0.3.4) tilt (1.4.1) - turbolinks (2.5.2) + tins (1.3.5) + turbolinks (2.5.3) coffee-rails tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.5.3) + tzinfo-data (1.2015.1) + tzinfo (>= 1.0.0) + uglifier (2.7.0) execjs (>= 0.3.0) json (>= 1.8.0) warden (1.2.3) rack (>= 1.0) will_paginate (3.0.7) + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby + x64-mingw32 DEPENDENCIES annotate - bcrypt (= 3.1.7) bootstrap-sass (= 3.2.0.0) bootstrap-will_paginate (= 0.0.10) byebug @@ -179,18 +273,25 @@ DEPENDENCIES capistrano (~> 3.1) capistrano-rails (~> 1.1) capistrano-rvm + capybara + codeclimate-test-reporter coffee-rails (~> 4.0.0) + coveralls devise faker (= 1.4.2) jbuilder (~> 2.0) jquery-rails + launchy mysql2 + omniauth-oauth2 + paper_trail (~> 4.0.0.beta) paperclip - rails (= 4.1.7) + rails (= 4.2) sass-rails (~> 4.0.3) sdoc (~> 0.4.0) spring sqlite3 turbolinks + tzinfo-data uglifier (>= 1.3.0) will_paginate (= 3.0.7) diff --git a/README.md b/README.md index e69de29..d7370a0 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,7 @@ +Tab +=== +[![Code Climate](https://codeclimate.com/github/ZeusWPI/Tab/badges/gpa.svg)](https://codeclimate.com/github/ZeusWPI/Tab) +[![Travis CI](https://travis-ci.org/ZeusWPI/Tab.svg)](https://travis-ci.org/ZeusWPI/Tab) +[![Coverage Status](https://coveralls.io/repos/ZeusWPI/Tab/badge.svg?branch=master)](https://coveralls.io/r/ZeusWPI/Tab?branch=master) + +Yes. We have to drink. But we also have to pay. This combines both. diff --git a/app/assets/javascripts/callbacks.js.coffee b/app/assets/javascripts/callbacks.js.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/callbacks.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/orders.js.coffee b/app/assets/javascripts/orders.js.coffee index 9eab234..32d707f 100644 --- a/app/assets/javascripts/orders.js.coffee +++ b/app/assets/javascripts/orders.js.coffee @@ -8,19 +8,33 @@ ready = -> $('.btn-dec').on 'click', -> increment($(this), -1) - $('.btn-dec').prop("disabled", true) + $('.form_row').each((index, row) -> + disIfNec(row) + $(row).on('input', recalculate) + ) + + recalculate() + +disIfNec = (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('.stock').val())) + +recalculate = () -> + value = ($(row).find('.row_counter').val() * $(row).find('.price').val() for row in $('.form_row')).reduce (a, b) -> a+b + $('#order_price').val((value / 100.0).toFixed(2)) increment = (button, n) -> + row = $(button).closest('.form_row') + # Fix the counter - counter = $(button).closest('.form_row').find('.row_counter') + counter = $(row).find('.row_counter') counter.val(parseInt(counter.val()) + n) - # Enable or disable the dec button - counter.parent().find('.btn-dec').prop("disabled", counter.val() == '0'); + # Disable buttons if necessary + disIfNec(row) - # Update the price - oldVal = parseFloat($('#order_total_price').val()) - $('#order_total_price').val(parseFloat(oldVal + counter.parent().find('.price').val() * n).toFixed(2)) + recalculate() $(document).ready(ready) $(document).on('page:load', ready) diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index f23eb96..d3eeca2 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -29,76 +29,58 @@ $gray-medium-light: #eaeaea; } /* miscellaneous */ + .nowrap { white-space: nowrap; } - -/* boostrap */ -body { - padding-top: 80px; -} - -/* footer */ -footer { - margin-top: 45px; - padding-top: 5px; - border-top: 1px solid #eaeaea; - color: #777; -} - -footer a { - color: #555; -} - -footer a:hover { - color: #222; -} - -footer small { - float: left; -} - -footer ul { - float: right; - list-style: none; -} - -footer ul li { - float: left; - margin-left: 15px; -} - -.overview{ - //margin-left: 2px; - width: 50%; - float: left; - border-radius: 0px; - margin-bottom: 0px; - border: 0px; - - .btn{ - margin-top: -55px; - width: 100%; - border-color: #444; - } - - .dagschotel{ - float:left; - position: absolute; - } - - .avatar{ - height: 190px; - width: 190px; - } -} - .center{ text-align: center; } +.form-field{ + margin-bottom: 15px; + text-align: bottom; +} + +/* bootstrap */ + +body { + padding-top: 80px; +} + +/* footer */ + +footer { + margin-top: 45px; + padding-top: 5px; + border-top: 1px solid $gray-medium-light; + color: #777; + a { + color: #555; + &:hover { + color: #222; + } + } + small { + float: left; + } + ul { + float: right; + list-style: none; + li { + float: left; + margin-left: 15px; + } + } +} + .debug_dump{ width: 100%; margin-top: 30px; } + +.nav-logo{ + margin:10px; + font-size:30px; +} diff --git a/app/assets/stylesheets/callbacks.css.scss b/app/assets/stylesheets/callbacks.css.scss new file mode 100644 index 0000000..e4c4d53 --- /dev/null +++ b/app/assets/stylesheets/callbacks.css.scss @@ -0,0 +1,3 @@ +// 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/orders.css.scss b/app/assets/stylesheets/orders.css.scss index 7415069..1f7d48c 100644 --- a/app/assets/stylesheets/orders.css.scss +++ b/app/assets/stylesheets/orders.css.scss @@ -1,3 +1,30 @@ // Place all the styles related to the Orders controller here. // 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; + } +} + diff --git a/app/assets/stylesheets/overview.css.scss b/app/assets/stylesheets/overview.css.scss new file mode 100644 index 0000000..62e4d98 --- /dev/null +++ b/app/assets/stylesheets/overview.css.scss @@ -0,0 +1,21 @@ +.overviewthumbnail { + margin-bottom: 0px; + border: 0px; + + .btn{ + margin-top: -55px; + width: 80%; + margin-left:10%; + border: 0px; + } + + .dagschotel{ + float:left; + position: absolute; + } + + .avatar{ + height: 150px; + width: 150px; + } +} diff --git a/app/assets/stylesheets/products.css.scss b/app/assets/stylesheets/products.css.scss index e77849f..9ec81e9 100644 --- a/app/assets/stylesheets/products.css.scss +++ b/app/assets/stylesheets/products.css.scss @@ -5,3 +5,20 @@ .orders .thumbnail { min-height: 200px; } + +.pic img { + padding-top: 10px; +} + +.caption{ + text-align: center; +} + +.caption h3 { + margin: 8px 0; +} + +.caption h6 { + margin-bottom: 20px; +} + diff --git a/app/assets/stylesheets/profile.css.scss b/app/assets/stylesheets/profile.css.scss new file mode 100644 index 0000000..6eab87e --- /dev/null +++ b/app/assets/stylesheets/profile.css.scss @@ -0,0 +1,73 @@ +/* User profile */ + +.user_info { + text-align: center; +} + +.user_info h2 { + line-height: 45px; + text-align: center; +} + +.user_info h5 { + margin-right: 10px; + text-align: right; +} + +.user_info .button_to input[type="submit"] { + font-size: 16px; + color: #FFF; + padding: 12px 50px 12px 50px; + border: 1px solid #999; + width: 60%; + font-family: monospace; + + text-shadow: 0px 1px 1px #666; + text-decoration: none; + + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + + background: #64a724; + background: -moz-linear-gradient(top, #64a724 0%, #579727 50%, #58982a 51%, #498c25 100%); + background: -webkit-gradient(linear, left top, left bottom, from(#64a724), to(#498c25), color-stop(0.4, #579727), color-stop(0.5, #58982a), color-stop(.9, #498c25), color-stop(0.9, #498c25)); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#64a724', endColorstr='#498c25', GradientType=0 ); + + cursor: pointer; +} + +.debt { + padding: 12px 0; + text-align: center; + font-family: monospace; + font-size: 16px; + width: 60%; + margin: auto; + margin-top: 10px; + background-color: #FF7F00; + color: white; +} + +.stats{ + width: 60%; + margin: auto; +} + +.stats h4 { + text-transform: uppercase; + font-family: monospace; + background-color: #333; + color: white; + padding: 5px 10px; + margin-top: 15px; +} + +.stats ul { + list-style-type: none; + padding: 0 0 0 15px; + margin: 0px; +} + +.stats li { +} diff --git a/app/assets/stylesheets/users.css.scss b/app/assets/stylesheets/users.css.scss index ddc2ba5..8e82fd4 100644 --- a/app/assets/stylesheets/users.css.scss +++ b/app/assets/stylesheets/users.css.scss @@ -3,6 +3,16 @@ // You can use Sass (SCSS) here: http://sass-lang.com/ +//signin +.sign-in{ + .checkbox label{ + padding-left: 0px; + } + #user_remember_me{ + margin-left: 10px; + } +} + .users .thumbnail { padding: 0px; min-height: 280px; @@ -37,11 +47,6 @@ background-color: #f5f5dc; } - - - - - /* sidebar */ aside { @@ -69,3 +74,36 @@ aside { } } } + +table { + width: 100%; +} + +.orders { + margin-top: -10px; + line-height: 140%; + & tr:nth-child(even){ + border-bottom: 1px solid #bbb; + td { + padding-bottom: 15px; + } + } + & tr:last-child { + border-bottom: none; + } + & tr:nth-child(odd) td { + padding-top: 10px; + } + & td.order_date { + font-size: 12px; + color: #999; + } +} + +.product_dagschotel { + height: 34px; +} + +#users-table td { + vertical-align: middle; +} diff --git a/app/controllers/admins_controller.rb b/app/controllers/admins_controller.rb index 7bfad91..96f8c66 100644 --- a/app/controllers/admins_controller.rb +++ b/app/controllers/admins_controller.rb @@ -1,7 +1,9 @@ require 'csv' class AdminsController < ApplicationController + def schulden - @users = User.all + authorize! :schulden, :admins + @users = User.members respond_to do |format| format.csv do headers['Content-Disposition'] = "attachment; filename=\"zeus-schulden\"" diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2e95090..8e4e7be 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,24 +4,28 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? + rescue_from CanCan::AccessDenied do |exception| + redirect_to root_path, flash: { error: exception.message } + end + def after_sign_in_path_for(resource) root_path end def after_sign_up_path_for(resource) - new_user_session_path + root_path end protected def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_up) { |u| u.permit( - :nickname, :name, :last_name, :password, :password_confirmation, - :current_password, :avatar + :nickname, :password, :password_confirmation, + :avatar ) } + devise_parameter_sanitizer.for(:account_update) { |u| u.permit( :password, :password_confirmation, :current_password, :avatar ) } end - end diff --git a/app/controllers/callbacks_controller.rb b/app/controllers/callbacks_controller.rb new file mode 100644 index 0000000..bbab5e8 --- /dev/null +++ b/app/controllers/callbacks_controller.rb @@ -0,0 +1,7 @@ +class CallbacksController < Devise::OmniauthCallbacksController + def zeuswpi + @user = User.from_omniauth(request.env["omniauth.auth"]) + @user.save + sign_in_and_redirect @user + end +end diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 84a2e57..ed72a23 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -1,49 +1,74 @@ class OrdersController < ApplicationController + include ActionView::Helpers::NumberHelper + + load_and_authorize_resource def new - @user = User.find(params[:user_id]) + init @order = @user.orders.build - @products = Product.all - @order_products = @order.order_products - @products.each do |p| - @order_products.build(product: p) - end + # products = @user.products.select("products.*", "sum(order_items.count) as count").group(:product_id).order("count desc") + # @order.g_order_items(products) + @order.g_order_items Product.all end def create - @user = User.find(params[:user_id]) - @order = @user.orders.build(order_params) - @products = Product.all - @order_products = @order.order_products + init + @order = @user.orders.build order_params + if @order.save - flash[:success] = "Ordered things! Get your stuff!" + flash[:success] = "#{@order.to_sentence} ordered. Enjoy it!" redirect_to root_path else + @order.g_order_items Product.all render 'new' end end - def index - @users_by_name = User.all.order(:name) - @users_by_order = User.all.order(:orders_count).reverse_order + def destroy + order = Order.find(params[:id]) + if order.created_at > 5.minutes.ago + order.cancel + flash[:success] = "Order has been removed." + else + flash[:error] = "This order has been placed too long ago, it can't be removed. Please contact a sysadmin." + end + redirect_to root_path + end + + def overview + @users = User.members.order(:nickname) end def quickpay - @user = User.find(params[:user_id]) - order = @user.orders.build - order.products << @user.dagschotel + user = User.find(params[:user_id]) + order = user.orders.build + order.order_items << OrderItem.new(count: 1, product: user.dagschotel, order: order) if order.save - flash[:success] = "Quick pay succeeded" + flash[:success] = "Quick pay succeeded. #{view_context.link_to("Undo", [user, order], method: :delete)}." else - flash[:error] = "Quick pay went wrong ... Sorry!" + flash[:error] = order.errors.full_messages.first end redirect_to root_path end private + def init + @user = User.find(params[:user_id]) + + if @user.koelkast? + flash[:error] = "Koelkast can't order things." + redirect_to root_path + end + + unless current_user.koelkast? || current_user.admin? || current_user == @user + flash[:error] = "Please don't order stuff for other people" + redirect_to root_path + end + end + def order_params - params.require(:order).permit(order_products_attributes: [:product_id, :count]) + params.require(:order).permit(order_items_attributes: [:count, :price, product_attributes: [:id]]) end end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 65b79e7..9c30afe 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -1,4 +1,6 @@ class ProductsController < ApplicationController + load_and_authorize_resource + def new @product = Product.new end @@ -6,14 +8,15 @@ class ProductsController < ApplicationController def create @product = Product.new(product_params) if @product.save - redirect_to action: :index + redirect_to products_path else - render :new + render 'new' end end def index @products = Product.all + @categories = Product.categories end def edit @@ -24,7 +27,7 @@ class ProductsController < ApplicationController @product = Product.find(params[:id]) if @product.update_attributes(product_params) flash[:success] = "Succesfully updated product" - redirect_to action: :index + redirect_to products_path else render 'edit' end @@ -36,10 +39,22 @@ class ProductsController < ApplicationController redirect_to products_path end + def stock + @products = Product.all + end + + def update_stock + @products = Product.all + @products.each do |product| + stock_inc = params[:products][product.id.to_s][:stock_inc].to_i + product.increment!(:stock, stock_inc) if stock_inc > 0 + end + redirect_to products_path + end + private def product_params - params.require(:product).permit(:name, :price, :avatar) + params.require(:product).permit(:name, :price, :avatar, :category, :stock) end - end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 2bceab6..786d3f3 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,23 +1,66 @@ class UsersController < ApplicationController + load_and_authorize_resource + def show - @user = User.find(params[:id]) - @orders = @user.orders.paginate(page: params[:page]) + @user = User.find_by_id(params[:id]) || current_user + @orders = @user.orders + .active + .order(:created_at) + .reverse_order + .paginate(page: params[:page]) + @products = @user.products + .select("products.*", "sum(order_items.count) as count") + .where("orders.cancelled = ?", false) + .group(:product_id) + .order("count") + .reverse_order + @categories = @user.products + .select("products.category", "sum(order_items.count) as count") + .where("orders.cancelled = ?", false) + .group(:category) end def index - @users = User.all + @users = User.members end def destroy - User.find(params[:id]).destroy + @user = User.find(params[:id]) + @user.destroy flash[:success] = "Succesfully removed user" redirect_to users_path end - def dagschotel - user = User.find(params[:user_id]) - user.dagschotel = Product.find(params[:product_id]) - user.save - redirect_to edit_user_registration_path(user) + def edit_dagschotel + @user = User.find(params[:user_id]) + @dagschotel = @user.dagschotel + + @products = Product.all + @categories = Product.categories end + + def update_dagschotel + @user = User.find(params[:user_id]) + @user.dagschotel = Product.find(params[:product_id]) + + @products = Product.all + @categories = Product.categories + + if @user.save + flash[:success] = "Succesfully updated dagschotel" + redirect_to @user + else + flash[:error] = "Error updating dagschotel" + @dagschotel = @user.reload.dagschotel + render 'edit_dagschotel' + end + + end + + private + + def init + @user = User.find(params[:user_id]) + redirect_to root_path, error: "You are not authorized to access this page." unless @user == current_user || current_user.admin? + end end diff --git a/app/form_builders/formatted_form_builder.rb b/app/form_builders/formatted_form_builder.rb new file mode 100644 index 0000000..0c415e0 --- /dev/null +++ b/app/form_builders/formatted_form_builder.rb @@ -0,0 +1,185 @@ +class FormattedFormBuilder < ActionView::Helpers::FormBuilder + include ActionView::Helpers::TextHelper + include ActionView::Context + include ActionView::Helpers::NumberHelper + + FIELD_HELPERS = %w[text_field number_field file_field password_field] + + delegate :content_tag, to: :@template + + def initialize(object_name, object, template, options) + @inline_errors = true + + super + end + + FIELD_HELPERS.each do |method_name| + with_method_name = "#{method_name}_with_format" + without_method_name = "#{method_name}_without_format" + + define_method(with_method_name) do |name, options = {}| + form_group_builder(name, options) do + send(without_method_name, name, options) + end + end + + alias_method_chain method_name, :format + end + + def price_field(name, options = {}) + options[:min] ||= 0 + options[:step] ||= 0.01 + # if object.is_a?(ActiveRecord::Base) + # options[:value] ||= object[name] + if object.respond_to?(name) + options[:value] ||= object.send name + end + options[:value] = number_with_precision(options[:value], precision: 2) + + form_group_builder(name, options) do + number_field_without_format(name, options) + end + end + + def check_box_with_format(name, options = {}, checked_value = "1", unchecked_value = "0", &block) + options.symbolize_keys! + + checkbox = check_box_without_format(name, options.except(:label), checked_value, unchecked_value) + label_content = block_given? ? capture(&block) : options[:label] + + content_tag :div, class: control_wrapper_class do + checkbox + " " + label(name, label_content) + end + end + + def counter(name, options = {}) + form_group_builder(name, options) do + counter_button("btn-dec", "glyphicon-minus") + + text_field_without_format(name, options.merge({class: 'form-control row_counter big-form-field'})) + + counter_button("btn-inc", "glyphicon-plus") + end + end + + alias_method_chain :check_box, :format + + def collection_select_with_format(name, collection, value_method = :to_s, text_method = :titlecase, options = {}, html_options = {}) + form_group_builder(name, options, html_options) do + collection_select_without_format(name, collection, value_method, text_method, options, html_options) + end + end + + alias_method_chain :collection_select, :format + + def submit_with_format(name = nil, options = {}) + options[:class] = submit_class unless options[:class] + content_tag :div, class: submit_wrapper_class do + submit_without_format(name, options) + end + end + + alias_method_chain :submit, :format + + def error_messages + if object.errors.any? + content_tag :div, class: "panel panel-danger form-errors" do + content_tag(:div, class: "panel-body") do + error_header + error_body + end + end + end + end + + def error_header + content_tag(:div, class: "panel-heading") do + "#{pluralize(object.errors.count, "error")} prohibited this #{object.class.name.downcase} from being saved:" + end + end + + def error_body + content_tag :ul do + object.errors.full_messages.map do |msg| + content_tag :li, msg + end.join.html_safe + end + end + + private + def label_class + "control-label" + end + + def control_class + "form-control" + end + + def control_wrapper_class + "form-group" + end + + def submit_class + "btn btn-primary" + end + + def submit_wrapper_class + "actions" + end + + def form_group(*args, &block) + options = args.extract_options! + name = args.first + + options[:class] = [control_wrapper_class, options[:class]].compact.join(' ') + + content_tag(:div, options.except(:label)) do + label = generate_label(name, options[:label]) if options[:label] + control = capture(&block).to_s + + if label + label + control + else + control + end + end + end + + def form_group_builder(method, options, html_options = nil) + options.symbolize_keys! + + css_options = html_options || options + css_options[:class] = [control_class, css_options[:class]].compact.join(" ") + + wrapper_class = css_options.delete(:wrapper_class) + wrapper_options = css_options.delete(:wrapper) + + form_group_options = { + class: wrapper_class + } + + if wrapper_options.is_a?(Hash) + form_group_options.merge!(wrapper_options) + end + + unless options.delete(:skip_label) + form_group_options.reverse_merge!(label: { + text: options.delete(:label), + class: label_class + }) + end + + form_group(method, form_group_options) do + yield + end + end + + def generate_label(name, options = {}) + label("#{name}:", options[:text], options) + end + + def counter_button(button, glyphicon) + content_tag :span, class: "input-group-btn" do + content_tag :button, class: "btn btn-default btn-lg #{button}", type: "button" do + content_tag :span, "", class: "glyphicon btn-lg #{glyphicon}" + end + end + end +end diff --git a/app/helpers/admins_helper.rb b/app/helpers/admins_helper.rb deleted file mode 100644 index d4f7b34..0000000 --- a/app/helpers/admins_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module AdminsHelper -end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 251c11f..c6f6172 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -10,57 +10,11 @@ module ApplicationHelper end def euro(f) - "€#{number_with_precision f, precision: 2}" + number_to_currency(f, unit: '€') end - #tijdelijk voor layout - def koelkast(status) - @koelkast ||= status + def f_form_for(record, options = {}, &block) + options[:builder] = FormattedFormBuilder + form_for(record, options, &block) end - - # Form helpers - def form_errors(object) - render partial: "form_errors", locals: {object: object} - end - - def form_text_field(f, tag) - render partial: "form_text_field", locals: {f: f, tag: tag} - end - - def form_password_field(f, tag) - render partial: "form_password_field", locals: {f: f, tag: tag} - end - - def form_text_area(f, tag) - render partial: "form_text_area", locals: {f: f, tag: tag} - end - - def form_fancy_text_area(f, tag) - render partial: "form_fancy_text_area", locals: {f: f, tag: tag} - end - - def form_email_field(f, tag) - render partial: "form_email_field", locals: {f: f, tag: tag} - end - - def form_date_field(f, tag, id, value) - render partial: "form_date_field", locals: {f: f, tag: tag, id: id, value: value} - end - - def form_number_field(f, tag) - render partial: "form_number_field", locals: {f: f, tag: tag} - end - - def form_collection_select(f, *args) - # This line enable passing optional arguments such as include_blank to the - # partial. If nothing is passed, an empty options hash is appended. - args << {} if args.length < 5 - - render partial: "form_collection_select", locals: {f: f, args: args} - end - - def form_check_box(f, tag) - render partial: "form_check_box", locals: {f: f, tag: tag} - end - end diff --git a/app/helpers/callbacks_helper.rb b/app/helpers/callbacks_helper.rb new file mode 100644 index 0000000..6c9550c --- /dev/null +++ b/app/helpers/callbacks_helper.rb @@ -0,0 +1,2 @@ +module CallbacksHelper +end diff --git a/app/helpers/devise_helper.rb b/app/helpers/devise_helper.rb deleted file mode 100644 index 377fdca..0000000 --- a/app/helpers/devise_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -module DeviseHelper - - def devise_error_messages! - return '' if resource.errors.empty? - - messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join - sentence = I18n.t('errors.messages.not_saved', - count: resource.errors.count, - resource: resource.class.model_name.human.downcase) - - html = <<-HTML -
- - Error! #{sentence} - #{messages} -
- HTML - - html.html_safe - end -end diff --git a/app/helpers/orders_helper.rb b/app/helpers/orders_helper.rb deleted file mode 100644 index 443227f..0000000 --- a/app/helpers/orders_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module OrdersHelper -end diff --git a/app/helpers/products_helper.rb b/app/helpers/products_helper.rb deleted file mode 100644 index ab5c42b..0000000 --- a/app/helpers/products_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ProductsHelper -end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb deleted file mode 100644 index 2310a24..0000000 --- a/app/helpers/users_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module UsersHelper -end diff --git a/app/models/ability.rb b/app/models/ability.rb index 047c60f..f0670d9 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -5,8 +5,15 @@ class Ability user ||= User.new # guest user (not logged in) if user.admin? can :manage, :all - else + can :schulden, :admins + elsif user.koelkast? + can :manage, Order + elsif user[:id] can :read, :all + can :update, User + can :edit_dagschotel, User + can :update_dagschotel, User + can :create, Order end end end diff --git a/app/models/order.rb b/app/models/order.rb index c0b7612..cf6a450 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -2,47 +2,63 @@ # # Table name: orders # -# id :integer not null, primary key -# user_id :integer -# cost :integer -# created_at :datetime not null -# updated_at :datetime not null +# id :integer not null, primary key +# user_id :integer +# price_cents :integer +# created_at :datetime not null +# updated_at :datetime not null +# cancelled :boolean default("f") # class Order < ActiveRecord::Base - after_initialize { self.total_price = 0 } - after_create :pay_price + include ActionView::Helpers::TextHelper + + after_create { self.user.increment!(:debt_cents, price_cents) } belongs_to :user, counter_cache: true - has_many :order_products - has_many :products, { through: :order_products} do - def << (product) - if proxy_association.owner.products.include?(product) - proxy_association.owner.order_products.find_by(product: product).increment!(:count, 1) + has_many :order_items, dependent: :destroy + has_many :products, through: :order_items + + scope :active, -> { where(cancelled: false) } + + validates :user, presence: true + validates :order_items, presence: true, in_stock: true + + accepts_nested_attributes_for :order_items, reject_if: proc { |oi| oi[:count].to_i <= 0 } + + def price_cents + self.order_items.map{ |oi| oi.count * oi.product.price_cents }.sum + end + + def price + self.price_cents / 100.0 + end + + def price=(_) + write_attribute(:price_cents, price_cents) + end + + def cancel + return if self.cancelled + user.decrement!(:debt_cents, price_cents) + User.decrement_counter(:orders_count, user.id) + update_attribute(:cancelled, true) + self.order_items.each(&:cancel) + end + + def to_sentence + self.order_items.map { + |oi| pluralize(oi.count, oi.product.name) + }.to_sentence + end + + def g_order_items(products) + products.each do |p| + if (oi = self.order_items.select { |t| t.product == p }).size > 0 + oi.first.count = [oi.first.product.stock, oi.first.count].min else - super + self.order_items.build(product: p) end end end - - attr_accessor :total_price - - validates :user, presence: true - validates :order_products, presence: true - - accepts_nested_attributes_for :order_products, reject_if: proc { |op| op[:count].to_i <= 0 } - - def price - price = 0 - order_products.each do |op| - price += op.count * op.product.read_attribute(:price) - end - price - end - - private - - def pay_price - user.pay(price) - end end diff --git a/app/models/order_item.rb b/app/models/order_item.rb new file mode 100644 index 0000000..534e3da --- /dev/null +++ b/app/models/order_item.rb @@ -0,0 +1,36 @@ +# == Schema Information +# +# Table name: order_items +# +# id :integer not null, primary key +# order_id :integer +# product_id :integer not null +# count :integer default("0") +# + +class OrderItem < ActiveRecord::Base + belongs_to :order + belongs_to :product + + validates :product, presence: true + validates :count, numericality: { only_integer: true, greater_than_or_equal_to: 0 } + + after_create :remove_from_stock + + accepts_nested_attributes_for :product + + def product_attributes=(attributes) + self.product = Product.find(attributes[:id]) + super + end + + def cancel + self.product.increment!(:stock, self.count) + end + + private + + def remove_from_stock + product.decrement!(:stock, count) + end +end diff --git a/app/models/order_product.rb b/app/models/order_product.rb deleted file mode 100644 index 0ac8ac4..0000000 --- a/app/models/order_product.rb +++ /dev/null @@ -1,19 +0,0 @@ -# == Schema Information -# -# Table name: order_products -# -# id :integer not null, primary key -# order_id :integer -# product_id :integer -# count :integer default(1) -# - -class OrderProduct < ActiveRecord::Base - belongs_to :order - belongs_to :product - - validates :product, presence: true - validates :count, numericality: { greater_than_or_equal_to: 0 } - - accepts_nested_attributes_for :product -end diff --git a/app/models/product.rb b/app/models/product.rb index e7f09dc..2344d1c 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -3,36 +3,37 @@ # Table name: products # # id :integer not null, primary key -# name :string(255) -# price :integer +# name :string not null +# price_cents :integer default("0"), not null # created_at :datetime # updated_at :datetime -# avatar_file_name :string(255) -# avatar_content_type :string(255) +# avatar_file_name :string +# avatar_content_type :string # avatar_file_size :integer # avatar_updated_at :datetime +# category :integer default("0") +# stock :integer default("0"), not null # class Product < ActiveRecord::Base - has_many :order_products - has_attached_file :avatar, styles: { medium: "100x100>" }, default_style: :medium + has_many :order_items + has_attached_file :avatar, styles: { dagschotel: "80x80>", medium: "100x100>" }, default_style: :medium + + enum category: %w(food beverages other) validates :name, presence: true - validates :price, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - validates_attachment :avatar, presence: true, content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] }, - default_url: "http://babeholder.pixoil.com/img/190/190" - - def count(order) - order_products.find_by(order: order).count - end + validates :price_cents, numericality: { only_integer: true, greater_than_or_equal_to: 0 } + validates :stock, numericality: { only_integer: true, greater_than_or_equal_to: 0 } + validates_attachment :avatar, + presence: true, + content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] } def price - (read_attribute(:price) || 0) / 100.0 + self.price_cents / 100.0 end def price=(value) if value.is_a? String then value.sub!(',', '.') end - write_attribute(:price, (value.to_f * 100).to_int) + self.price_cents = (value.to_f * 100).to_int end - end diff --git a/app/models/user.rb b/app/models/user.rb index 4468325..6a4a158 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,52 +3,76 @@ # Table name: users # # id :integer not null, primary key -# name :string(255) -# last_name :string(255) -# balance :integer default(0) -# nickname :string(255) +# debt_cents :integer default("0"), not null +# nickname :string # created_at :datetime # updated_at :datetime -# encrypted_password :string(255) default(""), not null +# encrypted_password :string default(""), not null # remember_created_at :datetime -# sign_in_count :integer default(0), not null +# sign_in_count :integer default("0"), not null # current_sign_in_at :datetime # last_sign_in_at :datetime -# current_sign_in_ip :string(255) -# last_sign_in_ip :string(255) -# dagschotel :reference +# current_sign_in_ip :string +# last_sign_in_ip :string +# admin :boolean # dagschotel_id :integer +# avatar_file_name :string +# avatar_content_type :string +# avatar_file_size :integer +# avatar_updated_at :datetime +# orders_count :integer default("0") +# koelkast :boolean default("f") +# provider :string +# uid :string # class User < ActiveRecord::Base - devise :database_authenticatable, :registerable, - :rememberable, :trackable - has_attached_file :avatar, styles: { medium: "100x100>" }, default_style: :medium, default_url: "http://babeholder.pixoil.com/img/70/70" + devise :database_authenticatable, :registerable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:zeuswpi] + + has_paper_trail only: [:debt_cents, :admin, :orders_count, :koelkast] + + has_attached_file :avatar, styles: { large: "150x150>", medium: "100x100>", small: "40x40>" }, default_style: :medium has_many :orders, -> { includes :products } + has_many :products, through: :orders belongs_to :dagschotel, class_name: 'Product' validates :nickname, presence: true, uniqueness: true - validates :name, presence: true - validates :last_name, presence: true - validates :password, length: { in: 8..128 }, confirmation: true, on: :create - validates_attachment :avatar, presence: true, content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] } + validates_attachment :avatar, + presence: true, + content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] } - def full_name - "#{name} #{last_name}" + scope :members, -> { where koelkast: false } + + def self.from_omniauth(auth) + where(provider: auth.provider, uid: auth.uid).first_or_create do |user| + user.provider = auth.provider + user.uid = auth.uid + end end - def pay(amount) - write_attribute(:balance, read_attribute(:balance) - amount) - self.save + def debt + self.debt_cents / 100.0 end - def balance - (read_attribute(:balance) || 0) / 100.0 - end - - def balance=(value) + def debt=(value) if value.is_a? String then value.sub!(',', '.') end - write_attribute(:balance, (value.to_f * 100).to_int) + self.debt_cents = (value.to_f * 100).to_int + end + + # Change URL params for User + + def to_param + "#{id} #{nickname}".parameterize + end + + # This is needed so Devise doesn't try to validate :email + + def email_required? + false + end + + def email_changed? + false end end diff --git a/app/validators/in_stock_validator.rb b/app/validators/in_stock_validator.rb new file mode 100644 index 0000000..ee7f3e3 --- /dev/null +++ b/app/validators/in_stock_validator.rb @@ -0,0 +1,9 @@ +class InStockValidator < ActiveModel::Validator + def validate(record) + p_short = [] + record.order_items.each do |oi| + p_short.append oi.product.name if oi.count > oi.product.stock + end + record.errors.add(:base, "There is not enough stock for your order of the following products: #{p_short.join(', ')}") if p_short.size > 0 + end +end diff --git a/app/views/admins/schulden.csv.erb b/app/views/admins/schulden.csv.erb index d707805..1400970 100644 --- a/app/views/admins/schulden.csv.erb +++ b/app/views/admins/schulden.csv.erb @@ -1,5 +1,3 @@ <%- headers = ['nickname', 'schulden'] -%> -<%= CSV.generate_line headers %> -<%- @users.each do |user| -%> - <%= CSV.generate_line([user.nickname, user.balance]) %> -<%- end -%> +<%= CSV.generate_line headers -%> +<% @users.each do |user| %><%= CSV.generate_line([user.nickname, user.debt]) %><% end %> diff --git a/app/views/application/_btn_dec.html.erb b/app/views/application/_btn_dec.html.erb deleted file mode 100644 index dd3e42b..0000000 --- a/app/views/application/_btn_dec.html.erb +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/views/application/_btn_inc.html.erb b/app/views/application/_btn_inc.html.erb deleted file mode 100644 index d1246cb..0000000 --- a/app/views/application/_btn_inc.html.erb +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/views/application/_errors.html.erb b/app/views/application/_errors.html.erb deleted file mode 100644 index 6a3452e..0000000 --- a/app/views/application/_errors.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -<% if model.errors.any? %> -
-
- <%= pluralize(model.errors.count, "error") %> prohibited this <%= model.class.name.downcase %> from being saved: -
-
- -
-
-<% end %> diff --git a/app/views/application/_flash.html.erb b/app/views/application/_flash.html.erb index 312b24d..18a7538 100644 --- a/app/views/application/_flash.html.erb +++ b/app/views/application/_flash.html.erb @@ -8,7 +8,7 @@ <% if flash[:success] %>
- Success! <%= flash[:success] %> + Success! <%= raw flash[:success] %>
<% end %> <% if flash[:notice] %> @@ -23,4 +23,10 @@ Warning! <%= flash[:warning] %> <% end %> + <% if flash[:alert] %> +
+ + Error! <%= flash[:alert] %> +
+ <% end %> diff --git a/app/views/application/_form_check_box.html.erb b/app/views/application/_form_check_box.html.erb deleted file mode 100644 index 8d8e429..0000000 --- a/app/views/application/_form_check_box.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -
- - <%= f.check_box tag %> -
diff --git a/app/views/application/_form_collection_select.html.erb b/app/views/application/_form_collection_select.html.erb deleted file mode 100644 index 99bd7e5..0000000 --- a/app/views/application/_form_collection_select.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<%# To pass options to the collection_select, add {options} as the last argument %> -
- <%= f.label args.first %>: - <%= f.collection_select *args, {class: 'form-control'} %> -
diff --git a/app/views/application/_form_date_field.html.erb b/app/views/application/_form_date_field.html.erb deleted file mode 100644 index 31cbe37..0000000 --- a/app/views/application/_form_date_field.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -
- <%= f.label tag %>: -
- - <%= f.text_field tag, class: 'form-control', id: id, value: value %> -
-
diff --git a/app/views/application/_form_email_field.html.erb b/app/views/application/_form_email_field.html.erb deleted file mode 100644 index c60dbd8..0000000 --- a/app/views/application/_form_email_field.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -
- <%= f.label tag %>: - <%= f.email_field tag, class: 'form-control' %> -
diff --git a/app/views/application/_form_errors.html.erb b/app/views/application/_form_errors.html.erb deleted file mode 100644 index 18c5c18..0000000 --- a/app/views/application/_form_errors.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -<% if object.errors.any? %> -
-
- <%= pluralize(object.errors.count, "error") %> prohibited this <%= object.class.name.downcase %> from being saved: -
-
- -
-
-<% end %> diff --git a/app/views/application/_form_fancy_text_area.html.erb b/app/views/application/_form_fancy_text_area.html.erb deleted file mode 100644 index 32bb3db..0000000 --- a/app/views/application/_form_fancy_text_area.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -
- <%= f.label tag %>: - <%= f.text_area tag, class: 'form-control ckeditor' %> -
diff --git a/app/views/application/_form_number_field.html.erb b/app/views/application/_form_number_field.html.erb deleted file mode 100644 index 5baa83e..0000000 --- a/app/views/application/_form_number_field.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -
- <%= f.label tag %>: - <%= f.number_field tag, class: 'form-control', min: 0 %> -
diff --git a/app/views/application/_form_password_field.html.erb b/app/views/application/_form_password_field.html.erb deleted file mode 100644 index 54e670a..0000000 --- a/app/views/application/_form_password_field.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -
- <%= f.label tag %>: - <%= f.password_field tag, class: 'form-control' %> -
diff --git a/app/views/application/_form_text_area.html.erb b/app/views/application/_form_text_area.html.erb deleted file mode 100644 index 256bfb7..0000000 --- a/app/views/application/_form_text_area.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -
- <%= f.label tag %>: - <%= f.text_area tag, class: 'form-control' %> -
diff --git a/app/views/application/_form_text_field.html.erb b/app/views/application/_form_text_field.html.erb deleted file mode 100644 index 7390ad4..0000000 --- a/app/views/application/_form_text_field.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -
- <%= f.label tag %>: - <%= f.text_field tag, class: 'form-control' %> -
diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb deleted file mode 100644 index 4540811..0000000 --- a/app/views/devise/confirmations/new.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -

Resend confirmation instructions

- -<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> - <%= devise_error_messages! %> - -
- <%= f.label :email %>
- <%= f.email_field :email, autofocus: true %> -
- -
- <%= f.submit "Resend confirmation instructions" %> -
-<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb deleted file mode 100644 index dc55f64..0000000 --- a/app/views/devise/mailer/confirmation_instructions.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

Welcome <%= @email %>!

- -

You can confirm your account email through the link below:

- -

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb deleted file mode 100644 index f667dc1..0000000 --- a/app/views/devise/mailer/reset_password_instructions.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -

Hello <%= @resource.email %>!

- -

Someone has requested a link to change your password. You can do this through the link below.

- -

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

- -

If you didn't request this, please ignore this email.

-

Your password won't change until you access the link above and create a new one.

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb deleted file mode 100644 index 41e148b..0000000 --- a/app/views/devise/mailer/unlock_instructions.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

Hello <%= @resource.email %>!

- -

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

- -

Click the link below to unlock your account:

- -

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb deleted file mode 100644 index 34a4960..0000000 --- a/app/views/devise/passwords/edit.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -

Change your password

- -<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> - <%= devise_error_messages! %> - <%= f.hidden_field :reset_password_token %> - -
<%= f.label :password, "New password" %>
- <%= f.password_field :password, :autofocus => true %>
- -
<%= f.label :password_confirmation, "Confirm new password" %>
- <%= f.password_field :password_confirmation %>
- -
<%= f.submit "Change my password" %>
-<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb deleted file mode 100644 index 9aa340f..0000000 --- a/app/views/devise/passwords/new.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -

Forgot your password?

- -<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> - <%= devise_error_messages! %> - - <%= form_email_field f, :email %> - <%= f.submit "Send me reset password instructions", class: "btn btn-primary" %> -<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 724991f..164865d 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -1,31 +1,15 @@

Edit <%= resource_name.to_s.humanize %>

+<%= render 'flash' %> -<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> - <%= devise_error_messages! %> +<%= f_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> + <%= f.error_messages %> - <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> -
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
- <% end %> + <%= f.password_field :password %> + <%= f.password_field :password_confirmation %> - <%= form_password_field f, :password %> - <%= form_password_field f, :password_confirmation %> + <%= f.password_field :current_password %> - <%= form_password_field f, :current_password %> - - <%= f.label :avatar %> <%= f.file_field :avatar %> - <%= f.submit "Update", class: 'btn btn-primary' %> -<% end %> - -<% if current_user.dagschotel.present? %> -

Current Dagschotel:

-
- <%= image_tag current_user.dagschotel.avatar %> -
-<% end %> - -

Choose new Dagschotel:

-<% Product.all.each do |p| %> - <%= link_to image_tag(p.avatar), user_dagschotel_path(current_user, p) %> + <%= f.submit "Update" %> <% end %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index d16492b..37eb96b 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -1,20 +1,16 @@

Sign up

-<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> - <%= devise_error_messages! %> +<%= f_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> + <%= f.error_messages %> - <%= form_text_field f, :nickname %> - <%= form_text_field f, :name %> - <%= form_text_field f, :last_name %> + <%= f.text_field :nickname %> - <%= form_password_field f, :password %> - <%= form_password_field f, :password_confirmation %> + <%= f.password_field :password %> + <%= f.password_field :password_confirmation %> - <%= f.label :avatar %> <%= f.file_field :avatar %> -
- <%= f.submit "Sign up", class: 'btn btn-primary' %> + <%= f.submit "Sign up" %> <% end %> <%= render "devise/shared/links" %> diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index bc6674d..222f8a5 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,16 +1,17 @@

Sign in

-
Partners sign in using the link sent by email.
+<%= render partial: 'flash' %> -<%= 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 :nickname %> + <%= f.password_field :password %> - <%= form_text_field f, :nickname %> - <%= form_password_field f, :password %> + <% if devise_mapping.rememberable? %> + <%= f.check_box :remember_me %> + <% end %> - <% if devise_mapping.rememberable? %> - <%= form_check_box f, :remember_me %> + <%= f.submit "Sign in" %> <% end %> - - <%= f.submit "Sign in", class: "btn btn-primary" %> -<% end %> +
<%= render "devise/shared/links" %> diff --git a/app/views/devise/shared/_links.erb b/app/views/devise/shared/_links.erb deleted file mode 100644 index 8d277bc..0000000 --- a/app/views/devise/shared/_links.erb +++ /dev/null @@ -1,25 +0,0 @@ -<%- if controller_name != 'sessions' %> - <%= link_to "Sign in", new_session_path(resource_name) %>
-<% end -%> - -<% if devise_mapping.registerable? && controller_name != 'registrations' %> - <% link_to "Sign up", new_registration_path(resource_name) %> -<% end %> - -<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> - <%= link_to "Forgot your password?", new_password_path(resource_name) %>
-<% end -%> - -<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> - <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
-<% end -%> - -<%- 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) %>
-<% end -%> - -<%- 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) %>
- <% end -%> -<% end -%> diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb deleted file mode 100644 index 9a4f185..0000000 --- a/app/views/devise/unlocks/new.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -

Resend unlock instructions

- -<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> - <%= devise_error_messages! %> - -
- <%= f.label :nickname %>
- <%= f.email_field :nickname, autofocus: true %> -
- -
- <%= f.submit "Resend unlock instructions" %> -
-<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb index 018625a..77e2b1a 100644 --- a/app/views/layouts/_footer.html.erb +++ b/app/views/layouts/_footer.html.erb @@ -1,7 +1,8 @@ -