mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-01 02:03:22 +00:00
Merge branch 'producer-emails' of https://github.com/folklabs/openfoodnetwork into folklabs-producer-emails
Conflicts: Gemfile.lock app/controllers/admin/order_cycles_controller.rb app/views/admin/order_cycles/edit.html.haml app/views/admin/order_cycles/show.rep db/schema.rb spec/controllers/admin/order_cycles_controller_spec.rb
This commit is contained in:
3
Gemfile
3
Gemfile
@@ -5,6 +5,8 @@ gem 'rails', '3.2.21'
|
||||
gem 'rails-i18n', '~> 3.0.0'
|
||||
gem 'i18n', '~> 0.6.11'
|
||||
|
||||
gem 'nokogiri'
|
||||
|
||||
gem 'pg'
|
||||
gem 'spree', :github => 'openfoodfoundation/spree', :branch => '1-3-stable'
|
||||
gem 'spree_i18n', :github => 'spree/spree_i18n', :branch => '1-3-stable'
|
||||
@@ -49,7 +51,6 @@ gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg'
|
||||
gem 'angularjs-file-upload-rails', '~> 1.1.0'
|
||||
gem 'roadie-rails', '~> 1.0.3'
|
||||
gem 'figaro'
|
||||
|
||||
gem 'foreigner'
|
||||
gem 'immigrant'
|
||||
|
||||
|
||||
@@ -573,6 +573,7 @@ DEPENDENCIES
|
||||
letter_opener
|
||||
momentjs-rails
|
||||
newrelic_rpm
|
||||
nokogiri
|
||||
oj
|
||||
paperclip
|
||||
pg
|
||||
|
||||
@@ -53,7 +53,6 @@ module Admin
|
||||
respond_to do |format|
|
||||
if @order_cycle.update_attributes(params[:order_cycle])
|
||||
OpenFoodNetwork::OrderCycleFormApplicator.new(@order_cycle, spree_current_user).go!
|
||||
|
||||
flash[:notice] = 'Your order cycle has been updated.'
|
||||
format.html { redirect_to admin_order_cycles_path }
|
||||
format.json { render :json => {:success => true} }
|
||||
@@ -79,6 +78,14 @@ module Admin
|
||||
redirect_to main_app.admin_order_cycles_path, :notice => "Your order cycle #{@order_cycle.name} has been cloned."
|
||||
end
|
||||
|
||||
# Send notifications to all producers who are part of the order cycle
|
||||
def notify_producers
|
||||
@order_cycle = OrderCycle.find params[:id]
|
||||
Delayed::Job.enqueue OrderCycleNotificationJob.new(@order_cycle)
|
||||
|
||||
redirect_to main_app.admin_order_cycles_path, :notice => 'Emails to be sent to producers have been queued for sending.'
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def collection(show_more=false)
|
||||
|
||||
7
app/jobs/order_cycle_notification_job.rb
Normal file
7
app/jobs/order_cycle_notification_job.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
OrderCycleNotificationJob = Struct.new(:order_cycle) do
|
||||
def perform
|
||||
order_cycle.suppliers.each { |supplier| ProducerMailer.order_cycle_report(supplier, order_cycle).deliver }
|
||||
end
|
||||
end
|
||||
|
||||
36
app/mailers/producer_mailer.rb
Normal file
36
app/mailers/producer_mailer.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
class ProducerMailer < Spree::BaseMailer
|
||||
|
||||
def order_cycle_report(producer, order_cycle)
|
||||
@producer = producer
|
||||
@coordinator = order_cycle.coordinator
|
||||
@order_cycle = order_cycle
|
||||
|
||||
subject = "[#{Spree::Config[:site_name]}] Order cycle report"
|
||||
|
||||
# if @order_cycle.distributors.any?
|
||||
# first_producer = @order_cycle.distributors.first
|
||||
# @distribution_date = @order_cycle.pickup_time_for first_producer
|
||||
# subject += " for #{@distribution_date}" if @distribution_date.present?
|
||||
# end
|
||||
|
||||
@line_items = Spree::LineItem.
|
||||
joins(:order => :order_cycle, :variant => :product).
|
||||
where('order_cycles.id = ?', order_cycle).
|
||||
where('spree_products.supplier_id = ?', producer)
|
||||
|
||||
# Arrange the items in a hash to group quantities
|
||||
@line_items = @line_items.inject({}) do |lis, li|
|
||||
lis[li.variant] ||= {line_item: li, quantity: 0}
|
||||
lis[li.variant][:quantity] += li.quantity
|
||||
lis
|
||||
end
|
||||
|
||||
mail(to: @producer.email,
|
||||
from: from_address,
|
||||
subject: subject,
|
||||
reply_to: @coordinator.email,
|
||||
cc: @coordinator.email)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -7,6 +7,11 @@
|
||||
- else
|
||||
{{ (incomingExchangeVariantsFor(exchange.enterprise_id)).length }}
|
||||
selected
|
||||
- if type == 'supplier'
|
||||
%td.receival-details
|
||||
= text_field_tag 'order_cycle_incoming_exchange_{{ $index }}_receival_time', '', 'id' => 'order_cycle_incoming_exchange_{{ $index }}_receival_time', 'placeholder' => 'Receive at (ie. Date / Time)', 'ng-model' => 'exchange.receival_time'
|
||||
%br/
|
||||
= text_field_tag 'order_cycle_incoming_exchange_{{ $index }}_receival_instructions', '', 'id' => 'order_cycle_incoming_exchange_{{ $index }}_receival_instructions', 'placeholder' => 'Receival instructions', 'ng-model' => 'exchange.receival_instructions'
|
||||
- if type == 'distributor'
|
||||
%td.collection-details
|
||||
= text_field_tag 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', '', 'id' => 'order_cycle_outgoing_exchange_{{ $index }}_pickup_time', 'placeholder' => 'Ready for (ie. Date / Time)', 'ng-model' => 'exchange.pickup_time', 'ng-disabled' => '!enterprises[exchange.enterprise_id].managed && !order_cycle.viewing_as_coordinator'
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
%tr
|
||||
%th Supplier
|
||||
%th Products
|
||||
%th Receival details
|
||||
%th Fees
|
||||
%th.actions
|
||||
%tbody{'ng-repeat' => 'exchange in order_cycle.incoming_exchanges'}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
= content_for :page_actions do
|
||||
%li
|
||||
= button_to "Notify producers", main_app.notify_producers_admin_order_cycle_path, :id => 'admin_notify_producers'
|
||||
|
||||
|
||||
%h1 Edit Order Cycle
|
||||
|
||||
- ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl'
|
||||
|
||||
- ng_controller = order_cycles_simple_form ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl'
|
||||
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.order_cycles', 'ng-controller' => ng_controller, 'ng-submit' => 'submit($event)'} do |f|
|
||||
- if order_cycles_simple_form
|
||||
= render 'simple_form', f: f
|
||||
|
||||
28
app/views/producer_mailer/order_cycle_report.text.haml
Normal file
28
app/views/producer_mailer/order_cycle_report.text.haml
Normal file
@@ -0,0 +1,28 @@
|
||||
Dear #{@producer.name},
|
||||
\
|
||||
We now have all the consumer orders for the food drop on #{@distribution_date}.
|
||||
Please deliver to #{@coordinator.address.address1}, #{@coordinator.address.city}, #{@coordinator.address.zipcode} during the regular delivery time. If this is not convenient then please call #{@coordinator.phone}.
|
||||
|
||||
Note: If you have to arrange a different delivery day and time, it is requested that you do not come on site during drop off/pick up times.
|
||||
|
||||
\
|
||||
Orders summary
|
||||
================
|
||||
\
|
||||
Here is a summary of the orders for your products:
|
||||
\
|
||||
- @line_items.each_pair do |variant, data|
|
||||
#{variant.sku} #{raw(variant.product.supplier.name)} #{raw(variant.product.name)} #{raw(variant.options_text)} (QTY: #{data[:quantity]}) @ #{data[:line_item].single_money} = #{data[:line_item].display_amount}
|
||||
|
||||
\
|
||||
Detailed orders breakdown
|
||||
===========================
|
||||
|
||||
|
||||
Please confirm that you have got this email.
|
||||
|
||||
Please send me an invoice for this amount so we can send you payment.
|
||||
|
||||
If you need to phone on the day please call #{@coordinator.phone}.
|
||||
\
|
||||
Thanks and best wishes - #{@coordinator.name}
|
||||
@@ -47,7 +47,10 @@ module Openfoodnetwork
|
||||
# -- all .rb files in that directory are automatically loaded.
|
||||
|
||||
# Custom directories with classes and modules you want to be autoloadable.
|
||||
config.autoload_paths += %W(#{config.root}/app/presenters)
|
||||
config.autoload_paths += %W(
|
||||
#{config.root}/app/presenters
|
||||
#{config.root}/app/jobs
|
||||
)
|
||||
|
||||
# Only load the plugins named here, in the order given (default is alphabetical).
|
||||
# :all can be used as a placeholder for all plugins not explicitly named.
|
||||
|
||||
@@ -44,6 +44,8 @@ Openfoodnetwork::Application.routes.draw do
|
||||
resources :order_cycles do
|
||||
post :bulk_update, on: :collection, as: :bulk_update
|
||||
get :clone, on: :member
|
||||
|
||||
post 'notify_producers', on: :member
|
||||
end
|
||||
|
||||
resources :enterprises do
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
class AddReceivalTimeToExchange < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :exchanges, :receival_time, :string
|
||||
add_column :exchanges, :receival_instructions, :string
|
||||
end
|
||||
end
|
||||
@@ -353,6 +353,8 @@ ActiveRecord::Schema.define(:version => 20150424025907) do
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.boolean "incoming", :default => false, :null => false
|
||||
t.string "receival_time"
|
||||
t.string "receival_instructions"
|
||||
end
|
||||
|
||||
add_index "exchanges", ["order_cycle_id"], :name => "index_exchanges_on_order_cycle_id"
|
||||
|
||||
@@ -21,10 +21,14 @@ module OpenFoodNetwork
|
||||
|
||||
if exchange_exists?(exchange[:enterprise_id], @order_cycle.coordinator_id, true)
|
||||
update_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, true,
|
||||
{variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids})
|
||||
{variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids,
|
||||
receival_time: exchange[:receival_time],
|
||||
receival_instructions: exchange[:receival_instructions]})
|
||||
else
|
||||
add_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, true,
|
||||
{variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids})
|
||||
{variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids,
|
||||
receival_time: exchange[:receival_time],
|
||||
receival_instructions: exchange[:receival_instructions],})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,12 +39,16 @@ module OpenFoodNetwork
|
||||
|
||||
if exchange_exists?(@order_cycle.coordinator_id, exchange[:enterprise_id], false)
|
||||
update_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], false,
|
||||
{variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids,
|
||||
pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]})
|
||||
{variant_ids: variant_ids,
|
||||
enterprise_fee_ids: enterprise_fee_ids,
|
||||
pickup_time: exchange[:pickup_time],
|
||||
pickup_instructions: exchange[:pickup_instructions]})
|
||||
else
|
||||
add_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], false,
|
||||
{variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids,
|
||||
pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]})
|
||||
{variant_ids: variant_ids,
|
||||
enterprise_fee_ids: enterprise_fee_ids,
|
||||
pickup_time: exchange[:pickup_time],
|
||||
pickup_instructions: exchange[:pickup_instructions]})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ require 'spec_helper'
|
||||
module Admin
|
||||
describe OrderCyclesController do
|
||||
include AuthenticationWorkflow
|
||||
|
||||
let!(:distributor_owner) { create_enterprise_user enterprise_limit: 2 }
|
||||
|
||||
before do
|
||||
@@ -95,6 +96,32 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "notifying producers" do
|
||||
let(:user) { create_enterprise_user }
|
||||
let(:admin_user) do
|
||||
user = create(:user)
|
||||
user.spree_roles << Spree::Role.find_or_create_by_name!('admin')
|
||||
user
|
||||
end
|
||||
let(:order_cycle) { create(:simple_order_cycle) }
|
||||
|
||||
before do
|
||||
controller.stub spree_current_user: admin_user
|
||||
spree_post :notify_producers, {id: order_cycle.id}
|
||||
end
|
||||
|
||||
it "enqueues a job" do
|
||||
expect(Delayed::Job).to receive(:enqueue).once
|
||||
end
|
||||
|
||||
it "redirects back to the order cycles path with a success message" do
|
||||
expect(response).to redirect_to admin_order_cycles_path
|
||||
flash[:notice].should == 'Emails to be sent to producers have been queued for sending.'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "destroy" do
|
||||
let!(:distributor) { create(:distributor_enterprise, owner: distributor_owner) }
|
||||
|
||||
|
||||
13
spec/jobs/order_cycle_notification_job_spec.rb
Normal file
13
spec/jobs/order_cycle_notification_job_spec.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe OrderCycleNotificationJob do
|
||||
let(:order_cycle) { create(:order_cycle) }
|
||||
|
||||
it 'sends a mail to each supplier' do
|
||||
mail = double()
|
||||
allow(mail).to receive(:deliver)
|
||||
expect(ProducerMailer).to receive(:order_cycle_report).twice.and_return(mail)
|
||||
job = OrderCycleNotificationJob.new(order_cycle)
|
||||
job.perform
|
||||
end
|
||||
end
|
||||
54
spec/mailers/producer_mailer_spec.rb
Normal file
54
spec/mailers/producer_mailer_spec.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe ProducerMailer do
|
||||
let(:s1) { create(:supplier_enterprise, address: create(:address)) }
|
||||
let(:s2) { create(:supplier_enterprise, address: create(:address)) }
|
||||
let(:d1) { create(:distributor_enterprise, address: create(:address)) }
|
||||
let(:d2) { create(:distributor_enterprise, address: create(:address)) }
|
||||
let(:p1) { create(:product, price: 12.34, supplier: s1) }
|
||||
let(:p2) { create(:product, price: 23.45, supplier: s2) }
|
||||
let(:order_cycle) { create(:simple_order_cycle) }
|
||||
let!(:order) do
|
||||
order = create(:order, distributor: d1, order_cycle: order_cycle, state: 'complete')
|
||||
order.line_items << create(:line_item, variant: p1.master)
|
||||
order.line_items << create(:line_item, variant: p1.master)
|
||||
order.line_items << create(:line_item, variant: p2.master)
|
||||
order.finalize!
|
||||
order.save
|
||||
order
|
||||
end
|
||||
|
||||
before do
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
after do
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
it "should send an email when an order cycle is closed" do
|
||||
ProducerMailer.order_cycle_report(s1, order_cycle).deliver
|
||||
puts ActionMailer::Base.deliveries
|
||||
ActionMailer::Base.deliveries.count.should == 1
|
||||
end
|
||||
|
||||
it "sets a reply-to of the enterprise email" do
|
||||
ProducerMailer.order_cycle_report(s1, order_cycle).deliver
|
||||
ActionMailer::Base.deliveries.last.reply_to.should == [s1.email]
|
||||
end
|
||||
|
||||
it "cc's the enterprise" do
|
||||
ProducerMailer.order_cycle_report(s1, order_cycle).deliver
|
||||
ActionMailer::Base.deliveries.last.cc.should == [s1.email]
|
||||
end
|
||||
|
||||
it "contains an aggregated list of produce" do
|
||||
ProducerMailer.order_cycle_report(s1, order_cycle).deliver
|
||||
email_body = ActionMailer::Base.deliveries.last.body
|
||||
email_body.to_s.each_line do |line|
|
||||
if line.include? p1.name
|
||||
line.include?('QTY: 2').should == true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user