commit
88096fc46a
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 bootstrap
|
||||||
//= require turbolinks
|
//= require turbolinks
|
||||||
//= require_tree .
|
//= 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.
|
// All this logic will automatically be available in application.js.
|
||||||
// You can use CoffeeScript in this file: http://coffeescript.org/
|
// You can use CoffeeScript in this file: http://coffeescript.org/
|
||||||
ready = function() {
|
ready = function() {
|
||||||
$('.btn-inc').on('click', function() {
|
/* INITIALIZE */
|
||||||
increment($(this), 1);
|
$('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);
|
$("#products_modal button").click(function() {
|
||||||
});
|
increment_product($(this).data("product"))
|
||||||
$('.form_row').each(function(index, row) {
|
})
|
||||||
updateInput(row, false);
|
|
||||||
$(row).on('input', function() {
|
/* BARCODE SCAN */
|
||||||
updateInput(row);
|
$("#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();
|
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);
|
$(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.
|
// They will automatically be included in application.css.
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
|
||||||
.big-form-button {
|
.barcode-wrapper {
|
||||||
height: 65px;
|
font-size: 300%;
|
||||||
width: 65px;
|
input {
|
||||||
}
|
width: 100%;
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form_row input::-webkit-outer-spin-button,
|
#products_modal {
|
||||||
.form_row input::-webkit-inner-spin-button {
|
.modal-header {
|
||||||
-webkit-appearance: none;
|
h4 {
|
||||||
margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
|
display: inline-block;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form_row .btn-lg {
|
#product_buttons {
|
||||||
padding: 10px 10px;
|
.col-md-2, .col-md-3 {
|
||||||
}
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
.form_row .caption {
|
button.product {
|
||||||
h4 {
|
width: 100%;
|
||||||
position: relative;
|
p {
|
||||||
span {
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
max-width: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
padding-right: 50px;
|
|
||||||
}
|
}
|
||||||
small {
|
img {
|
||||||
margin-left: -45px;
|
max-width: 100%;
|
||||||
position: absolute;
|
margin-left: auto;
|
||||||
top: 5px;
|
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
|
//signin
|
||||||
.sign-in{
|
.sign-in {
|
||||||
.checkbox label{
|
.checkbox label {
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
}
|
}
|
||||||
#user_remember_me{
|
#user_remember_me {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
min-height: 280px;
|
min-height: 280px;
|
||||||
border: 3px solid #333;
|
border: 3px solid #333;
|
||||||
.header{
|
.header {
|
||||||
border-bottom: 3px solid #333;
|
border-bottom: 3px solid #333;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
@ -26,19 +26,19 @@
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|
||||||
}
|
}
|
||||||
.caption{
|
.caption {
|
||||||
.avatar{
|
.avatar {
|
||||||
float: right;
|
float: right;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.footer{
|
.footer {
|
||||||
width:80%;
|
width:80%;
|
||||||
border-top: 1px dashed #333;
|
border-top: 1px dashed #333;
|
||||||
margin:10%;
|
margin:10%;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
.btn{
|
.btn {
|
||||||
width:100%;
|
width:100%;
|
||||||
margin-top:10px;
|
margin-top:10px;
|
||||||
margin-bottom:0px;
|
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
|
class ApplicationController < ActionController::Base
|
||||||
protect_from_forgery with: :exception
|
protect_from_forgery with: :exception
|
||||||
check_authorization
|
|
||||||
|
|
||||||
rescue_from CanCan::AccessDenied do |exception|
|
rescue_from CanCan::AccessDenied do |exception|
|
||||||
redirect_to root_path, flash: { error: exception.message }
|
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
|
class CallbacksController < Devise::OmniauthCallbacksController
|
||||||
skip_authorization_check
|
|
||||||
|
|
||||||
def zeuswpi
|
def zeuswpi
|
||||||
@user = User.from_omniauth(request.env["omniauth.auth"])
|
@user = User.from_omniauth(request.env["omniauth.auth"])
|
||||||
sign_in_and_redirect @user
|
sign_in_and_redirect @user
|
||||||
|
|
|
@ -3,10 +3,8 @@ class OrdersController < ApplicationController
|
||||||
load_and_authorize_resource :order, through: :user, shallow: true
|
load_and_authorize_resource :order, through: :user, shallow: true
|
||||||
|
|
||||||
def new
|
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 = Product.all.for_sale.order(:name)
|
||||||
products.each do |p|
|
@order.products << @products
|
||||||
@order.order_items.build(product: p)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -14,6 +12,7 @@ class OrdersController < ApplicationController
|
||||||
flash[:success] = "#{@order.to_sentence} ordered. Enjoy it!"
|
flash[:success] = "#{@order.to_sentence} ordered. Enjoy it!"
|
||||||
redirect_to root_path
|
redirect_to root_path
|
||||||
else
|
else
|
||||||
|
@products = Product.all.for_sale.order(:name)
|
||||||
render 'new'
|
render 'new'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,15 +3,26 @@ class ProductsController < ApplicationController
|
||||||
|
|
||||||
respond_to :html, :js
|
respond_to :html, :js
|
||||||
|
|
||||||
def new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
if @product.save
|
if @product.save
|
||||||
flash[:success] = "Product created!"
|
flash[:success] = "Product created!"
|
||||||
redirect_to products_path
|
redirect_to barcode_products_path
|
||||||
else
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,12 +39,15 @@ class ProductsController < ApplicationController
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@product.update_attributes product_params
|
@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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def product_params
|
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
|
||||||
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
|
||||||
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
|
def edit_dagschotel
|
||||||
@dagschotel = @user.dagschotel
|
@dagschotel = @user.dagschotel
|
||||||
|
|
||||||
|
@ -35,14 +25,6 @@ class UsersController < ApplicationController
|
||||||
@categories = Product.categories
|
@categories = Product.categories
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_dagschotel
|
|
||||||
@user.dagschotel = Product.find(params[:product_id])
|
|
||||||
@user.save
|
|
||||||
|
|
||||||
flash[:success] = "Succesfully updated dagschotel"
|
|
||||||
redirect_to @user
|
|
||||||
end
|
|
||||||
|
|
||||||
def quickpay
|
def quickpay
|
||||||
order = @user.orders.build
|
order = @user.orders.build
|
||||||
order.order_items.build(count: 1, product: @user.dagschotel)
|
order.order_items.build(count: 1, product: @user.dagschotel)
|
||||||
|
@ -57,7 +39,7 @@ class UsersController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def user_params
|
def user_params
|
||||||
params.require(:user).permit(:avatar, :private)
|
params.require(:user).permit(:avatar, :private, :dagschotel_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def init
|
def init
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
class WelcomeController < ApplicationController
|
class WelcomeController < ApplicationController
|
||||||
skip_authorization_check
|
skip_before_filter :verify_authenticity_token, only: :token_sign_in
|
||||||
|
|
||||||
def index
|
def index
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -37,7 +37,12 @@ class FormattedFormBuilder < ActionView::Helpers::FormBuilder
|
||||||
options[:value] = number_with_precision(options[:value], precision: 2)
|
options[:value] = number_with_precision(options[:value], precision: 2)
|
||||||
|
|
||||||
form_group_builder(name, options) do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,14 @@ class Ability
|
||||||
def initialize(user)
|
def initialize(user)
|
||||||
return unless user
|
return unless user
|
||||||
|
|
||||||
|
can :from_barcode, Product
|
||||||
|
|
||||||
if user.admin?
|
if user.admin?
|
||||||
can :manage, :all
|
can :manage, :all
|
||||||
elsif user.koelkast?
|
elsif user.koelkast?
|
||||||
can :manage, Order
|
can :manage, Order do |order|
|
||||||
|
!order.try(:user).try(:private)
|
||||||
|
end
|
||||||
can :quickpay, User
|
can :quickpay, User
|
||||||
else
|
else
|
||||||
can :read, :all
|
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
|
private
|
||||||
|
|
||||||
def calculate_price
|
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
|
end
|
||||||
|
|
||||||
def create_api_job
|
def create_api_job
|
||||||
|
|
|
@ -21,10 +21,12 @@ class Product < ActiveRecord::Base
|
||||||
include Avatarable
|
include Avatarable
|
||||||
|
|
||||||
has_many :order_items
|
has_many :order_items
|
||||||
|
has_many :barcodes
|
||||||
|
accepts_nested_attributes_for :barcodes
|
||||||
|
|
||||||
enum category: %w(food beverages other)
|
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 :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 :stock, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
||||||
validates :calories, numericality: { only_integer: true, allow_nil: true, greater_than_or_equal_to: 0 }
|
validates :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
|
if value.is_a? String then value.sub!(',', '.') end
|
||||||
self.price_cents = (value.to_f * 100).to_int
|
self.price_cents = (value.to_f * 100).to_int
|
||||||
end
|
end
|
||||||
|
|
||||||
def take_out_of_sale!
|
|
||||||
update_attribute :deleted, true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,11 +6,6 @@
|
||||||
# created_at :datetime
|
# created_at :datetime
|
||||||
# updated_at :datetime
|
# updated_at :datetime
|
||||||
# remember_created_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
|
# admin :boolean
|
||||||
# dagschotel_id :integer
|
# dagschotel_id :integer
|
||||||
# avatar_file_name :string
|
# avatar_file_name :string
|
||||||
|
@ -28,12 +23,14 @@ class User < ActiveRecord::Base
|
||||||
include Statistics, Avatarable, FriendlyId
|
include Statistics, Avatarable, FriendlyId
|
||||||
friendly_id :name, use: :finders
|
friendly_id :name, use: :finders
|
||||||
|
|
||||||
devise :database_authenticatable, :omniauthable, :omniauth_providers => [:zeuswpi]
|
devise :omniauthable, :omniauth_providers => [:zeuswpi]
|
||||||
|
|
||||||
has_many :orders, -> { includes :products }
|
has_many :orders, -> { includes :products }
|
||||||
has_many :products, through: :orders
|
has_many :products, through: :orders
|
||||||
belongs_to :dagschotel, class_name: 'Product'
|
belongs_to :dagschotel, class_name: 'Product'
|
||||||
|
|
||||||
|
validates :dagschotel, presence: true, if: -> { dagschotel_id }
|
||||||
|
|
||||||
scope :members, -> { where koelkast: false }
|
scope :members, -> { where koelkast: false }
|
||||||
scope :publik, -> { where private: false }
|
scope :publik, -> { where private: false }
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
#flash
|
#flash
|
||||||
- if flash[:error]
|
- if flash[:error]
|
||||||
.alert.alert-danger.alert-dismissable
|
.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!
|
%strong Error!
|
||||||
= flash[:error]
|
= flash[:error]
|
||||||
- if flash[:success]
|
- if flash[:success]
|
||||||
.alert.alert-success.alert-dismissable
|
.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!
|
%strong Success!
|
||||||
= raw flash[:success]
|
= raw flash[:success]
|
||||||
- if flash[:notice]
|
- if flash[:notice]
|
||||||
.alert.alert-info.alert-dismissable
|
.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!
|
%strong Notice!
|
||||||
= flash[:notice]
|
= flash[:notice]
|
||||||
- if flash[:warning]
|
- if flash[:warning]
|
||||||
.alert.alert-warning.alert-dismissable
|
.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!
|
%strong Warning!
|
||||||
= flash[:warning]
|
= flash[:warning]
|
||||||
- if flash[:alert]
|
- if flash[:alert]
|
||||||
.alert.alert-danger.alert-dismissable
|
.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!
|
%strong Error!
|
||||||
= flash[:alert]
|
= flash[:alert]
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
%h2 Sign in
|
= content_for :title, "Sign in"
|
||||||
= render partial: 'flash'
|
|
||||||
.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.text_field :name
|
||||||
= f.password_field :password
|
= f.password_field :password
|
||||||
- if devise_mapping.rememberable?
|
- if devise_mapping.rememberable?
|
||||||
= f.check_box :remember_me
|
= f.check_box :remember_me
|
||||||
= f.submit "Sign in"
|
= 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
|
.container-fluid
|
||||||
/ Brand and toggle get grouped for better mobile display
|
/ Brand and toggle get grouped for better mobile display
|
||||||
.navbar-header
|
.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
|
%span.icon-bar
|
||||||
%span.icon-bar
|
%span.icon-bar
|
||||||
|
@ -11,11 +11,7 @@
|
||||||
- unless current_user && current_user.koelkast?
|
- unless current_user && current_user.koelkast?
|
||||||
.collapse.navbar-collapse
|
.collapse.navbar-collapse
|
||||||
.hidden-xs.navbar-form.navbar-right
|
.hidden-xs.navbar-form.navbar-right
|
||||||
.form-group
|
= render 'layouts/session_button'
|
||||||
- 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"
|
|
||||||
%ul.nav.navbar-nav.navbar-right
|
%ul.nav.navbar-nav.navbar-right
|
||||||
%li= mail_to "tab@zeus.ugent.be", "Send feedback"
|
%li= mail_to "tab@zeus.ugent.be", "Send feedback"
|
||||||
- if user_signed_in?
|
- if user_signed_in?
|
||||||
|
@ -26,26 +22,15 @@
|
||||||
%span.caret
|
%span.caret
|
||||||
%ul.dropdown-menu{role: "menu"}
|
%ul.dropdown-menu{role: "menu"}
|
||||||
%li= link_to "List", products_path
|
%li= link_to "List", products_path
|
||||||
%li= link_to "Add product" , new_product_path
|
%li= link_to "Add product" , barcode_products_path
|
||||||
%li= link_to "Add stock", new_stock_path
|
|
||||||
%li.dropdown
|
|
||||||
%a.dropdown-toggle{"aria-expanded" => "false", "data-toggle" => "dropdown", href: "#", role: "button"}
|
|
||||||
Users
|
|
||||||
%span.caret
|
|
||||||
%ul.dropdown-menu{role: "menu"}
|
|
||||||
%li= link_to "List" , users_path
|
|
||||||
%li.dropdown
|
%li.dropdown
|
||||||
%a.dropdown-toggle{"data-toggle" => "dropdown", href: "#"}
|
%a.dropdown-toggle{"data-toggle" => "dropdown", href: "#"}
|
||||||
Logged in as #{current_user.name}
|
Logged in as #{current_user.name}
|
||||||
%b.caret
|
%b.caret
|
||||||
%ul.dropdown-menu
|
%ul.dropdown-menu
|
||||||
%li= link_to "Edit avatar", edit_user_path(current_user)
|
%li= link_to "Edit profile", edit_user_path(current_user)
|
||||||
%li
|
%li
|
||||||
%p.navbar-text
|
%p.navbar-text
|
||||||
Balance: #{euro_from_cents(current_user.balance)}
|
Balance: #{euro_from_cents(current_user.balance)}
|
||||||
.visible-xs.navbar-form
|
.visible-xs.navbar-form
|
||||||
.form-group
|
= render 'layouts/session_button'
|
||||||
- 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"
|
|
||||||
|
|
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
|
%body
|
||||||
= render 'layouts/header'
|
= render 'layouts/header'
|
||||||
.container
|
.container
|
||||||
|
%h2= yield :title
|
||||||
|
= render partial: 'flash'
|
||||||
= yield
|
= yield
|
||||||
= render 'layouts/footer'
|
= render 'layouts/footer'
|
||||||
= debug(params) if Rails.env.development?
|
= debug(params) if Rails.env.development?
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.col-md-3.form_products
|
.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.center
|
||||||
.form_row_image
|
.form_row_image
|
||||||
= image_tag product.avatar
|
= 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
|
.row
|
||||||
= f_form_for [@user, @order] do |f|
|
.col-md-6.col-md-offset-1.barcode-wrapper
|
||||||
= f.error_messages
|
.center
|
||||||
.col-md-12
|
%h1 Order for #{@user.name}
|
||||||
= f.fields_for :order_items do |op_field|
|
= form_tag nil, id: "from_barcode_form" do
|
||||||
= render op_field.object, f: op_field, product: op_field.object.product
|
%input.center-block{ type: :number, name: :id, autofocus: true }
|
||||||
= render 'orders/price', f: f
|
= "- 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
|
.warning.center
|
||||||
%h1 TESTFASE | GELIEVE STREEPJES TE BLIJVEN ZETTEN | TESTFASE
|
%h1 TESTFASE | GELIEVE STREEPJES TE BLIJVEN ZETTEN | TESTFASE
|
||||||
.row
|
.row
|
||||||
- @users.each do |user|
|
= render @users
|
||||||
= render 'users/new_order', user: user
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
.row
|
= f_form_for @product, html: { multipart: true } do |f|
|
||||||
.col-md-6.col-md-offset-3.sign-in
|
= f.error_messages
|
||||||
= f_form_for @product, html: { multipart: true } do |f|
|
= f.text_field :name
|
||||||
= f.error_messages
|
= f.price_field :price
|
||||||
= f.text_field :name
|
= f.collection_select :category, Product.categories.keys
|
||||||
= f.price_field :price
|
= f.number_field :stock
|
||||||
= f.collection_select :category, Product.categories.keys
|
= f.number_field :calories
|
||||||
= f.number_field :stock
|
= f.file_field :avatar
|
||||||
= f.number_field :calories
|
= f.fields_for :barcodes do |ff|
|
||||||
= f.file_field :avatar
|
= ff.number_field :code, readonly: true
|
||||||
= f.submit
|
= 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 "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?'}
|
= link_to "Delete", product_path(product), method: :delete, class: "btn btn-danger", data: {confirm: 'Are you sure?'}
|
||||||
- if controller_name == 'users'
|
- if controller_name == 'users'
|
||||||
.product_dagschotel
|
.product_dagschotel
|
||||||
- if current_user.dagschotel != product
|
- 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
|
- 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
|
.col-md-3
|
||||||
.thumbnail.pic
|
.thumbnail.pic
|
||||||
.form_row_image
|
.center
|
||||||
= image_tag product.avatar
|
= image_tag product.avatar
|
||||||
.caption
|
.caption
|
||||||
= kcal_tag product.calories
|
= 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"
|
= render "form"
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
%h1 All products
|
= content_for :title, "All products"
|
||||||
= render partial: 'flash'
|
|
||||||
= render 'products/index'
|
= 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"
|
= 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)}"}
|
%tr{id: "products_row_#{dom_id(product)}"}
|
||||||
%td= image_tag product.avatar(:small)
|
%td= link_to image_tag(product.avatar(:small)), edit_product_path(product)
|
||||||
%td= product.name
|
%td= product.name
|
||||||
%td= euro(product.price)
|
%td= euro(product.price)
|
||||||
%td= product.stock
|
%td= product.stock
|
||||||
%td
|
%td
|
||||||
%span{:class => "glyphicon #{product.deleted ? "glyphicon-check" : "glyphicon-unchecked"}"}
|
%span{class: "glyphicon #{product.deleted ? "glyphicon-check" : "glyphicon-unchecked"}"}
|
||||||
%td= product.calories
|
%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
|
#products-errors
|
||||||
.row.products
|
.row.products
|
||||||
.col-md-8.col-md-offset-2
|
.col-md-8.col-md-offset-2
|
||||||
%h1 Products
|
= link_to "Add products", barcode_products_path, class: "btn btn-default"
|
||||||
= render partial: 'flash'
|
|
||||||
= link_to "Add Stock", new_stock_path, class: "btn btn-default"
|
|
||||||
%table#products-table.table.table-striped
|
%table#products-table.table.table-striped
|
||||||
%tr
|
%tr
|
||||||
%th
|
%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
|
.col-md-2.overviewthumbnail
|
||||||
%td= user.id
|
- unless user.dagschotel.nil?
|
||||||
%td= image_tag user.avatar(:small)
|
= link_to quickpay_user_path(user) do
|
||||||
%td= user.name
|
= image_tag user.dagschotel.avatar(:dagschotel), class: "img-circle dagschotel"
|
||||||
%td= euro(user.debt)
|
= link_to image_tag(user.avatar(:large) , class: "img-circle avatar"), new_user_order_path(user)
|
||||||
- if current_user.admin?
|
= link_to user.name , new_user_order_path(user), class: "btn btn-info", style: get_color_style(user)
|
||||||
%td
|
|
||||||
= link_to "Delete", user_path(user), method: :delete, class: "btn btn-danger", data: { confirm: "Are you sure?" }
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
= render 'flash'
|
|
||||||
.row
|
.row
|
||||||
= render 'sidebar'
|
= render 'sidebar'
|
||||||
.col-sm-9
|
.col-sm-9
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
%h3
|
= content_for :title, "Choose new Dagschotel"
|
||||||
Choose new Dagschotel
|
= render 'products/index'
|
||||||
= 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
|
.row
|
||||||
= render 'sidebar'
|
= render 'sidebar'
|
||||||
#user_info.col-sm-9
|
#user_info.col-sm-9
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
%h2 Login
|
= content_for :title, "Login"
|
||||||
= render 'flash'
|
|
||||||
If this is the first time you log in, an account will be created for you.
|
If this is the first time you log in, an account will be created for you.
|
||||||
%div
|
%div
|
||||||
%br/
|
|
||||||
= link_to "Sign in with Zeus WPI account.", omniauth_authorize_path("user", "zeuswpi"), class: "btn btn-large btn-primary"
|
= 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
|
Rails.application.routes.draw do
|
||||||
devise_for :users, controllers: {
|
devise_for :users, controllers: { omniauth_callbacks: "callbacks" }
|
||||||
omniauth_callbacks: "callbacks",
|
|
||||||
sessions: "sessions"
|
|
||||||
}
|
|
||||||
|
|
||||||
devise_scope :user do
|
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
|
unauthenticated :user do
|
||||||
root to: 'welcome#index'
|
root to: 'welcome#index'
|
||||||
end
|
end
|
||||||
|
@ -18,17 +17,21 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :users, only: [:show, :edit, :update, :index, :destroy] do
|
resources :users, only: [:show, :edit, :update] do
|
||||||
resources :orders, only: [:new, :create, :destroy]
|
resources :orders, only: [:new, :create, :destroy]
|
||||||
member do
|
member do
|
||||||
get 'quickpay' => 'users#quickpay'
|
get 'quickpay' => 'users#quickpay'
|
||||||
get 'dagschotel/edit' => 'users#edit_dagschotel', as: 'edit_dagschotel'
|
get 'dagschotel/edit' => 'users#edit_dagschotel', as: 'edit_dagschotel'
|
||||||
get 'dagschotel/:product_id' => 'users#update_dagschotel', as: 'dagschotel'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :products, only: [:new, :create, :index, :edit, :update]
|
resources :products, only: [:create, :index, :edit, :update] do
|
||||||
resources :stocks, only: [:new, :create]
|
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"
|
get 'overview' => 'orders#overview', as: "orders"
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,7 @@ development:
|
||||||
omniauth_client_secret: blargh
|
omniauth_client_secret: blargh
|
||||||
access_token: "token"
|
access_token: "token"
|
||||||
tab_api_key: "HriaktSIhRaB5CJzD71uLQ=="
|
tab_api_key: "HriaktSIhRaB5CJzD71uLQ=="
|
||||||
|
koelkast_token: ""
|
||||||
|
|
||||||
test:
|
test:
|
||||||
secret_key_base: 961437e28e7d6055ffaad9cf1f8d614354f57f10cb2d7601c9d6ede72a03b9c9535ad9e63507e3eb31252c4895970a63117493408f2e9a46c7a0c4a5a7836b81
|
secret_key_base: 961437e28e7d6055ffaad9cf1f8d614354f57f10cb2d7601c9d6ede72a03b9c9535ad9e63507e3eb31252c4895970a63117493408f2e9a46c7a0c4a5a7836b81
|
||||||
|
@ -29,3 +30,4 @@ production:
|
||||||
omniauth_client_secret: ""
|
omniauth_client_secret: ""
|
||||||
access_token: ""
|
access_token: ""
|
||||||
tab_api_key: ""
|
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.
|
# 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|
|
create_table "delayed_jobs", force: :cascade do |t|
|
||||||
t.integer "priority", default: 0, null: false
|
t.integer "priority", default: 0, null: false
|
||||||
|
@ -66,11 +75,6 @@ ActiveRecord::Schema.define(version: 20150917165758) do
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.datetime "remember_created_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.boolean "admin"
|
||||||
t.integer "dagschotel_id"
|
t.integer "dagschotel_id"
|
||||||
t.string "avatar_file_name"
|
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
|
describe ProductsController, type: :controller do
|
||||||
before :each do
|
before :each do
|
||||||
@admin = create :admin
|
@admin = create :admin
|
||||||
sign_in @admin
|
sign_in @admin
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#########
|
||||||
|
# NEW #
|
||||||
|
#########
|
||||||
|
|
||||||
describe 'GET new' do
|
describe 'GET new' do
|
||||||
it 'should render the form' do
|
it 'should render the form' do
|
||||||
get :new
|
get :new
|
||||||
expect(response).to render_template(:new)
|
expect(response).to render_template(:new)
|
||||||
expect(response).to have_http_status(200)
|
expect(response).to have_http_status(200)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
##########
|
||||||
|
# POST #
|
||||||
|
##########
|
||||||
|
|
||||||
describe 'POST create' do
|
describe 'POST create' do
|
||||||
context 'successfull' do
|
context 'successfull' do
|
||||||
it 'should create a product' do
|
it 'should create a product' do
|
||||||
|
@ -22,7 +45,7 @@ describe ProductsController, type: :controller do
|
||||||
|
|
||||||
it 'should redirect to index page' do
|
it 'should redirect to index page' do
|
||||||
post :create, product: attributes_for(:product)
|
post :create, product: attributes_for(:product)
|
||||||
expect(response).to redirect_to action: :index
|
expect(response).to redirect_to action: :barcode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -35,11 +58,15 @@ describe ProductsController, type: :controller do
|
||||||
|
|
||||||
it 'should render form' do
|
it 'should render form' do
|
||||||
post :create, product: attributes_for(:invalid_product)
|
post :create, product: attributes_for(:invalid_product)
|
||||||
expect(response).to render_template(:new)
|
expect(response).to render_template(:link)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
###########
|
||||||
|
# INDEX #
|
||||||
|
###########
|
||||||
|
|
||||||
describe 'GET index' do
|
describe 'GET index' do
|
||||||
it 'should load all the products' do
|
it 'should load all the products' do
|
||||||
product = create :product
|
product = create :product
|
||||||
|
@ -48,6 +75,10 @@ describe ProductsController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##########
|
||||||
|
# EDIT #
|
||||||
|
##########
|
||||||
|
|
||||||
describe 'GET edit' do
|
describe 'GET edit' do
|
||||||
before :each do
|
before :each do
|
||||||
@product = create :product
|
@product = create :product
|
||||||
|
@ -65,6 +96,10 @@ describe ProductsController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
############
|
||||||
|
# UPDATE #
|
||||||
|
############
|
||||||
|
|
||||||
describe 'PUT update' do
|
describe 'PUT update' do
|
||||||
before :each do
|
before :each do
|
||||||
@product = create :product
|
@product = create :product
|
||||||
|
@ -75,12 +110,32 @@ describe ProductsController, type: :controller do
|
||||||
expect(assigns :product).to eq(@product)
|
expect(assigns :product).to eq(@product)
|
||||||
end
|
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
|
context 'failed' do
|
||||||
it 'should not update attributes' 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)
|
put :update, id: @product, product: attributes_for(:invalid_product)
|
||||||
expect(@product.reload.attributes).to eq(old_attributes)
|
expect(@product.reload.attributes).to eq(old_attributes)
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
require 'identicon'
|
# quickpay_user GET /users/:id/quickpay(.:format) users#quickpay
|
||||||
require 'faker'
|
# 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
|
describe UsersController, type: :controller do
|
||||||
before :each do
|
before :each do
|
||||||
|
@ -7,6 +12,10 @@ describe UsersController, type: :controller do
|
||||||
sign_in @user
|
sign_in @user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##########
|
||||||
|
# SHOW #
|
||||||
|
##########
|
||||||
|
|
||||||
describe 'GET show' do
|
describe 'GET show' do
|
||||||
before :each do
|
before :each do
|
||||||
get :show, id: @user
|
get :show, id: @user
|
||||||
|
@ -22,6 +31,10 @@ describe UsersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##########
|
||||||
|
# EDIT #
|
||||||
|
##########
|
||||||
|
|
||||||
describe 'GET edit' do
|
describe 'GET edit' do
|
||||||
before :each do
|
before :each do
|
||||||
get :edit, id: @user
|
get :edit, id: @user
|
||||||
|
@ -36,6 +49,10 @@ describe UsersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
############
|
||||||
|
# UPDATE #
|
||||||
|
############
|
||||||
|
|
||||||
describe 'PUT update' do
|
describe 'PUT update' do
|
||||||
it 'should load the correct user' do
|
it 'should load the correct user' do
|
||||||
put :update, id: @user, user: attributes_for(:user)
|
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 }
|
put :update, id: @user, user: { private: new_private }
|
||||||
expect(@user.reload.private).to be new_private
|
expect(@user.reload.private).to be new_private
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET index' do
|
#####################
|
||||||
before :each do
|
# EDIT_DAGSCHOTEL #
|
||||||
get :index
|
#####################
|
||||||
end
|
|
||||||
|
|
||||||
it 'should load an array of all users' do
|
|
||||||
expect(assigns(:users)).to eq([@user])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should render the correct template' do
|
|
||||||
expect(response).to render_template(:index)
|
|
||||||
expect(response).to have_http_status(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET edit_dagschotel' do
|
describe 'GET edit_dagschotel' do
|
||||||
it 'should render the page' do
|
it 'should render the page' do
|
||||||
|
@ -73,12 +85,4 @@ describe UsersController, type: :controller do
|
||||||
expect(response).to have_http_status(200)
|
expect(response).to have_http_status(200)
|
||||||
end
|
end
|
||||||
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
|
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
|
# created_at :datetime
|
||||||
# updated_at :datetime
|
# updated_at :datetime
|
||||||
# remember_created_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
|
# admin :boolean
|
||||||
# dagschotel_id :integer
|
# dagschotel_id :integer
|
||||||
# avatar_file_name :string
|
# avatar_file_name :string
|
||||||
|
|
|
@ -5,37 +5,44 @@ describe User do
|
||||||
subject(:ability){ Ability.new(user) }
|
subject(:ability){ Ability.new(user) }
|
||||||
let(:user) { nil}
|
let(:user) { nil}
|
||||||
|
|
||||||
|
# Admin
|
||||||
describe 'as admin' do
|
describe 'as admin' do
|
||||||
let(:user) { create :admin }
|
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, 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, Stock.new) }
|
||||||
it{ should be_able_to(:manage, User.new) }
|
it{ should be_able_to(:manage, User.new) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Normal User
|
||||||
describe 'as normal user' do
|
describe 'as normal user' do
|
||||||
let(:user) { create :user }
|
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(: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 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(: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 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
|
end
|
||||||
|
|
||||||
describe 'as koelkast' do
|
describe 'as koelkast' do
|
||||||
let(:user) { create :koelkast }
|
let(:user) { create :koelkast }
|
||||||
|
|
||||||
it{ should_not be_able_to(:manage, Product.new) }
|
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, Stock.new) }
|
||||||
it{ should_not be_able_to(:manage, User.new) }
|
it{ should_not be_able_to(:manage, User.new) }
|
||||||
end
|
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
|
expect(order_item).to be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
############
|
||||||
|
# FIELDS #
|
||||||
|
############
|
||||||
|
|
||||||
|
describe 'fields' do
|
||||||
before :each do
|
before :each do
|
||||||
@order_item = create :order_item
|
@order_item = create :order_item
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'product should be present' do
|
describe 'product' do
|
||||||
@order_item.product = nil
|
it 'should be present' do
|
||||||
expect(@order_item).to_not be_valid
|
@order_item.product = nil
|
||||||
|
expect(@order_item).to_not be_valid
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'count' do
|
describe 'count' do
|
||||||
|
@ -34,10 +40,21 @@ describe OrderItem do
|
||||||
@order_item.count = -5
|
@order_item.count = -5
|
||||||
expect(@order_item).to_not be_valid
|
expect(@order_item).to_not be_valid
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'product stock' do
|
###############
|
||||||
|
# CALLBACKS #
|
||||||
|
###############
|
||||||
|
|
||||||
|
describe 'stock change' do
|
||||||
before :each do
|
before :each do
|
||||||
@product = create :product
|
@product = create :product
|
||||||
@count = rand 10
|
@count = rand 10
|
||||||
|
@ -48,7 +65,7 @@ describe OrderItem do
|
||||||
expect{ @order_item.save }.to change{ @product.stock }.by(-@count)
|
expect{ @order_item.save }.to change{ @product.stock }.by(-@count)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should increment on cancel' do
|
it 'should increment on destroy' do
|
||||||
@order_item.save
|
@order_item.save
|
||||||
expect{ @order_item.destroy }.to change{ @product.stock }.by(@count)
|
expect{ @order_item.destroy }.to change{ @product.stock }.by(@count)
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,16 +20,58 @@ describe Order do
|
||||||
expect(@order).to be_valid
|
expect(@order).to be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'price' do
|
############
|
||||||
it 'should be calculated from order_items' do
|
# FIELDS #
|
||||||
@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|
|
describe 'fields' do
|
||||||
@order.order_items << oi
|
describe 'user' do
|
||||||
end
|
it { Order.reflect_on_association(:user).macro.should eq(:belongs_to) }
|
||||||
end.map{ |oi| oi.count * oi.product.price_cents }.sum
|
it 'should be present' do
|
||||||
@order.valid?
|
@order.user = nil
|
||||||
expect(@order.price_cents).to eq(sum)
|
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
|
||||||
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
|
end
|
||||||
|
|
|
@ -26,23 +26,38 @@ describe Product do
|
||||||
expect(@product).to be_valid
|
expect(@product).to be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
############
|
||||||
it 'name should be present' do
|
# FIELDS #
|
||||||
@product.name = ''
|
############
|
||||||
expect(@product).to_not be_valid
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'price' do
|
describe 'fields' do
|
||||||
it 'should be positive' do
|
describe 'name' do
|
||||||
@product.price = -5
|
it 'should be present' do
|
||||||
|
@product.name = nil
|
||||||
expect(@product).to_not be_valid
|
expect(@product).to_not be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should be saved correctly' do
|
it 'shold be unique' do
|
||||||
@product.price = 1.20
|
expect(build :product, name: @product.name).to_not be_valid
|
||||||
@product.save
|
end
|
||||||
expect(@product.reload.price).to eq(1.20)
|
end
|
||||||
expect(@product.reload.price_cents).to eq(120)
|
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -55,11 +70,13 @@ describe Product do
|
||||||
it 'should be positive' do
|
it 'should be positive' do
|
||||||
@product.stock = -5
|
@product.stock = -5
|
||||||
expect(@product).to_not be_valid
|
expect(@product).to_not be_valid
|
||||||
|
@product.stock = 0
|
||||||
|
expect(@product).to be_valid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'calories' do
|
describe 'calories' do
|
||||||
it 'should not be present' do
|
it 'does not have to be present' do
|
||||||
@product.calories = nil
|
@product.calories = nil
|
||||||
expect(@product).to be_valid
|
expect(@product).to be_valid
|
||||||
end
|
end
|
||||||
|
@ -70,9 +87,28 @@ describe Product do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'avatar should be present' do
|
describe 'avatar' do
|
||||||
@product.avatar = nil
|
it 'should be present' do
|
||||||
expect(@product).to_not be_valid
|
@product.avatar = nil
|
||||||
|
expect(@product).to_not be_valid
|
||||||
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -6,11 +6,6 @@
|
||||||
# created_at :datetime
|
# created_at :datetime
|
||||||
# updated_at :datetime
|
# updated_at :datetime
|
||||||
# remember_created_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
|
# admin :boolean
|
||||||
# dagschotel_id :integer
|
# dagschotel_id :integer
|
||||||
# avatar_file_name :string
|
# avatar_file_name :string
|
||||||
|
@ -32,4 +27,23 @@ describe User do
|
||||||
it 'has a valid factory' do
|
it 'has a valid factory' do
|
||||||
expect(@user).to be_valid
|
expect(@user).to be_valid
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Reference in a new issue