Make requests for money

This commit is contained in:
benji 2017-01-09 15:46:43 +01:00
parent cc12ad3b7a
commit 4748625502
21 changed files with 194 additions and 10 deletions

View file

@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

View file

@ -0,0 +1,3 @@
// Place all the styles related to the requests controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View file

@ -5,5 +5,4 @@ class PagesController < ApplicationController
def landing def landing
@statistics = Statistics.new @statistics = Statistics.new
end end
end end

View file

@ -0,0 +1,26 @@
class RequestsController < ApplicationController
load_and_authorize_resource :user, only: :index
before_action :load_request, only: [:confirm, :decline]
authorize_resource :request, only: [:confirm, :decline]
def index
@requests = User.find(params[:user_id]).incoming_requests.group_by(&:status)
end
def confirm
@request.confirm!
redirect_to user_requests_path(@request.debtor)
end
def decline
@request.decline!
redirect_to user_requests_path(@request.debtor)
end
private
def load_request
@request = Request.find params[:request_id]
end
end

View file

@ -9,14 +9,23 @@ class TransactionsController < ApplicationController
def create def create
@transaction = Transaction.new(transaction_params) @transaction = Transaction.new(transaction_params)
@transaction.reverse if @transaction.amount < 0 @transaction.reverse if @transaction.amount < 0
authorize!(:create, @transaction)
if can? :create, @transaction
if @transaction.save if @transaction.save
render json: @transaction, status: :created render json: @transaction, status: :created
else else
render json: @transaction.errors.full_messages, render json: @transaction.errors.full_messages,
status: :unprocessable_entity status: :unprocessable_entity
end end
else
request = Request.new @transaction.info
if request.save
render json: request, status: :created
else
render json: request.errors.full_messages,
status: :unprocessable_entity
end
end
end end
private private

View file

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

33
app/models/request.rb Normal file
View file

@ -0,0 +1,33 @@
class Request < ActiveRecord::Base
belongs_to :debtor, class_name: 'User'
belongs_to :creditor, class_name: 'User'
belongs_to :issuer, polymorphic: true
validates :amount, numericality: { greater_than: 0 }
validate :different_debtor_creditor
enum status: [:open, :confirmed, :declined]
def confirm!
return unless open?
Transaction.create attributes.symbolize_keys.extract!(
:debtor_id, :creditor_id, :issuer_id, :issuer_type, :amount, :message
)
update_attributes status: :confirmed
end
def decline!
return unless open?
update_attributes status: :declined
end
private
def different_debtor_creditor
if self.debtor == self.creditor
self.errors.add :base, "Can't write money to yourself"
end
end
end

View file

@ -42,6 +42,10 @@ class Transaction < ActiveRecord::Base
self.amount *= -1 self.amount *= -1
end end
def info
attributes.symbolize_keys.extract!(:debtor_id, :creditor_id, :issuer_id, :issuer_type, :message, :amount)
end
private private
def recalculate_balances def recalculate_balances

View file

@ -18,6 +18,10 @@ class User < ActiveRecord::Base
class_name: 'Transaction', foreign_key: 'creditor_id' class_name: 'Transaction', foreign_key: 'creditor_id'
has_many :outgoing_transactions, has_many :outgoing_transactions,
class_name: 'Transaction', foreign_key: 'debtor_id' class_name: 'Transaction', foreign_key: 'debtor_id'
has_many :incoming_requests,
class_name: 'Request', foreign_key: 'debtor_id'
has_many :outgoing_requests,
class_name: 'Request', foreign_key: 'debtor_id'
has_many :issued_transactions, as: :issuer, class_name: 'Transaction' has_many :issued_transactions, as: :issuer, class_name: 'Transaction'

View file

@ -6,6 +6,7 @@ class UserAbility
can :manage, :all if user.penning? can :manage, :all if user.penning?
can :read, user, id: user.id can :read, user, id: user.id
can :manage, Request, user_id: user.id
can :create, Transaction do |t| can :create, Transaction do |t|
t.debtor == user && t.amount <= Rails.application.config.maximum_amount t.debtor == user && t.amount <= Rails.application.config.maximum_amount
end end

View file

@ -17,6 +17,8 @@
- if current_user.penning - if current_user.penning
%li.pure-menu-item %li.pure-menu-item
=link_to "Zeus", User.zeus, class: "pure-menu-link" =link_to "Zeus", User.zeus, class: "pure-menu-link"
%li.pure-menu-item
= link_to 'Requests', user_requests_path(current_user), class: 'pure-menu-link'
.pure-u-1 .pure-u-1
= render 'partials/flash' = render 'partials/flash'
= yield = yield

View file

@ -0,0 +1,25 @@
%h4= title
%table.pure-table
%thead
%tr
%th Peer
%th Issuer
%th Amount
%th Message
- if actions
%th Accept
%th Decline
%tbody
- (requests || []).each do |r|
%tr
%td= r.creditor.name
%td= r.issuer.name
%td= "€#{r.amount/100.0}"
%td= r.message
- if actions
%td
= link_to request_confirm_path(r), method: :post do
%span.glyphicon.glyphicon-ok
%td
= link_to request_decline_path(r), method: :post do
%span.glyphicon.glyphicon-remove

View file

@ -0,0 +1,3 @@
= render 'index', actions: true, title: 'Open Requests', requests: @requests['open']
= render 'index', actions: false, title: 'Confirmed Requests', requests: @requests['confirmed']
= render 'index', actions: false, title: 'Declined Requests', requests: @requests['declined']

View file

@ -13,7 +13,7 @@
%span.input-group-addon %span.input-group-addon
%span.glyphicon.glyphicon-euro %span.glyphicon.glyphicon-euro
= f.number_field :euros, value: amount(@transaction.amount), = f.number_field :euros, value: amount(@transaction.amount),
placeholder: "Amount", step: 0.01, min: (0.01 unless current_user.penning), placeholder: "Amount", step: 0.01,
class: "form-control", size: 20, required: true, class: "form-control", size: 20, required: true,
max: (Rails.application.config.maximum_amount/100 unless current_user.penning) max: (Rails.application.config.maximum_amount/100 unless current_user.penning)
= f.submit "Send it!", class: "pure-button pure-button-primary btn" = f.submit "Send it!", class: "pure-button pure-button-primary btn"

View file

@ -6,7 +6,12 @@ Rails.application.routes.draw do
root to: 'pages#landing' root to: 'pages#landing'
resources :transactions, only: [:index, :create] resources :transactions, only: [:index, :create]
resources :users, only: [:show, :index] resources :users, only: [:index, :show] do
resources :requests, only: [:index], shallow: true do
post :confirm
post :decline
end
end
get 'datatables/:id' => 'datatables#transactions_for_user', as: "user_transactions" get 'datatables/:id' => 'datatables#transactions_for_user', as: "user_transactions"
end end

View file

@ -0,0 +1,18 @@
class CreateRequests < ActiveRecord::Migration
def change
create_table :requests do |t|
t.references :debtor, index: true, null: false
t.references :creditor, index: true, null: false
t.references :issuer, polymorphic: true, index: true, null: false
t.integer :amount, null: false, default: 0
t.string :message
t.integer :status, default: 0
t.timestamps null: false
end
add_foreign_key :request, :users, column: :creditor_id
add_foreign_key :request, :users, column: :debtor_id
end
end

View file

@ -11,7 +11,7 @@
# #
# 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: 20150914095049) do ActiveRecord::Schema.define(version: 20170109123717) do
create_table "clients", force: :cascade do |t| create_table "clients", force: :cascade do |t|
t.string "name", null: false t.string "name", null: false
@ -23,6 +23,22 @@ ActiveRecord::Schema.define(version: 20150914095049) do
add_index "clients", ["key"], name: "index_clients_on_key" add_index "clients", ["key"], name: "index_clients_on_key"
add_index "clients", ["name"], name: "index_clients_on_name" add_index "clients", ["name"], name: "index_clients_on_name"
create_table "requests", force: :cascade do |t|
t.integer "debtor_id", null: false
t.integer "creditor_id", null: false
t.integer "issuer_id", null: false
t.string "issuer_type", null: false
t.integer "amount", default: 0, null: false
t.string "message"
t.integer "status", default: 0
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "requests", ["creditor_id"], name: "index_requests_on_creditor_id"
add_index "requests", ["debtor_id"], name: "index_requests_on_debtor_id"
add_index "requests", ["issuer_type", "issuer_id"], name: "index_requests_on_issuer_type_and_issuer_id"
create_table "transactions", force: :cascade do |t| create_table "transactions", force: :cascade do |t|
t.integer "debtor_id", null: false t.integer "debtor_id", null: false
t.integer "creditor_id", null: false t.integer "creditor_id", null: false

View file

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe RequestsController, type: :controller do
end

View file

@ -0,0 +1,6 @@
FactoryGirl.define do
factory :request do
end
end

View file

@ -0,0 +1,15 @@
require 'rails_helper'
# Specs in this file have access to a helper object that includes
# the RequestsHelper. For example:
#
# describe RequestsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
RSpec.describe RequestsHelper, type: :helper do
pending "add some examples to (or delete) #{__FILE__}"
end

View file

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Request, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end