Multidocument + Contact messages

This commit is contained in:
Nicolas VARROT 2016-03-08 20:49:34 +01:00
parent a018c87cef
commit 9fbc6eae1c
43 changed files with 715 additions and 115 deletions

View File

@ -2,6 +2,12 @@ class Admin::AcceptedOffersController < ApplicationController
layout "admin"
before_filter :auth_admin
def show
@offer = Offer.find(params[:offer_id])
@accepted_offer = @offer.accepted_offers.find(params[:id])
@documents = @accepted_offer.documents
end
def upload_devis
@accepted_offer = AcceptedOffer.find(params[:id])

View File

@ -0,0 +1,39 @@
class Admin::ContactMessagesController < ApplicationController
layout "admin"
before_filter :auth_admin
def create
@contact_message = ContactMessage.new
@contact_message.assign_attributes(message_params)
@contact_message.admin = current_admin
@contact_message.read_by_admin = true
if @contact_message.save
flash[:notice] = "Commentaire envoyé."
return redirect_to :back
else
flash[:error] = "Votre commentaire n'a pas pu être envoyé."
return render 'index'
end
end
def destroy
@message = ContactMessage.find(params['id'])
if @message.destroy
flash[:notice] = "message supprimé"
else
flash[:error] = "Impossible de supprimer ce message"
end
redirect_to :back
end
def message_params
params.require(:contact_message).permit(:content, :contact_id)
end
end

View File

@ -0,0 +1,29 @@
class Admin::ConversationsController < ApplicationController
layout "admin"
before_filter :auth_admin
def index
@conversations = ContactMessage.select([
"count(CASE WHEN contact_messages.read_by_admin = FALSE THEN 1 END ) as unread_count",
"max(created_at) as last_at",
"count(contact_id) as messages_count",
"contact_messages.*"])
.group(:contact_id)
.order(created_at: :desc)
.page(params[:page])
end
def show
@customer = Customer.find(params[:id])
ContactMessage.where(contact_id: params[:id]).update_all(read_by_admin: true)
@contact_messages = ContactMessage.where('customer_id = ? OR contact_id = ?', params[:id], params[:id])
.order(created_at: :desc).page params[:page]
end
end

View File

@ -0,0 +1,113 @@
class Admin::DocumentsController < ApplicationController
layout "admin"
before_filter :auth_admin
before_filter :load_offer
def create
@accepted_offer.documents.create({title: params[:document][:title]})
if @accepted_offer.save
flash[:success] = "Document créé"
else
flash[:error] = "Impossible de créer le document"
end
redirect_to :back
end
def upload_document
if !params[:document]
flash[:error] = "Vous devez sélectionner un fichier"
else
@document = @accepted_offer.documents.find(params[:document_id])
@document.document = params[:document]
if @document.save
@document.upload_document! if @document.not_available?
@document.save
flash[:success] = "Document chargé"
else
flash[:error] = "Impossible de charger le document"
end
end
redirect_to :back
end
def upload_returned_document
if !params[:returned_document]
flash[:error] = "Vous devez sélectionner un fichier"
else
@document = @accepted_offer.documents.find(params[:document_id])
@document.returned_document = params[:returned_document]
if @document.save
@document.state = :document_returned
@document.save
flash[:success] = "Document chargé"
else
flash[:error] = "Impossible de charger le document"
end
end
redirect_to :back
end
def load_offer
@offer = Offer.find(params[:offer_id])
@accepted_offer = @offer.accepted_offers.find(params[:accepted_offer_id])
end
def download
@document = @accepted_offer.documents.find(params[:document_id])
send_file @document.document.file.path
end
def download_returned
@document = @accepted_offer.documents.find(params[:document_id])
send_file @document.returned_document.file.path
end
def delete
@document = @accepted_offer.documents.find(params[:document_id])
if @document.remove_document!
flash[:success] = "Fichier supprimé"
else
flash[:error] = "Impossible de supprimer le fichier"
end
redirect_to :back
end
def destroy
@document = @accepted_offer.documents.find(params[:document_id])
if @document.destroy
flash[:success] = "Document Supprimé"
else
flash[:error] = "Impossible de supprimer le document"
end
redirect_to :back
end
def delete_returned
@document = @accepted_offer.documents.find(params[:document_id])
if @document.remove_returned_document!
flash[:success] = "Fichier supprimé"
else
flash[:error] = "Impossible de supprimer le fichier"
end
redirect_to :back
end
def verify_returned
@document = @accepted_offer.documents.find(params[:document_id])
@document.state = :document_verified
if @document.save
flash[:success] = "Document vérifié"
else
flash[:error] = "Impossible vérifier le document"
end
redirect_to :back
end
end

View File

@ -145,7 +145,7 @@ class Admin::NeedsController < ApplicationController
def accept
@need = Need.find(params[:id])
if @need.offers.length < 1
flash[:error] = "Vous devez créer au moins une offre avant de passer ce besoin en négocié"
flash[:error] = "Vous devez créer au moins une proposition avant de passer ce besoin en négocié"
else
if @need.accept!
flash[:notice] = "Le besoin est maintenant négocié"

View File

@ -65,7 +65,7 @@ class Admin::OffersController < ApplicationController
@offer = Offer.new(offer_params)
if @offer.save
@need.offers << @offer
flash[:notice] = "Offre créée avec succès."
flash[:notice] = "Proposition créée avec succès."
@offer.validate!
redirect_to admin_need_offers_path(@need)
else
@ -83,7 +83,7 @@ class Admin::OffersController < ApplicationController
def update
@offer = Offer.find(params[:id])
if @offer.update_attributes(offer_params)
flash[:notice] = "Offre modifiée avec succès."
flash[:notice] = "Proposition modifiée avec succès."
if params[:need_id]
@need = Need.find(params[:need_id])
redirect_to admin_need_offers_path(@need)
@ -99,9 +99,9 @@ class Admin::OffersController < ApplicationController
@offer = Offer.find(params[:id])
if(@offer.destroy)
flash[:notice] = "Offre supprimée avec succès."
flash[:notice] = "Proposition supprimée avec succès."
else
flash[:error] = "Impossible de supprimer cette offre"
flash[:error] = "Impossible de supprimer cette proposition"
end
if params[:need_id]

View File

@ -13,9 +13,10 @@ class Public::AcceptedOffersController < ApplicationController
@accepted_offer.save
send_file @accepted_offer.devis.file.path
end
end
def index
@accepted_offer = AcceptedOffer.find(params[:id])
end
end

View File

@ -0,0 +1,49 @@
class Public::ContactMessagesController < ApplicationController
layout "public"
def index
ContactMessage.update_all(read_by_customer: true)
@contact_message = ContactMessage.new
@contact_messages = ContactMessage.where("customer_id = ? OR contact_id = ?", current_customer.id,current_customer.id )
.order(created_at: :desc)
.page params[:page]
end
def create
@contact_message = ContactMessage.new
@contact_message.assign_attributes(message_params)
@contact_message.customer = current_customer
@contact_message.contact = current_customer
@contact_message.read_by_customer = true
if @contact_message.save
flash[:notice] = "Commentaire envoyé."
return redirect_to :back
else
flash[:error] = "Votre commentaire n'a pas pu être envoyé."
return render 'index'
end
end
def destroy
@message = ContactMessage.find(params['id'])
if @message.destroy
flash[:notice] = "message supprimé"
else
flash[:error] = "Impossible de supprimer ce message"
end
redirect_to :back
end
def message_params
params.require(:contact_message).permit(:content)
end
end

View File

@ -6,14 +6,19 @@ class Public::OffersController < ApplicationController
def accept
if @offer.customers.include?(current_customer)
flash[:error] = "Vous avez déjà accepter cette offre"
flash[:error] = "Vous avez déjà accepter cette proposition"
return redirect_back_or_default :root
end
@accepted_offer = AcceptedOffer.new
@accepted_offer.customer = current_customer
@accepted_offer.offer = @offer
@document = Document.new
@document.title = "Proposition"
@accepted_offer.documents << @document
if @accepted_offer.save
flash[:notice] = "Offre acceptée avec succès !"
flash[:notice] = "Proposition acceptée avec succès !"
redirect_back_or_default :root
end
end

View File

@ -2,6 +2,7 @@ class AcceptedOffer < ActiveRecord::Base
include Workflow
belongs_to :customer
belongs_to :offer
has_many :documents, dependent: :destroy
mount_uploader :devis, DevisUploader
validates :customer, :presence => true
validates :offer, :presence => true
@ -9,43 +10,28 @@ class AcceptedOffer < ActiveRecord::Base
workflow_column :state
workflow do
state :waiting_devis do
event :upload_devis, :transitions_to => :devis_available
state :waiting_documents do
event :complete_documents, :transitions_to => :devis_available
end
state :devis_available do
event :download_devis, :transitions_to => :devis_downloaded
end
state :devis_downloaded do
event :receive_devis, :transitions_to => :devis_received
end
state :devis_received
state :documents_completed
end
def human_state
case state
when 'waiting_devis'
"Création du devis en cours..."
when 'devis_available'
"Devis disponible"
when 'devis_downloaded'
"En attente de réception du devis signé"
when 'devis_received'
"Devis reçu et signé"
when 'waiting_documents'
"En attente des documents à retourner"
when 'documents_completed'
"Documents retournés et vérifiés"
end
end
def human_admin_state
case state
when 'waiting_devis'
"En attente de création du devis"
when 'devis_available'
"Devis disponible pour le client mais pas encore téléchargé"
when 'devis_downloaded'
"Dévis téléchargé par le client, en attente de réception du devis signé"
when 'devis_received'
"Devis reçu et signé"
when 'waiting_documents'
"En attente des documents à retourner"
when 'documents_completed'
"Documents retournés et vérifiés"
end
end
end

View File

@ -33,6 +33,10 @@ class Admin < ActiveRecord::Base
AdminMailer.delay.password_reset(self)
end
def fullname
"#{firstname.capitalize} #{name.upcase}"
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64

33
app/models/contact_message.rb Executable file
View File

@ -0,0 +1,33 @@
class ContactMessage < ActiveRecord::Base
belongs_to :customer
belongs_to :admin
belongs_to :contact, class_name: "Customer"
paginates_per 5
acts_as_paranoid
validates :content, :presence => true, length: {within: 1..1024}
def conversation_id
if customer
customer.id
elsif contact
contact.id
else
nil
end
end
def author_name
if customer
customer.fullname
elsif admin
admin.fullname + " (Administrateur)"
else
nil
end
end
end

61
app/models/document.rb Executable file
View File

@ -0,0 +1,61 @@
class Document < ActiveRecord::Base
include Workflow
belongs_to :accepted_offer
mount_uploader :document, DocumentUploader
mount_uploader :returned_document, DocumentUploader
acts_as_paranoid
workflow_column :state
validates :title, :presence => true
workflow do
state :not_available do
event :upload_document, :transitions_to => :document_available
end
state :document_available do
event :download_document, :transitions_to => :document_downloaded
end
state :document_downloaded do
event :return_document, :transitions_to => :document_returned
end
state :document_returned do
event :verify, :transitions_to => :document_verified
end
state :document_verified
end
def human_state
case state
when 'not_available'
"Pas encore disponible, patientez..."
when 'document_available'
"Disponible"
when 'document_downloaded'
"Téléchargé, à retourner signé"
when 'document_returned'
"Retourné, en attente de vérification"
when 'document_verified'
"Retourné et vérifié"
end
end
def human_admin_state
case state
when 'not_available'
"Vous devez charger le document"
when 'document_available'
"Disponible et en attente de téléchargement par le client"
when 'document_downloaded'
"Téléchargé par le client, en attente de retour"
when 'document_returned'
"Retourné par le client, en attente de vérification"
when 'document_verified'
"Retourné et vérifié"
end
end
end

View File

@ -0,0 +1,30 @@
# encoding: utf-8
class DocumentUploader < CarrierWave::Uploader::Base
include Rails.application.routes.url_helpers
def filename
"#{mounted_as}-#{friendly_filename(model.title)}.#{file.extension}" if original_filename.present?
end
def store_dir
"#{Rails.root}/private_medias/documents/#{model.id}/#{mounted_as}"
end
def extension_white_list
%w(pdf)
end
def url
#download_admin_offer_accepted_offer_path(model.offer, model)
end
def friendly_filename(filename)
filename.gsub(/[^\w\s_-]+/, '')
.gsub(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2')
.gsub(/\s+/, '_')
end
end

View File

@ -0,0 +1,10 @@
%tr
%td
Titre du document
%td
Document
%td
Document retourné
%td{style:"text-align:center"}
Etat
%td{style:"text-align:righ"}

View File

@ -0,0 +1 @@
ezrf

View File

@ -0,0 +1,72 @@
%h1
= "Gestion des documents pour l'utilisateur #{@accepted_offer.customer.fullname} sur la proposition #{@accepted_offer.offer.need.title}"
%h3
= "Ajouter un document"
%br
=semantic_form_for [:admin, @offer, @accepted_offer, Document.new] do |f|
.content
=f.inputs do
=f.input :title, :label => "Titre du document : "
.actions= f.submit "Créer le document", :class => "btn btn-primary"
%br
%h3
= "Liste des documents (#{@accepted_offer.documents.where(state: :document_verified).count} sur #{@accepted_offer.documents.count} vérifiés)"
%table.table.admin-table.table-hover.table-striped
%thead.rows_header
%tr
%th
Titre du document
%th{style:"text-align:center"}
Fichier à retourner par le client
%th{style:"text-align:center"}
Fichier retourné par le client
%th{style:"text-align:left"}
État du document
%th{style:"text-align:right"}
%tbody.rows
-@documents.each do |document|
%tr{class: document.document_verified? ? "success" : ""}
%td
=document.title
%td{style:"text-align:center"}
-if !document.document?
= form_tag admin_offer_accepted_offer_document_upload_document_path(@offer, @accepted_offer, document), name: :document, method: :post, multipart: true do
%span.btn.btn-default.btn-file
="Ouvrir (PDF)"
= file_field_tag :document
= submit_tag("Charger" , class:"btn btn-primary")
-else
=link_to i(:"download"), admin_offer_accepted_offer_document_download_path(@offer, @accepted_offer, document),title: "Télécharger le document"
-if !document.document_verified?
=link_to i(:"remove"), admin_offer_accepted_offer_document_delete_path(@offer, @accepted_offer, document), title: "Supprimer le fichier chargé", :data => {:confirm => 'Voulez-vous vraiment supprimer le fichier chargé ?'}
%td{style:"text-align:center"}
-if !document.not_available?
-if !document.returned_document?
= form_tag admin_offer_accepted_offer_document_upload_returned_document_path(@offer, @accepted_offer, document), name: :returned_document, method: :post, multipart: true do
%span.btn.btn-default.btn-file
="Ouvrir (PDF)"
= file_field_tag :returned_document
= submit_tag("Charger manuellement" , class:"btn btn-primary")
-else
=link_to i(:"download"), admin_offer_accepted_offer_document_download_returned_path(@offer, @accepted_offer, document),title: "Télécharger le document"
-if !document.document_verified?
=link_to i(:"remove"), admin_offer_accepted_offer_document_delete_returned_path(@offer, @accepted_offer, document), title: "Supprimer le fichier chargé", :data => {:confirm => 'Voulez-vous vraiment supprimer le fichier chargé ?'}
-if document.document_returned?
=link_to ic(:"check"), admin_offer_accepted_offer_document_verify_returned_path(@offer, @accepted_offer, document), :data => {:confirm => 'Voulez-vous vraiment marquer le document retourné par le client comme vérifié et signé ?'}, title: "Marquer le document comme vérifié et signé"
-else
Charger un document d'abord
%td{style:"text-align:left"}
=document.human_admin_state
%td{style:"text-align:right"}
=link_to i(:"trash"), admin_offer_accepted_offer_document_destroy_path(@offer, @accepted_offer, document), title: "Supprimer le document", :data => {:confirm => 'Voulez-vous vraiment supprimer ce document ?'}

View File

@ -0,0 +1,9 @@
%tr
%td
%td
=contact_message.content
%td
=contact_message.created_at
%td{:style => "width:100px"}
=link_to i(:"eye"), admin_conversation_path(contact_message.conversation_id), class: "btn"

View File

@ -0,0 +1,6 @@
.white.padding.message-item{style:contact_message.admin ? "background-color:#e8e8e8" : "background-color:#b7e7ff"}
%h4= ic(:"user") + " " + contact_message.author_name
%p.info=ic(:"clock-o") + " Posté le #{contact_message.created_at.strftime('%d/%m/%Y à %H:%M')}, il y a #{time_ago_in_words(contact_message.created_at)}"
%p= contact_message.content

View File

@ -0,0 +1,15 @@
%tr
%td
= link_to contact_message.contact.fullname + " (#{contact_message.contact.organisation})", edit_admin_customer_path( contact_message.contact)
%td
="#{contact_message.last_at.strftime('%d/%m/%Y à %H:%M')}, il y a #{time_ago_in_words(contact_message.last_at)}"
%td{style:"text-align:center"}
=contact_message.messages_count
%td{style:"text-align:center"}
-if contact_message.unread_count > 0
%span.badge{style:"background-color:#D9534F"}
=contact_message.unread_count
-else
Aucun
%td{:style => "width:100px;text-align:right"}
=link_to i(:"comments"), admin_conversation_path(contact_message.conversation_id)

View File

@ -0,0 +1,25 @@
%h1
Liste des conversations avec les utilisateurs
%table.table.admin-table.table-hover.table-striped
%thead.rows_header
%tr
%th
Utilisateur
%th
Date du dernier message
%th{style:"text-align:center"}
Nombre de messages
%th{style:"text-align:center"}
Messages non lus
%th{:style => "width:100px"}
&nbsp;
%tbody.rows
=render collection: @conversations, partial: 'conversation', as: :contact_message
.pagination.pull-right= paginate @conversations

View File

@ -0,0 +1,21 @@
.center.white.row
.row
%h1
= ic(:"comments") + " Conversation avec "
= link_to @customer.fullname,edit_admin_customer_path(@customer)
= semantic_form_for [:admin, ContactMessage.new], :html => {id: :contact_message_form, :method => :post } do |f|
%h4 Répondre
= f.inputs do
= f.input :content, as: :text, label: false, rows: 5, :input_html => {:style => "height:100px;"}
= f.input :contact_id, as: :hidden, value: @customer.id
= f.submit "Envoyer", :class => "btn btn-square btn-primary pull-right"
.row{style:"margin-top:20px;"}
.padding
=render collection: @contact_messages, partial: 'contact_message'
.pull-right
.pagination= paginate @contact_messages

View File

@ -22,7 +22,7 @@
-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-up"), accept_admin_need_path(need), title: "Passer en négocié et créer une proposition", :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)

View File

@ -51,7 +51,7 @@
%th{style: 'text-align:center' }
Commentaires/Intérêts
%th{style: 'text-align:center' }
Offres
Propositions
%th
Statut
%th{:style => "width:100px"}

View File

@ -15,10 +15,10 @@
%td.actions{:style => "width:150px;text-align:right"}
-if @need
= link_to i(:"cog"), accepted_admin_offer_path(offer), title: "Gérer les offres par client"
= link_to i(:"trash"), [:admin,@need, offer], :data => {:confirm => 'Voulez-vous vraiment supprimer cette offre ?'}, :method => :delete
= link_to i(:"cog"), accepted_admin_offer_path(offer), title: "Gérer les propositions par client"
= link_to i(:"trash"), [:admin,@need, offer], :data => {:confirm => 'Voulez-vous vraiment supprimer cette proposition ?'}, :method => :delete
= link_to i(:pencil), edit_admin_need_offer_path(@need, offer)
-else
= link_to i(:"cog"), accepted_admin_offer_path(offer), title: "Gérer les offres par client"
= link_to i(:"trash"), [:admin, offer], :data => {:confirm => 'Voulez-vous vraiment supprimer cette offre ?'}, :method => :delete
= link_to i(:"cog"), accepted_admin_offer_path(offer), title: "Gérer les propositions par client"
= link_to i(:"trash"), [:admin, offer], :data => {:confirm => 'Voulez-vous vraiment supprimer cette proposition ?'}, :method => :delete
= link_to i(:pencil), edit_admin_offer_path(offer)

View File

@ -1,5 +1,5 @@
%h1
= "Gestion des offres par client pour le besoin "
= "Gestion des propositions par client pour le besoin "
<strong>
= @offer.need.title
</strong>
@ -10,12 +10,12 @@
=pluralize(@offer.need.wishes.length, "Personne")
</strong>
%h4
= "Offres acceptées: "
= "Propositions acceptées: "
<strong>
=pluralize(@offer.accepted_offers.length, "Personne")
</strong>
%h4
= "Prix de l'offre: "
= "Prix proposé: "
<strong>
=number_to_currency(@offer.price, locale: :fr)
</strong>
@ -37,16 +37,16 @@
%th
Email
%th{style:"text-align:center"}
Offre acceptée?
Propositions acceptée?
%th{style:"text-align:center"}
Devis
Documents
%th{style:"text-align:center"}
État
%tbody.rows
-@offer.need.wishes.each do |wish|
-accepted_offer = @offer.accepted_offers.where(customer_id: wish.customer.id).first
-if accepted_offer && accepted_offer.devis_received?
-if accepted_offer && accepted_offer.documents_completed?
-class_name = "success"
%tr{class: class_name}
%td
@ -56,41 +56,26 @@
=wish.customer.firstname + " " + wish.customer.name
%td
-if wish.customer.phone
= i("phone") + " #{wish.customer.phone}"
= ic("phone") + " #{wish.customer.phone}"
%td
-if wish.customer.email
= link_to i("envelope-o") + " #{wish.customer.email}", 'mailto:' + wish.customer.email
= link_to ic("envelope-o") + " #{wish.customer.email}", 'mailto:' + wish.customer.email
%td{style:"text-align:center"}
-if accepted_offer
=i(:"check") + " Acceptée"
=ic(:"check") + " Acceptée"
-else
\-
%td{style:"text-align:center"}
-if accepted_offer
=link_to ic(:file) + " #{accepted_offer.documents.where(state: :document_verified).count} / #{accepted_offer.documents.count}", admin_offer_accepted_offer_path(@offer, accepted_offer), style:"display:inline-block", class: "btn btn-primary btn-xs"
-if accepted_offer.devis?
-if accepted_offer.devis_downloaded?
=link_to "Reçu et signé", received_admin_offer_accepted_offer_path(@offer, accepted_offer), :data => {:confirm => 'Voulez-vous vraiment marquer le devis comme reçu et signé ?'}, style:"display:inline-block", class: "btn btn-success btn-sm"
=link_to "Télécharger le devis", accepted_offer.devis.url, style:"display:inline-block", class: "btn btn-primary btn-sm"
=link_to "Supprimer le devis", delete_admin_offer_accepted_offer_path(@offer, accepted_offer), style:"display:inline-block", class: "btn btn-danger btn-sm"
-else
= form_tag upload_devis_admin_offer_accepted_offer_path(@offer, accepted_offer), name: :devis, method: :post, multipart: true do
%span.btn.btn-default.btn-file
="..."
= file_field_tag :devis
= submit_tag("Charger" , class:"btn btn-primary")
-else
\-
%td{style:"text-align:center"}
-if accepted_offer
=accepted_offer.human_admin_state
-else
Offre pas encore acceptée
Pas encore acceptée
:javascript

View File

@ -1,4 +1,4 @@
%h1
Modifier une offre
Modifier une proposition
=render :partial => "form"

View File

@ -1,33 +1,33 @@
-if @need
%h1
Gestion des offres pour le besoin
Gestion des propositions pour le besoin
%strong= @need.title
%p.alert.alert-info
Seul les offres concernant le besoin
Seul les propositions concernant le besoin
%strong= @need.title
sont affichées sur cette page.
%br
Pour voir toutes les offres, aller dans l'onglet
%strong Gestion des offres
Pour voir toutes les propositions, aller dans l'onglet
%strong Gestion des propositions
depuis la barre de navigation
-else
%h1
Gestion des offres
Gestion des propositions
%p.alert.alert-info
Cette page affiche toutes les offres existantes.
Cette page affiche toutes les propositions existantes.
%br
Pour créer une offre, il faut d'abord choisir un besoin. Pour cela, passer par l'onglet
Pour créer une proposition, il faut d'abord choisir un besoin. Pour cela, passer par l'onglet
%strong Gestion des besoins
puis cliquer sur le bouton
%strong= ic(:gift)
en face du besoin de votre choix.
-if @offers.length < 1
Aucune offre actuellement
Aucune proposition actuellement
-if @need
%br
%br
=link_to "Ajouter une offre", new_admin_need_offer_path(@need),class:"btn btn-primary"
=link_to "Ajouter une proposition", new_admin_need_offer_path(@need),class:"btn btn-primary"
%br
-else
.row
@ -38,7 +38,7 @@
%tr
%th
Besoin concerné par l'offre
Besoin concerné par la proposition
%th
Fournisseur
%th
@ -46,7 +46,7 @@
%th{style:"text-align:center;"}
Clients Intéressés
%th{style:"text-align:center;"}
Offres Acceptées
Propositions Acceptées
%th{:style => "width:100px"}
&nbsp;
@ -57,7 +57,7 @@
.pagination.pull-right= paginate @offers
.col-md-2
-if @need
=link_to "Ajouter une offre", new_admin_need_offer_path(@need),class:"btn btn-primary btn-block"
=link_to "Ajouter une proposition", new_admin_need_offer_path(@need),class:"btn btn-primary btn-block"
%br
= semantic_form_for :search, :html => {id: :search_form, :method => :get } do |f|
= f.inputs do

View File

@ -1,4 +1,4 @@
%h1
Créer une offre pour le besoin
Créer une proposition pour le besoin
%strong= @need.title
=render :partial => "form"

View File

@ -42,13 +42,17 @@
%li= link_to "Clients", admin_customers_path
- unvalidated_need_count = Need.where(state: 'created').count
-if unvalidated_need_count > 0
%li= link_to content_tag(:span,unvalidated_need_count , class: 'badge') + " Besoins", admin_needs_path
%li= link_to content_tag(:span,unvalidated_need_count , style:"background-color:#D9534F", class: 'badge') + " Besoins", admin_needs_path
-else
%li= link_to " Besoins", admin_needs_path
%li= link_to " Catégories", admin_need_categories_path
%li= link_to " Offres", admin_offers_path
%li= link_to " Propositions", admin_offers_path
- unread_messages = ContactMessage.where(read_by_admin: false).count
-if unread_messages > 0
%li=link_to content_tag(:span,unread_messages , style:"background-color:#D9534F", class: 'badge') + " Contacts", admin_conversations_path
-else
%li= link_to " Contacts", admin_conversations_path
%ul.nav.navbar-nav.navbar-right

View File

@ -0,0 +1,4 @@
.center.white.row
.row.gutter
%h1= i(:"file") + " Documents à retourner pour la proposition #{@accepted_offer.offer.need.title}"

View File

@ -0,0 +1,6 @@
.white.padding.message-item{style:contact_message.admin ? "background-color:#b7e7ff" : "background-color:#e8e8e8"}
%h4= i(:"user") + " " + contact_message.author_name
%p.info=i(:"clock-o") + " Posté le #{contact_message.created_at.strftime('%d/%m/%Y à %H:%M')}, il y a #{time_ago_in_words(contact_message.created_at)}"
%p= contact_message.content

View File

@ -0,0 +1,17 @@
.center.white.row
.row.gutter
%h1= i(:"comments") + " Nous contacter"
= semantic_form_for [:public, @contact_message ], :html => {id: :contact_message_form, :method => :post } do |f|
%h4 Écrire un nouveau message
= f.inputs do
= f.input :content, as: :text, label: false, rows: 5, :input_html => {:style => "height:100px;"}
=f.submit "Envoyer", :class => "btn btn-square btn-primary pull-right"
.clear
.padding
=render collection: @contact_messages, partial: 'contact_message'
.pagination= paginate @contact_messages

View File

@ -47,10 +47,10 @@
=link_to "Modifier mes infos", public_edit_infos_path, :class => "btn btn-primary"
.padding.center.white
%h3
Mes offres acceptées
Mes propositions acceptées
%div.alert.alert-info
%p
Voici la liste de toutes les offres que vous avez acceptées. C'est ici que vous pouvez télécharger les devis quand ils sont disponibles.
Voici la liste de toutes les propositions que vous avez acceptées. C'est ici que vous pouvez télécharger les devis quand ils sont disponibles.
%p
Une fois le devis téléchargé, vous devez nous le renvoyer signé.
@ -59,7 +59,7 @@
.pagination= paginate @accepted_offers, param_name: 'page_offers'
-else
%p
Vous n'avez pas encore accepté d'offre
Vous n'avez pas encore accepté de proposition
.padding.center.white
%h3
Mes besoins
@ -67,9 +67,9 @@
%p
Votre liste de besoins comprend tout les besoins pour lesquels vous avez signalé un intérêt.
%p
Signaler votre intérêt vous permet d'acceder à des offres intéressantes si nous décidons par la suite de négocier ce besoin auprès de nos fournisseurs.
Signaler votre intérêt vous permet d'acceder à des propositions intéressantes si nous décidons par la suite de négocier ce besoin auprès de nos fournisseurs.
%p
Signaler un intérêt ne vous engage en rien, vous serez engagé seulement après avoir accepté une offre que nous vous proposons.
Signaler un intérêt ne vous engage en rien, vous serez engagé seulement après avoir accepté une proposition que nous vous proposons.
%p
Remarque: Vous pouvez signaler/supprimer un intérêt pour un besoin uniquement quand celui-ci est en sondage.
-if @wishes.length > 0

View File

@ -1,4 +1,4 @@
-if(accepted_offer.devis_received?)
-if(accepted_offer.documents_completed?)
-class_name="success"
-else
-class_name="warning"
@ -10,12 +10,7 @@
=link_to accepted_offer.offer.need.title, public_need_path(accepted_offer.offer.need)
%td
=number_to_currency(accepted_offer.offer.price, locale: :fr)
%td
=accepted_offer.offer.supplier
%td{style: 'text-align:center'}
=accepted_offer.human_state
%td{style: 'text-align:right'}
-if accepted_offer.devis?
=link_to i(:"download") + " Télécharger", download_devis_public_accepted_offer_path(accepted_offer), class: "btn btn-primary"
-else
Pas encore disponible
=link_to i(:"file") + " #{accepted_offer.documents.where(state: :document_verified).count} / #{accepted_offer.documents.count}", public_documents_path(accepted_offer), class: "btn btn-sm btn-primary"

View File

@ -5,11 +5,10 @@
Titre du besoin
%th
Prix négocié
%th
Fournisseur
%th{style: 'text-align:center'}
État
%th{style: 'text-align:right'}
Devis
Documents à retourner
%tbody
=render partial: "public/needs/accepted_offer", collection: @accepted_offers, as: :accepted_offer

View File

@ -58,7 +58,7 @@
-elsif(need.negociated?)
-if(need.customers.include?(current_customer))
=link_to i(:"download") +" Voir les offres", public_need_path(need), class: "btn btn-success pull-right"
=link_to i(:"download") +" Voir les propositions", public_need_path(need), class: "btn btn-success pull-right"
-else
=link_to i(:"times-circle") + " Trop tard pour vous !", public_need_path(need) , class: "btn btn-danger pull-right"

View File

@ -43,13 +43,13 @@
-if @need.customers.include?(current_customer)
.alert.alert-success
%h3= i(:"check") + ' Vous êtes intéressé par ce besoin'
%p Marquer votre interêt pour un besoin vous permettra d'accéder à des offres intéressantes si nous décidons par la suite de négocier ce besoin auprès de nos fournisseurs.
%p Marquer votre interêt pour un besoin vous permettra d'accéder à des propositions intéressantes si nous décidons par la suite de négocier ce besoin auprès de nos fournisseurs.
-else
.alert.alert-info
%h3 Vous avez aussi ce besoin ? signalez-le nous !
%p Vous pouvez marquer votre interêt pour ce besoin en cliquant sur le bouton <strong>Ça m'intéresse !</strong>
%p Si il y a un nombre suffisant de personnes avec ce même besoin, une négociation sera entamée auprès de nos fournisseurs afin de vous faire une offre au meilleur prix.
%p Vous ensuite libre d'accepter ou non l'offre
%p Si il y a un nombre suffisant de personnes avec ce même besoin, une négociation sera entamée auprès de nos fournisseurs afin de vous faire une proposition au meilleur prix.
%p Vous ensuite libre d'accepter ou non la proposition
-elsif @need.negociating?
-if @need.customers.include?(current_customer)
@ -57,8 +57,8 @@
%h3 Négociation en cours...
%p Ce besoin a suscité un interêt suffisant pour que nous engagions une négociation afin de vous proposer cet article au meilleur prix.
%p Vous avez marqué votre interêt pour ce besoin et vous serez donc prevenu dès que la négociation sera terminée.
%p Une ou plusieurs offres vous seront faites et vous serez libre de les accepter ou non.
%p <strong>Attention, accepter l'offre vous engage à payer.</strong>
%p Une ou plusieurs propositions vous seront faites et vous serez libre de les accepter ou non.
%p <strong>Attention, accepter la proposition vous engage à payer.</strong>
-else
.alert.alert-warning
%h3 Négociation en cours...
@ -70,8 +70,8 @@
.alert.alert-success
%h3 Négociation terminée
%p= "Nous avons négocié ce besoin à partir de #{number_to_currency(offers.first.price, locale: :fr)}"
%p Vous avez maintenant le choix d'accepter ou refuser les offres proposées.
%p <strong>Attention, accepter l'offre vous engage à payer.</strong>
%p Vous avez maintenant le choix d'accepter ou refuser la proposition.
%p <strong>Attention, accepter la proposition vous engage à payer.</strong>
-else
.alert.alert-success
%h3 Négociation terminée
@ -98,24 +98,23 @@
-elsif @need.negociated?
-if @need.offers.length > 0
%h2= i(:"gift") + " Les offres négociées"
%h2= i(:"gift") + " Les propositions négociées"
-@need.offers.each do |offer|
.offer
.price
=number_to_currency(offer.price, locale: :fr)
.supplier{style: "position:absolute;bottom:-5px;"}
="Avec #{offer.supplier}"
-if offer.need.customers.include?(current_customer)
-if !offer.customers.include?(current_customer)
.accept-offer
=link_to i(:"check") + " Accepter l'offre", accept_public_need_offer_path(@need, offer), data: {confirm: "Voulez-vous vraiment accepter cette offre ? Attention, cette action vous engage à payer la somme proposée."}, class: "btn btn-lg btn-success "
=link_to i(:"check") + " Accepter la proposition", accept_public_need_offer_path(@need, offer), data: {confirm: "Voulez-vous vraiment accepter cette proposition ? Attention, cette action vous engage à payer la somme proposée."}, class: "btn btn-lg btn-success "
-else
.offer-accepted
=i(:"check") + " Offre Acceptée"
=i(:"check") + " Propositions Acceptée"
.my-account-link
Consulter vos offres depuis la rubrique
Consulter vos propositions acceptées depuis la rubrique
=link_to "Mon compte", public_my_account_path
-else
.offer-not-aceptable

View File

@ -5,6 +5,8 @@
%ul
%li=link_to ic(:star)+" Besoins", public_needs_path, :class => "btn"
- unread_message = ContactMessage.where(contact_id: current_customer.id, read_by_customer: false).count
%li=link_to ic(:comment)+" Nous contacter" + (unread_message > 0 ? " (#{unread_message})" : ""), public_contact_messages_path, :class => "btn"
%li=link_to ic(:user)+" Mon compte", public_my_account_path, :class => "btn"
%li=link_to "Se déconnecter", logout_public_customers_auths_path, :class => "btn"

View File

@ -90,7 +90,8 @@ Rails.application.routes.draw do
resources :accepted_offers do
member do
get :download_devis
resources :documents
end
end
resources :needs do
@ -105,6 +106,10 @@ Rails.application.routes.draw do
end
end
resources :contact_messages do
end
end
@ -265,13 +270,32 @@ Rails.application.routes.draw do
get :reject
end
end
resources :conversations do
end
resources :contact_messages do
end
resources :offers do
resources :accepted_offers do
resources :documents do
post :upload_document
post :upload_returned_document
get :download
get :download_returned
get :delete
get :delete_returned
get :verify_returned
get :destroy
end
member do
post :upload_devis
get :download
get :delete
get :received
end
end

View File

@ -0,0 +1,14 @@
class CreateContactMessage < ActiveRecord::Migration
def change
create_table :contact_messages do |t|
t.references :customer
t.references :admin
t.references :contact
t.datetime :deleted_at
t.boolean :read_by_admin, default: false
t.boolean :read_by_customer, default: false
t.text :content
t.timestamps null: false
end
end
end

View File

@ -0,0 +1,13 @@
class CreateDocument < ActiveRecord::Migration
def change
create_table :documents do |t|
t.references :accepted_offer
t.datetime :deleted_at
t.string :title
t.string :state
t.string :document
t.string :returned_document
t.timestamps null: false
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: 20160215170354) do
ActiveRecord::Schema.define(version: 20160308112733) do
create_table "accepted_offers", force: :cascade do |t|
t.datetime "created_at", null: false
@ -145,6 +145,18 @@ ActiveRecord::Schema.define(version: 20160215170354) do
t.datetime "updated_at"
end
create_table "contact_messages", force: :cascade do |t|
t.integer "customer_id", limit: 4
t.integer "admin_id", limit: 4
t.integer "contact_id", limit: 4
t.datetime "deleted_at"
t.boolean "read_by_admin", limit: 1, default: false
t.boolean "read_by_customer", limit: 1, default: false
t.text "content", limit: 65535
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "customer_newsgroups", force: :cascade do |t|
t.integer "customer_id", limit: 4
t.integer "newsgroup_id", limit: 4
@ -224,6 +236,17 @@ ActiveRecord::Schema.define(version: 20160215170354) do
t.datetime "updated_at"
end
create_table "documents", force: :cascade do |t|
t.integer "accepted_offer_id", limit: 4
t.datetime "deleted_at"
t.string "title", limit: 255
t.string "state", limit: 255
t.string "document", limit: 255
t.string "returned_document", limit: 255
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "download_contents", force: :cascade do |t|
t.string "title", limit: 255
t.string "style", limit: 255