This commit is contained in:
benji 2015-09-14 20:26:16 +02:00
parent de1436f5c7
commit b55ba47209
21 changed files with 102 additions and 97 deletions

View file

@ -0,0 +1,24 @@
module Statistics
extend ActiveSupport::Concern
def products_group_by_category
products
.select("products.category", "sum(order_items.count) as count")
.group(:category)
end
def all_orders page
orders
.order(:created_at)
.reverse_order
.paginate(page: page)
end
def products_group_by_id
products
.select("products.*", "sum(order_items.count) as count")
.group(:product_id)
.order("count")
.reverse_order
end
end

View file

@ -1,13 +1,12 @@
class OrdersController < ApplicationController class OrdersController < ApplicationController
include ActionView::Helpers::NumberHelper
include ApplicationHelper
load_resource :user load_resource :user
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 = (@user.products.for_sale.select("products.*", "sum(order_items.count) as count").group(:product_id).order("count desc") | Product.for_sale)
@order.g_order_items products products.each do |p|
@order.order_items.build(product: p)
end
end end
def create def create
@ -29,18 +28,6 @@ class OrdersController < ApplicationController
@users = User.members.publik.order(:name) @users = User.members.publik.order(:name)
end end
def quickpay
user = User.find(params[:id])
order = user.orders.build
order.order_items << OrderItem.new(count: 1, product: user.dagschotel, order: order)
if order.save
flash[:success] = "Quick pay succeeded. #{view_context.link_to("Undo", [user, order], method: :delete)}."
else
flash[:error] = order.errors.full_messages.first
end
redirect_to root_path
end
private private
def order_params def order_params

View file

@ -4,11 +4,9 @@ class ProductsController < ApplicationController
respond_to :html, :js respond_to :html, :js
def new def new
@product = Product.new
end end
def create def create
@product = Product.new(product_params)
if @product.save if @product.save
flash[:success] = "Product created!" flash[:success] = "Product created!"
redirect_to products_path redirect_to products_path
@ -25,12 +23,10 @@ class ProductsController < ApplicationController
end end
def edit def edit
@product = Product.find(params[:id])
respond_with @product respond_with @product
end end
def update def update
@product = Product.find(params[:id])
@product.update_attributes product_params @product.update_attributes product_params
respond_with @product respond_with @product
end end

View file

@ -2,7 +2,6 @@ class StocksController < ApplicationController
load_and_authorize_resource load_and_authorize_resource
def new def new
@stock = Stock.new
Product.all.each do |p| Product.all.each do |p|
@stock.stock_entries << Stock::StockEntry.new(product: p) @stock.stock_entries << Stock::StockEntry.new(product: p)
end end

View file

@ -1,11 +1,8 @@
class UsersController < ApplicationController class UsersController < ApplicationController
load_and_authorize_resource load_and_authorize_resource
before_action :init, only: [:show, :edit, :update] before_action :init, only: :show
def show def show
@categories = @user.products
.select("products.category", "sum(order_items.count) as count")
.group(:category)
end end
def edit def edit
@ -26,15 +23,12 @@ class UsersController < ApplicationController
end end
def destroy def destroy
user = User.find(params[:id]) @user.destroy
user.destroy
flash[:success] = "Succesfully removed user" flash[:success] = "Succesfully removed user"
redirect_to users_path redirect_to users_path
end end
def edit_dagschotel def edit_dagschotel
@user = User.find(params[:id])
authorize! :update_dagschotel, @user
@dagschotel = @user.dagschotel @dagschotel = @user.dagschotel
@products = Product.for_sale @products = Product.for_sale
@ -42,14 +36,22 @@ class UsersController < ApplicationController
end end
def update_dagschotel def update_dagschotel
user = User.find(params[:id]) @user.dagschotel = Product.find(params[:product_id])
authorize! :update_dagschotel, user @user.save
user.dagschotel = Product.find(params[:product_id])
user.save
flash[:success] = "Succesfully updated dagschotel" flash[:success] = "Succesfully updated dagschotel"
redirect_to user redirect_to @user
end
def quickpay
order = @user.orders.build
order.order_items.build(count: 1, product: user.dagschotel)
if order.save
flash[:success] = "Quick pay succeeded. #{view_context.link_to("Undo", [user, order], method: :delete)}."
else
flash[:error] = order.errors.full_messages.first
end
redirect_to root_path
end end
private private
@ -60,14 +62,5 @@ class UsersController < ApplicationController
def init def init
@user = User.find_by_id(params[:id]) || current_user @user = User.find_by_id(params[:id]) || current_user
@orders = @user.orders
.order(:created_at)
.reverse_order
.paginate(page: params[:page])
@products = @user.products
.select("products.*", "sum(order_items.count) as count")
.group(:product_id)
.order("count")
.reverse_order
end end
end end

View file

@ -1,2 +0,0 @@
module CallbacksHelper
end

View file

@ -1,2 +0,0 @@
module SessionsHelper
end

View file

@ -1,2 +0,0 @@
module StockHelper
end

View file

@ -1,2 +0,0 @@
module WelcomeHelper
end

View file

@ -2,8 +2,18 @@ TabApiJob = Struct.new(:order_id) do
def perform(*args) def perform(*args)
order = Order.find_by(id: order_id) order = Order.find_by(id: order_id)
if order && !order.transaction_id if order && !order.transaction_id
body = { transaction: { debtor: order.user.uid, cents: order.price_cents, message: order.to_sentence, id_at_client: order.id } } body = {
headers = { "Authorization" => "Token token=#{Rails.application.secrets.tab_api_key}" } transaction: {
debtor: order.user.uid,
cents: order.price_cents,
message: order.to_sentence,
id_at_client: order.id
}
}
headers = {
"Authorization" => "Token token=#{Rails.application.secrets.tab_api_key}"
}
result = HTTParty.post("https://zeus.ugent.be/tab/transactions", body: body, headers: headers ) result = HTTParty.post("https://zeus.ugent.be/tab/transactions", body: body, headers: headers )
order.update_attribute(:transaction_id, JSON.parse(result.body)["id"].to_i) order.update_attribute(:transaction_id, JSON.parse(result.body)["id"].to_i)
end end

View file

@ -2,18 +2,21 @@ class Ability
include CanCan::Ability include CanCan::Ability
def initialize(user) def initialize(user)
user ||= User.new # guest user (not logged in) return unless user
if user.admin? if user.admin?
can :manage, :all can :manage, :all
elsif user.koelkast? elsif user.koelkast?
can :manage, Order can :manage, Order
elsif user[:id] else
can :read, :all can :read, :all
can :manage, User, id: user.id can :manage, User, id: user.id
can :manage, Order do |order| can :create, Order do |order|
order.try(:user) == user order.try(:user) == user
end end
can :delete, Order do |order|
order.try(:user) == user && order.created_at > Rails.application.config.call_api_after.ago
end
end end
end end
end end

View file

@ -10,7 +10,6 @@
# transaction_id :integer # transaction_id :integer
# #
require 'httparty'
class Order < ActiveRecord::Base class Order < ActiveRecord::Base
include ActionView::Helpers::TextHelper include ActionView::Helpers::TextHelper
@ -20,11 +19,12 @@ class Order < ActiveRecord::Base
before_validation :calculate_price before_validation :calculate_price
before_save { |o| o.order_items = o.order_items.reject{ |oi| oi.count == 0 } } before_save { |o| o.order_items = o.order_items.reject{ |oi| oi.count == 0 } }
after_create { Delayed::Job.enqueue TabApiJob.new(id) } after_create :create_api_job
validates :user, presence: true validates :user, presence: true
validates :order_items, presence: true, in_stock: true validates :order_items, presence: true
validates :price_cents, presence: true validates :price_cents, presence: true
validates_associated :order_items
accepts_nested_attributes_for :order_items accepts_nested_attributes_for :order_items
@ -34,15 +34,17 @@ class Order < ActiveRecord::Base
}.to_sentence }.to_sentence
end end
def g_order_items(products)
products.each do |p|
self.order_items.build(product: p)
end
end
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.price_cents }.sum
end end
def create_api_job
priority = 0
run_at = Rails.application.config.call_api_after.from_now
job = TabApiJob.new(id)
Delayed::Job.enqueue job, priority, run_at
end
end end

View file

@ -13,25 +13,31 @@ class OrderItem < ActiveRecord::Base
belongs_to :product belongs_to :product
validates :product, presence: true validates :product, presence: true
validates :count, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :count, presence: true, numericality: { only_integer: true,
less_than_or_equal_to: ->(oi) { oi.product.stock },
greater_than_or_equal_to: 0 }
before_destroy :put_back_in_stock before_destroy :put_back_in_stock!
after_create :remove_from_stock after_create :remove_from_stock!
accepts_nested_attributes_for :product accepts_nested_attributes_for :product
def product_attributes=(attributes) def product_attributes=(attributes)
self.product = Product.find(attributes[:id]) self.product = OrderItem.products.select{ |p| p.id == attributes[:id].to_i }.first
super super
end end
def self.products
@products || Product.all
end
private private
def remove_from_stock def remove_from_stock!
product.decrement!(:stock, count) product.decrement!(:stock, count)
end end
def put_back_in_stock def put_back_in_stock!
product.increment!(:stock, self.count) product.increment!(:stock, self.count)
end end
end end

View file

@ -42,7 +42,7 @@ class Product < ActiveRecord::Base
self.price_cents = (value.to_f * 100).to_int self.price_cents = (value.to_f * 100).to_int
end end
def out_of_sale def take_out_of_sale!
update_attribute :deleted, true update_attribute :deleted, true
end end
end end

View file

@ -27,6 +27,8 @@
require 'identicon' require 'identicon'
class User < ActiveRecord::Base class User < ActiveRecord::Base
include Statistics
devise :database_authenticatable, :trackable, :omniauthable, :omniauth_providers => [:zeuswpi] devise :database_authenticatable, :trackable, :omniauthable, :omniauth_providers => [:zeuswpi]
has_paper_trail has_paper_trail

View file

@ -1,9 +0,0 @@
class InStockValidator < ActiveModel::Validator
def validate(record)
p_short = []
record.order_items.each do |oi|
p_short.append oi.product.name if oi.count > oi.product.stock
end
record.errors.add(:base, "There is not enough stock for your order of the following products: #{p_short.join(', ')}") if p_short.size > 0
end
end

View file

@ -8,7 +8,6 @@
<%= order.to_sentence %> <%= order.to_sentence %>
</td> </td>
<td> <td>
<% p order %>
<%= euro_from_cents(order.price_cents) %> <%= euro_from_cents(order.price_cents) %>
</td> </td>
</tr> </tr>

View file

@ -11,7 +11,7 @@
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item text-muted">Orders</li> <li class="list-group-item text-muted">Orders</li>
<li class="list-group-item"><strong>Orders placed</strong><span class="badge"><%= @user.orders_count %></span></li> <li class="list-group-item"><strong>Orders placed</strong><span class="badge"><%= @user.orders_count %></span></li>
<li class="list-group-item"><strong>Products ordered</strong><span class="badge"><%= @products.map(&:count).sum %></span></li> <li class="list-group-item"><strong>Products ordered</strong><span class="badge"><%= @user.products_group_by_id.map(&:count).sum %></span></li>
<% if can? :create, @user.orders.build %> <% if can? :create, @user.orders.build %>
<li class="list-group-item"><%= link_to "Place new order", new_user_order_path(@user), class: "btn btn-default btn-block" %></li> <li class="list-group-item"><%= link_to "Place new order", new_user_order_path(@user), class: "btn btn-default btn-block" %></li>
<% end %> <% end %>

View file

@ -2,25 +2,25 @@
<div class="row"> <div class="row">
<%= render 'sidebar' %> <%= render 'sidebar' %>
<div id="user_info" class="col-sm-9"> <div id="user_info" class="col-sm-9">
<% if @orders.any? %> <% if (all_orders = @user.all_orders(params[:page])).any? %>
<h4>Previously ordered</h4> <h4>Previously ordered</h4>
Total: Total:
<ul> <ul>
<li> <li>
<%= @categories.map{|c| pluralize(c.count, c.category)}.to_sentence %> <%= @user.products_group_by_category.map{|c| pluralize(c.count, c.category)}.to_sentence %>
</li> </li>
</ul> </ul>
Specifics: Specifics:
<%= content_tag :ul do %> <%= content_tag :ul do %>
<% @products.each do |p| %> <% @user.products_group_by_id.each do |p| %>
<%= content_tag :li, pluralize(p.count, p.name) %> <%= content_tag :li, pluralize(p.count, p.name) %>
<% end %> <% end %>
<% end %> <% end %>
<h4>All orders (<%= @user.orders_count %>)</h4> <h4>All orders (<%= @user.orders_count %>)</h4>
<table class="orders"><%= render @orders %></table> <table class="orders"><%= render all_orders %></table>
<%= will_paginate @orders %> <%= will_paginate all_orders %>
</div> </div>
<% end %> <% end %>
</div> </div>

View file

@ -21,5 +21,6 @@ module Tab002
# config.i18n.default_locale = :de # config.i18n.default_locale = :de
config.active_record.raise_in_transactional_callbacks = true config.active_record.raise_in_transactional_callbacks = true
config.active_job.queue_adapter = :delayed_job config.active_job.queue_adapter = :delayed_job
config.call_api_after = 5.minutes
end end
end end

View file

@ -18,17 +18,17 @@ Rails.application.routes.draw do
end end
end end
resources :users do resources :users, only: [:show, :edit, :update, :destroy] do
resources :orders resources :orders, only: [:new, :create, :destroy]
member do member do
get 'quickpay' => 'orders#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' get 'dagschotel/:product_id' => 'users#update_dagschotel', as: 'dagschotel'
end end
end end
resources :products resources :products, only: [:new, :create, :index, :edit, :update]
resources :stocks resources :stocks, only: [:new, :create]
get 'overview' => 'orders#overview', as: "orders" get 'overview' => 'orders#overview', as: "orders"
end end