commit
88096fc46a
73 changed files with 752 additions and 431 deletions
BIN
app/assets/images/logo.png
Normal file
BIN
app/assets/images/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
12
app/assets/javascripts/increment.js
Normal file
12
app/assets/javascripts/increment.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
ready = function() {
|
||||
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);
|
|
@ -2,64 +2,75 @@
|
|||
// All this logic will automatically be available in application.js.
|
||||
// You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
ready = function() {
|
||||
$('.btn-inc').on('click', function() {
|
||||
increment($(this), 1);
|
||||
/* INITIALIZE */
|
||||
$('tr.order_item_wrapper').hide();
|
||||
$('tr.order_item_wrapper').filter(function() {
|
||||
return parseIntNaN($(this).find('[type=number]').val()) > 0;
|
||||
}).show();
|
||||
|
||||
/* 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 */
|
||||
array = $('tr.order_item_wrapper').map(function() {
|
||||
return parseIntNaN($(this).data("price")) * parseIntNaN($(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 parseIntNaN($(this).val()) > 0;
|
||||
}).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('');
|
||||
});
|
||||
$('.btn-dec').on('click', function() {
|
||||
increment($(this), -1);
|
||||
});
|
||||
$('.form_row').each(function(index, row) {
|
||||
updateInput(row, false);
|
||||
$(row).on('input', function() {
|
||||
updateInput(row);
|
||||
|
||||
$("#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();
|
||||
};
|
||||
|
||||
// Validate input, and then update
|
||||
updateInput = function(row, useRecalculate) {
|
||||
if (useRecalculate == null) {
|
||||
useRecalculate = true;
|
||||
}
|
||||
cell = row.querySelector("input");
|
||||
if (!cell.validity.valid) {
|
||||
if (parseInt(cell.value) > parseInt(cell.max)) {
|
||||
cell.value = parseInt(cell.max);
|
||||
} else {
|
||||
cell.value = 0;
|
||||
}
|
||||
}
|
||||
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])
|
||||
}
|
||||
|
||||
$(document).ready(ready);
|
||||
|
|
|
@ -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/
|
|
@ -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/
|
|
@ -2,60 +2,81 @@
|
|||
// 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 */
|
||||
#products_modal {
|
||||
.modal-header {
|
||||
h4 {
|
||||
display: inline-block;
|
||||
}
|
||||
input {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form_row .btn-lg {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
.form_row .caption {
|
||||
h4 {
|
||||
position: relative;
|
||||
span {
|
||||
#product_buttons {
|
||||
.col-md-2, .col-md-3 {
|
||||
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: "€";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/
|
|
@ -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/
|
22
app/assets/stylesheets/stock_entry.css.scss
Normal file
22
app/assets/stylesheets/stock_entry.css.scss
Normal file
|
@ -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; */
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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/
|
|
@ -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 }
|
||||
|
|
18
app/controllers/barcodes_controller.rb
Normal file
18
app/controllers/barcodes_controller.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
class BarcodesController < ApplicationController
|
||||
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
|
||||
params.require(:barcode).permit(:code)
|
||||
end
|
||||
end
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,12 +39,15 @@ 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, notice: "Stock has been updated!" }
|
||||
end
|
||||
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, barcodes_attributes: [:code])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
class SessionsController < Devise::SessionsController
|
||||
skip_authorization_check
|
||||
end
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
@ -35,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)
|
||||
|
@ -57,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -4,10 +4,14 @@ class Ability
|
|||
def initialize(user)
|
||||
return unless user
|
||||
|
||||
can :from_barcode, Product
|
||||
|
||||
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
|
||||
|
|
20
app/models/barcode.rb
Normal file
20
app/models/barcode.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# == 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
|
||||
include FriendlyId
|
||||
friendly_id :code, use: :finders
|
||||
|
||||
belongs_to :product
|
||||
|
||||
# validates :product, presence: true
|
||||
validates :code, presence: true, uniqueness: true
|
||||
end
|
|
@ -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
|
||||
|
|
|
@ -21,10 +21,12 @@ class Product < ActiveRecord::Base
|
|||
include Avatarable
|
||||
|
||||
has_many :order_items
|
||||
has_many :barcodes
|
||||
accepts_nested_attributes_for :barcodes
|
||||
|
||||
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 }
|
||||
|
@ -39,8 +41,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
|
||||
|
|
|
@ -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
|
||||
|
@ -28,12 +23,14 @@ 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
|
||||
belongs_to :dagschotel, class_name: 'Product'
|
||||
|
||||
validates :dagschotel, presence: true, if: -> { dagschotel_id }
|
||||
|
||||
scope :members, -> { where koelkast: false }
|
||||
scope :publik, -> { where private: false }
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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/
|
|
@ -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?
|
||||
|
@ -26,26 +22,15 @@
|
|||
%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.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= link_to "Add product" , barcode_products_path
|
||||
%li.dropdown
|
||||
%a.dropdown-toggle{"data-toggle" => "dropdown", href: "#"}
|
||||
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
|
||||
Balance: #{euro_from_cents(current_user.balance)}
|
||||
.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'
|
||||
|
|
5
app/views/layouts/_session_button.html.haml
Normal file
5
app/views/layouts/_session_button.html.haml
Normal file
|
@ -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"
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
.col-md-3.form_total
|
||||
%strong Total price
|
||||
.input-group
|
||||
%span.input-group-addon €
|
||||
= content_tag :span, "", id: "order_price", class: "input-group-addon"
|
||||
%span.input-group-btn
|
||||
= f.submit "Order!", class: "btn btn-primary big-form-button", skip_wrapper: true
|
19
app/views/orders/_products_modal.html.haml
Normal file
19
app/views/orders/_products_modal.html.haml
Normal file
|
@ -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
|
||||
#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 } }
|
||||
%p= product.name
|
||||
= image_tag product.avatar(:dagschotel), class: "center"
|
||||
.modal-footer
|
||||
%button.btn.btn-default{ data: { dismiss: :modal } }
|
||||
Close
|
|
@ -1,9 +1,42 @@
|
|||
%h3
|
||||
Order for #{@user.name} (Huidige schuld: #{euro_from_cents(@user.balance)})
|
||||
.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
|
||||
.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
|
||||
= form_for [@user, @order] do |f|
|
||||
= render 'errors', object: @order
|
||||
#current_order
|
||||
.div.center
|
||||
= image_tag "logo.png"
|
||||
%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'
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
= render partial: 'flash'
|
||||
.warning.center
|
||||
%h1 TESTFASE | GELIEVE STREEPJES TE BLIJVEN ZETTEN | TESTFASE
|
||||
.row
|
||||
- @users.each do |user|
|
||||
= render 'users/new_order', user: user
|
||||
= render @users
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.col-md-3
|
||||
.thumbnail.pic
|
||||
.form_row_image
|
||||
.center
|
||||
= image_tag product.avatar
|
||||
.caption
|
||||
= kcal_tag product.calories
|
||||
|
|
6
app/views/products/barcode.html.haml
Normal file
6
app/views/products/barcode.html.haml
Normal file
|
@ -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 }
|
|
@ -1,2 +1,2 @@
|
|||
%h1 Update product
|
||||
= content_for :title, "Update product"
|
||||
= render "form"
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
%h1 All products
|
||||
= render partial: 'flash'
|
||||
= content_for :title, "All products"
|
||||
= render 'products/index'
|
||||
|
|
12
app/views/products/link.html.haml
Normal file
12
app/views/products/link.html.haml
Normal file
|
@ -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'
|
|
@ -1,2 +1,2 @@
|
|||
%h1 New product
|
||||
= content_for :title, "New product"
|
||||
= render "form"
|
||||
|
|
15
app/views/products/stock_entry.html.haml
Normal file
15
app/views/products/stock_entry.html.haml
Normal file
|
@ -0,0 +1,15 @@
|
|||
.row
|
||||
#stock_entry.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, 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"
|
||||
= f.submit "Update stock", class: "btn btn-primary form-control"
|
|
@ -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
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
= 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"
|
||||
= link_to "Add products", barcode_products_path, class: "btn btn-default"
|
||||
%table#products-table.table.table-striped
|
||||
%tr
|
||||
%th
|
||||
|
|
|
@ -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
|
|
@ -1,13 +0,0 @@
|
|||
.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|
|
||||
.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'
|
|
@ -1,6 +0,0 @@
|
|||
.col-md-2.overviewthumbnail
|
||||
- unless user.dagschotel.nil?
|
||||
= link_to quickpay_user_path(user) do
|
||||
= image_tag user.dagschotel.avatar(:dagschotel), class: "img-circle dagschotel"
|
||||
= link_to image_tag(user.avatar(:large) , class: "img-circle avatar"), new_user_order_path(user)
|
||||
= link_to user.name , new_user_order_path(user), class: "btn btn-info", style: get_color_style(user)
|
|
@ -1,8 +1,6 @@
|
|||
%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?" }
|
||||
.col-md-2.overviewthumbnail
|
||||
- unless user.dagschotel.nil?
|
||||
= link_to quickpay_user_path(user) do
|
||||
= image_tag user.dagschotel.avatar(:dagschotel), class: "img-circle dagschotel"
|
||||
= link_to image_tag(user.avatar(:large) , class: "img-circle avatar"), new_user_order_path(user)
|
||||
= link_to user.name , new_user_order_path(user), class: "btn btn-info", style: get_color_style(user)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
= render 'flash'
|
||||
.row
|
||||
= render 'sidebar'
|
||||
.col-sm-9
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
%h3
|
||||
Choose new Dagschotel
|
||||
= render 'products/index'
|
||||
= content_for :title, "Choose new Dagschotel"
|
||||
= render 'products/index'
|
||||
|
|
|
@ -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
|
|
@ -1,4 +1,3 @@
|
|||
= render partial: 'flash'
|
||||
.row
|
||||
= render 'sidebar'
|
||||
#user_info.col-sm-9
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
@ -18,17 +17,21 @@ 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'
|
||||
get 'dagschotel/edit' => 'users#edit_dagschotel', as: 'edit_dagschotel'
|
||||
get 'dagschotel/:product_id' => 'users#update_dagschotel', as: 'dagschotel'
|
||||
end
|
||||
end
|
||||
|
||||
resources :products, only: [:new, :create, :index, :edit, :update]
|
||||
resources :stocks, only: [:new, :create]
|
||||
resources :products, only: [:create, :index, :edit, :update] do
|
||||
resources :barcodes, only: [:create, :show], shallow: true
|
||||
collection do
|
||||
get 'barcode' => 'products#barcode', as: :barcode
|
||||
post 'barcode' => 'products#load_barcode', as: :load_barcode
|
||||
end
|
||||
end
|
||||
|
||||
get 'overview' => 'orders#overview', as: "orders"
|
||||
end
|
||||
|
|
|
@ -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: ""
|
||||
|
|
|
@ -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
|
10
db/migrate/20150919091214_add_barcode_to_products.rb
Normal file
10
db/migrate/20150919091214_add_barcode_to_products.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
class AddBarcodeToProducts < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :barcodes do |t|
|
||||
t.references :product
|
||||
t.string :code, index: true, null: false, default: ""
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
16
db/schema.rb
16
db/schema.rb
|
@ -11,7 +11,16 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20150917165758) do
|
||||
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
|
||||
|
@ -66,11 +75,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"
|
||||
|
|
|
@ -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
|
||||
|
@ -22,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
|
||||
|
||||
|
@ -35,11 +58,15 @@ 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
|
||||
|
||||
###########
|
||||
# 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,32 @@ 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
|
||||
bar = create :barcode, product: product
|
||||
post :from_barcode, barcode: bar.code
|
||||
expect(JSON.parse(response.body)["id"]).to eq(product.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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,23 +65,18 @@ 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
|
||||
|
||||
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
|
||||
#####################
|
||||
# EDIT_DAGSCHOTEL #
|
||||
#####################
|
||||
|
||||
describe 'GET edit_dagschotel' do
|
||||
it 'should render the page' do
|
||||
|
@ -73,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
|
||||
|
|
17
spec/factories/barcodes.rb
Normal file
17
spec/factories/barcodes.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
27
spec/models/barcode_spec.rb
Normal file
27
spec/models/barcode_spec.rb
Normal file
|
@ -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
|
|
@ -14,14 +14,20 @@ describe OrderItem do
|
|||
expect(order_item).to be_valid
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
############
|
||||
# FIELDS #
|
||||
############
|
||||
|
||||
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 +40,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 +65,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
|
||||
|
|
|
@ -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.build(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
|
||||
|
|
|
@ -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,28 @@ describe Product do
|
|||
end
|
||||
end
|
||||
|
||||
it 'avatar should be present' do
|
||||
@product.avatar = nil
|
||||
expect(@product).to_not be_valid
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
@ -32,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
|
||||
|
|
Loading…
Reference in a new issue