Mail job queue with Sidekiq + negociation mailing + offers

This commit is contained in:
Nicolas VARROT 2015-12-09 20:51:37 +01:00
parent 9dc8ad2394
commit b3a37cd78f
30 changed files with 314 additions and 151 deletions

View File

@ -1,10 +1,11 @@
source 'https://rubygems.org'
#gem 'activerecord-session_store'
gem 'rails', '4.2.0'
gem "mysql2"
gem "mysql2", group: :mysql
gem 'sass-rails', '~> 5.0'
@ -71,4 +72,4 @@ gem "paranoia", "~> 2.0"
gem 'workflow', '~> 1.2.0'
gem 'elasticsearch-model', git: 'git://github.com/elasticsearch/elasticsearch-rails.git'
gem 'sidekiq'

View File

@ -1,12 +1,3 @@
GIT
remote: git://github.com/elasticsearch/elasticsearch-rails.git
revision: 5f32e484a6d0458f26dc9acfa9dd5fed4e5d6453
specs:
elasticsearch-model (0.1.8)
activesupport (> 3)
elasticsearch (> 0.4)
hashie
GEM
remote: https://rubygems.org/
specs:
@ -76,20 +67,12 @@ GEM
execjs
coffee-script-source (1.9.1)
columnize (0.9.0)
concurrent-ruby (1.0.0)
connection_pool (2.2.0)
debug_inspector (0.0.2)
debugger-linecache (1.2.0)
elasticsearch (1.0.14)
elasticsearch-api (= 1.0.14)
elasticsearch-transport (= 1.0.14)
elasticsearch-api (1.0.14)
multi_json
elasticsearch-transport (1.0.14)
faraday
multi_json
erubis (2.7.0)
execjs (2.3.0)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
formtastic (2.3.0)
actionpack (>= 3.0)
formtastic-bootstrap (3.0.0)
@ -106,7 +89,6 @@ GEM
haml (>= 3.1, < 5.0)
html2haml (>= 1.0.1)
railties (>= 4.0.1)
hashie (3.4.3)
highline (1.7.2)
hike (1.2.3)
html2haml (2.0.0)
@ -138,7 +120,6 @@ GEM
mini_portile (0.6.2)
minitest (5.5.1)
multi_json (1.10.1)
multipart-post (2.0.0)
mysql2 (0.3.18)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
@ -182,6 +163,7 @@ GEM
rake (10.4.2)
rdoc (4.2.0)
json (~> 1.4)
redis (3.2.2)
rmagick (2.13.4)
ruby_parser (3.6.4)
sexp_processor (~> 4.1)
@ -198,6 +180,11 @@ GEM
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
sexp_processor (4.4.5)
sidekiq (4.0.1)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
json (~> 1.0)
redis (~> 3.2, >= 3.2.1)
slop (3.6.0)
spring (1.3.2)
sprockets (2.12.3)
@ -249,7 +236,6 @@ DEPENDENCIES
capistrano (= 2.15.5)
carrierwave
coffee-rails (~> 4.1.0)
elasticsearch-model!
formtastic (= 2.3.0)
formtastic-bootstrap
geocoder
@ -266,6 +252,7 @@ DEPENDENCIES
rvm-capistrano (= 1.4.1)
sass-rails (~> 5.0)
sdoc (~> 0.4.0)
sidekiq
spring
turbolinks
twitter-bootstrap-rails

View File

@ -25,7 +25,7 @@ class Admin::CustomersController < ApplicationController
@customer.account_validated = true
@customer.account_validated_at = Time.now
@customer.save
CustomerMailer.validate_account(@customer).deliver
CustomerMailer.delay.validate_account(@customer)
redirect_to :back
end

View File

@ -106,7 +106,6 @@ class Admin::NeedsController < ApplicationController
@need = Need.find(params[:id])
if @need.validate!
flash[:notice] = "Besoin validé avec succès"
CustomerMailer.validate_need(@need).deliver
else
flash[:error] = "L'état actuel de ce besoin ne permet pas sa validation"
end
@ -117,13 +116,33 @@ class Admin::NeedsController < ApplicationController
@need = Need.find(params[:id])
if @need.refuse!
flash[:notice] = "Besoin refusé avec succès"
CustomerMailer.refuse_need(@need).deliver
else
flash[:error] = "L'état actuel de ce besoin ne permet son refus"
end
redirect_to admin_needs_path
end
def negociate
@need = Need.find(params[:id])
if @need.negociate!
flash[:notice] = "Le besoin est maintenant en cours de négociation"
else
flash[:error] = "L'état actuel de ce besoin ne permet pas cette action"
end
redirect_to admin_needs_path
end
def reject
@need = Need.find(params[:id])
if @need.reject!
flash[:notice] = "Le besoin est maintenant en négociation échouée"
else
flash[:error] = "L'état actuel de ce besoin ne permet pas cette action"
end
redirect_to admin_needs_path
end
private
def need_params

View File

@ -0,0 +1,38 @@
class Admin::NeedsController < ApplicationController
layout "admin"
before_filter :auth_admin
before_action :build_category_tree, only:[:new, :update, :create, :edit, :index]
def index
end
def new
def create
end
def edit
end
def update
end
def destroy
end
private
def order_params
params.require(:order).permit(:title, :description, :category_id, :author_id)
end
end

View File

@ -3,30 +3,20 @@ class ApplicationController < ActionController::Base
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def auth_customer
session[:devise_id] = params[:d] if params[:d]
if !current_customer
session[:before_auth_url] = request.url
redirect_to new_public_customers_auth_path(:p => params[:p], :for_annonce => (true if params[:controller] == "public/annonces"))
end
end
private
def auth_admin
if !current_admin
redirect_to new_admin_admin_auth_path
end
end
@ -64,6 +54,9 @@ class ApplicationController < ActionController::Base
end
def redirect_back_or_default(default = root_path, options = {})
redirect_to (request.referer.present? ? :back : default), options
end

View File

@ -33,7 +33,6 @@ class Public::CustomersAuthsController < ApplicationController
elsif params[:p] and @parent = Customer.find_parrain(params[:p])
@customer.parent_code = @parent.mlm_token.upcase
end
params[:step] = "login"
@ -51,15 +50,11 @@ class Public::CustomersAuthsController < ApplicationController
user.last_sign_in_at = Time.now
user.save(:validate => false)
if session[:for_annonce]
redirect_to new_public_annonce_path
else
redirect_to public_my_account_path
end
if session[:for_annonce]
redirect_to new_public_annonce_path
else
redirect_to :root
end
else
flash.now.alert = "Email ou mot de passe incorect"

View File

@ -63,8 +63,8 @@ class Public::CustomersController < ApplicationController
@customer = Customer.new(params.require(:customer).permit!)
if @customer.save
CustomerMailer.confirm(@customer).deliver
CustomerMailer.notify_ins(@customer).deliver
CustomerMailer.delay.confirm(@customer)
CustomerMailer.delay.notify_ins(@customer)
@customer.authenticate(params[:password])
@ -89,7 +89,7 @@ class Public::CustomersController < ApplicationController
@customer.enabled = true
@customer.save(:validate => false)
CustomerMailer.confirm_ins(@customer).deliver
CustomerMailer.delay.confirm_ins(@customer)
cookies[:customer_auth_token] = @customer.token

View File

@ -43,7 +43,7 @@ class Public::MyAccountController < ApplicationController
def reconfirm
@no_search = true
CustomerMailer.confirm(current_customer).deliver
CustomerMailer.delay.confirm(current_customer)
redirect_to public_my_account_path, :notice => "Le mail vous a été renvoyé"
end

View File

@ -4,6 +4,7 @@ class Public::NeedsController < ApplicationController
before_filter :auth_customer
before_filter :build_category_tree, only:[:index,:new,:create,:edit,:update]
before_filter :check_owner, only: [:destroy,:edit,:update]
def index
@ -99,11 +100,7 @@ class Public::NeedsController < ApplicationController
if @need.save
flash[:notice] = "Votre besoin à été créé avec succès."
# Find all admins with emails
admins = Admin.where.not(email: nil)
admins.each do |admin|
AdminMailer.new_need(admin, @need).deliver
end
redirect_to public_my_account_path
else
render :action => "new"
@ -135,4 +132,12 @@ class Public::NeedsController < ApplicationController
params.require(:need).permit(:title, :description, :category_id)
end
def check_owner
@need = Need.find(params[:id])
if !@need.author or @need.author.id != current_customer.id
flash[:error] = "Ce besoin ne vous appartient pas"
redirect_back_or_default :root
end
end
end

View File

@ -47,6 +47,12 @@ class CustomerMailer < ApplicationMailer
mail to: @customer.email, :subject => "Proposition de besoin refusée"
end
def negociate_need(need, customer)
@need = need
@customer = customer
mail to: @customer.email, :subject => "Négociation en cours !"
end
def new_user(customer)
@customer = customer
@parent = @customer.parent

View File

@ -30,7 +30,7 @@ class Admin < ActiveRecord::Base
generate_token(:reset_password_token)
self.reset_password_sent_at = Time.now
save!
AdminMailer.password_reset(self).deliver
AdminMailer.delay.password_reset(self)
end
def generate_token(column)

View File

@ -176,7 +176,7 @@ class Customer < ActiveRecord::Base
self.save(:validate => false)
CustomerMailer.password_reset(self).deliver
CustomerMailer.delay.password_reset(self)
end

View File

@ -1,4 +1,3 @@
require 'elasticsearch/model'
class Need < ActiveRecord::Base
include Workflow
@ -21,6 +20,7 @@ class Need < ActiveRecord::Base
where('title LIKE ?', "%#{search}%")
}
after_create :create
workflow_column :state
@ -54,17 +54,62 @@ class Need < ActiveRecord::Base
state :failed
end
def create
if self.author
# Find all admins with email
admins = Admin.where.not(email: nil)
admins.each do |admin|
AdminMailer.delay.new_need(admin, self)
end
end
end
def validate
if self.author
CustomerMailer.delay.validate_need(self)
end
end
def negociate
customers = self.customers
customers.each do |customer|
CustomerMailer.delay.negociate_need(self, customer)
end
end
def accept
customers = self.customers
customers.each do |customer|
CustomerMailer.delay.accept_need(self, customer)
end
end
def reject
customers = self.customers
customers.each do |customer|
CustomerMailer.delay.reject_need(self, customer)
end
end
def refuse
if self.author
CustomerMailer.delay.refuse_need(self)
end
end
# Human state conversion
def human_state
case state
when 'created'
"En attente de validation"
when 'verified'
"Pas encore négocié"
"Validé"
when 'refused'
"Refusé"
when 'negociating'
"En cours de negociation"
"En gociation"
when 'negociated'
"Négociation effecutée"
when 'failed'

2
app/models/offer.rb Normal file
View File

@ -0,0 +1,2 @@
class Offer < ActiveRecord::Base
end

View File

@ -18,7 +18,7 @@
=link_to customer.email, "mailto:#{customer.email}"
%td.actions{:style => "width:150px;"}
%td.actions{:style => "width:150px;text-align:right;"}
= link_to i(:"trash-o"), [:admin, customer], :data => {:confirm => 'Voulez-vous vraiment supprimer ce compte utilisateur ?'}, :method => :delete, :remote => true
=# link_to i(:eye), [:admin, customer]
= link_to i(:pencil), edit_admin_customer_path(customer)

View File

@ -21,7 +21,7 @@
%th{:style => "width:100px"}
%th{:style => "width:100px;text-align:right;"}
&nbsp;

View File

@ -1,4 +1,6 @@
-css_class = "warning" if need.negociating?
-css_class = "error" if need.failed?
-css_class = "success" if need.negociated?
%tr{:id => need.id}
%td
=link_to need.title, edit_admin_need_path(need)
@ -16,9 +18,15 @@
=link_to i(:"hand-paper-o") + " " + need.wishes.length.to_s, admin_need_wishes_path(need)
&nbsp;&nbsp;
=link_to i(:"comment-o") + " " + need.messages.length.to_s, admin_need_messages_path(need)
%td{style: 'text-align:center' }
%td{class: css_class}
=need.human_state
%td.actions{:style => "width:150px;text-align:right"}
= link_to i(:"trash-o"), [:admin, need], :data => {:confirm => 'Voulez-vous vraiment supprimer ce besoin ?'}, :method => :delete
-if(need.verified?)
= link_to i(:"comments"), negociate_admin_need_path(need), title: "Passer en négociation", :data => {:confirm => 'Voulez-vous vraiment passer ce besoin en négociation'}
-if(need.negociating?)
= link_to i(:"thumbs-up"), accept_admin_need_path(need), title: "Passer en négocié et créer une offre", :data => {:confirm => 'Voulez-vous vraiment passer ce besoin en négocié ?'}
= link_to i(:"thumbs-down"), reject_admin_need_path(need), title: "Passer en négociation échouée", :data => {:confirm => 'Voulez-vous vraiment passer ce besoin en négociation échouée'}
= link_to i(:"trash"), [:admin, need], :data => {:confirm => 'Voulez-vous vraiment supprimer ce besoin ?'}, :method => :delete
= link_to i(:pencil), edit_admin_need_path(need)
-if(need.created?)
= link_to i(:remove), refuse_admin_need_path(need), title: "Refuser", :data => {:confirm => 'Voulez-vous vraiment refuser ce besoin ?'}
= link_to i(:check), validate_admin_need_path(need), title: "Valider", :data => {:confirm => 'Voulez-vous vraiment valider ce besoin ?'}

View File

@ -15,8 +15,9 @@
Il y a #{time_ago_in_words( need.created_at)}
%td.actions{:style => "width:150px;text-align:right"}
= link_to i(:"trash-o"), [:admin, need], :data => {:confirm => 'Voulez-vous vraiment supprimer ce besoin ?'}, :method => :delete
= link_to i(:pencil), edit_admin_need_path(need)
-if(need.created?)
= link_to i(:remove), refuse_admin_need_path(need), title: "Refuser", :data => {:confirm => 'Voulez-vous vraiment refuser ce besoin ?'}
= link_to i(:check), validate_admin_need_path(need), title: "Valider", :data => {:confirm => 'Voulez-vous vraiment valider ce besoin ?'}
= link_to i(:"trash-o"), [:admin, need], :data => {:confirm => 'Voulez-vous vraiment supprimer ce besoin ?'}, :method => :delete
= link_to i(:pencil), edit_admin_need_path(need)

View File

@ -33,7 +33,6 @@
%h2 Liste des besoins
.row
.col-md-2
= semantic_form_for :search, :html => {id: :search_form, :method => :get } do |f|
= f.inputs do
=f.input :q, :as => :search, label: "Recherche", input_html: {value: params[:q], :name => 'q' }, placeholder: "Rechercher un besoin"
@ -60,9 +59,12 @@
Catégorie
%th
Émetteur
%th{style: 'text-align:center' }
Commentaires/Intérêts
%th{style: 'text-align:center' }
Offres
%th
Statut
%th{:style => "width:100px"}
&nbsp;

View File

@ -0,0 +1,10 @@
%p Bonjour,
%p
Nous vous informons que le besoin
%strong= @need.title
est maintenant en cours de négociation.
%p Vous serez informé dès que la négociation aura abouti.
%p Merci !

View File

@ -6,7 +6,7 @@
%th
Catégorie
%th
État
État de la proposition
%th{:style => "width:100px"}
&nbsp;
%tbody

View File

@ -1,4 +1,9 @@
-css_class = 'warning' if need.created?
-css_class = 'danger' if need.refused?
-css_class = 'success' if need.verified?
-state = 'Validé' if need.verified? or need.negociated? or need.negociating? or need.failed?
-state = 'En attente de validation' if need.created?
-state = 'Refusée' if need.refused?
%tr{:id => need.id, class: css_class}
%td
@ -7,8 +12,8 @@
-if need.category
=need.category.name
%td
=need.human_state
=state
%td.actions{:style => "width:150px;text-align:right"}
-if need.created? or need.refused?
= link_to i(:"trash-o btn btn-default"), [:public, need], :data => {:confirm => 'Voulez-vous vraiment supprimer ce besoin ?'}, method: :delete
= link_to i(:"pencil btn btn-primary"), edit_public_need_path(need)
= link_to i(:"pencil btn btn-primary"), edit_public_need_path(need)

View File

@ -9,6 +9,8 @@
.pagination= paginate @needs
.row.col-md-3
.white.side-menu
=link_to "Proposer un nouveau besoin", new_public_need_path,style:'display:block', class: 'btn btn-primary'
.white.side-menu
= semantic_form_for :search, :html => {id: :search_form, :method => :get } do |f|
= f.inputs do

View File

@ -1,4 +1,4 @@
.center.padding.white
%h2
Déclaration d'un nouveau besoin
Proposer un nouveau besoin
=render :partial => "public/needs/form"

View File

@ -247,6 +247,9 @@ Rails.application.routes.draw do
member do
get :validate
get :refuse
get :negociate
get :accept
get :reject
end
end
@ -321,7 +324,7 @@ Rails.application.routes.draw do
get '*url.html' => 'public/menu_items#show', :as => :menu_item, :f => "html"
get '*url.:f' => 'public/menu_items#redirect', :f => "html"
root 'public/customers#new'
root 'public/needs#index'

View File

@ -0,0 +1,12 @@
class CreateOffers < ActiveRecord::Migration
def change
create_table :offers do |t|
t.timestamps null: false
t.float :price
t.float :fee_percentage
t.references :needs, index: true
t.string :supplier
end
end
end

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20151207162817) do
ActiveRecord::Schema.define(version: 20151209125427) do
create_table "admins", force: :cascade do |t|
t.string "name", limit: 255
@ -435,6 +435,17 @@ ActiveRecord::Schema.define(version: 20151207162817) do
t.datetime "updated_at"
end
create_table "offers", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.float "price", limit: 24
t.float "fee_percentage", limit: 24
t.integer "needs_id", limit: 4
t.string "supplier", limit: 255
end
add_index "offers", ["needs_id"], name: "index_offers_on_needs_id", using: :btree
create_table "pages", force: :cascade do |t|
t.text "title", limit: 65535
t.text "description", limit: 65535

11
test/fixtures/offers.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
# This model initially had no columns defined. If you add columns to the
# model remove the '{}' from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
one: {}
# column: value
#
two: {}
# column: value

View File

@ -0,0 +1,7 @@
require 'test_helper'
class OfferTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end