Skip to content

Commit

Permalink
Sends invoice to email (#262)
Browse files Browse the repository at this point in the history
* Sends invoice to email

* Added api to send invoice email
  • Loading branch information
shivamsinghchahar authored Apr 18, 2022
1 parent ed2f1c4 commit 2e7c5e7
Show file tree
Hide file tree
Showing 14 changed files with 210 additions and 1 deletion.
12 changes: 12 additions & 0 deletions app/controllers/internal_api/v1/invoices_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ def update
}
end

def send_invoice
authorize invoice

invoice.send_to_email(subject: invoice_email_params[:subject], recipients: invoice_email_params[:recipients])

render json: { message: "Invoice will be sent!" }, status: :accepted
end

private

def load_client
Expand All @@ -58,4 +66,8 @@ def invoice_params
policy(Invoice).permitted_attributes
)
end

def invoice_email_params
params.require(:invoice_email).permit(:subject, :body, recipients: [])
end
end
13 changes: 13 additions & 0 deletions app/mailers/invoice_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

class InvoiceMailer < ApplicationMailer
after_action -> { @invoice.sent! }

def invoice
@invoice = params[:invoice]
recipients = params[:recipients]
subject = params[:subject]

mail(to: recipients, subject:)
end
end
9 changes: 9 additions & 0 deletions app/models/concerns/invoice_sendable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module InvoiceSendable
extend ActiveSupport::Concern

def send_to_email(subject:, recipients:)
InvoiceMailer.with(invoice: self, subject:, recipients:).invoice.deliver_later
end
end
2 changes: 2 additions & 0 deletions app/models/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
# frozen_string_literal: true

class Invoice < ApplicationRecord
include InvoiceSendable

attr_accessor :sub_total

enum status: [
Expand Down
4 changes: 4 additions & 0 deletions app/policies/invoice_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ def show?
user_owner_or_admin?
end

def send_invoice?
user_owner_or_admin?
end

def permitted_attributes
[
:issue_date, :due_date,
Expand Down
19 changes: 19 additions & 0 deletions app/views/invoice_mailer/invoice.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Invoice: <%= @invoice.invoice_number %></h1>

<p>
Hi <%= @invoice.client_name %>, You have an invoice with amount <%= @invoice.amount %>.
</p>

<p>
The due date is <%= @invoice.due_date %>.
</p>

<p>Thanks!</p>
</body>
</html>
8 changes: 8 additions & 0 deletions app/views/invoice_mailer/invoice.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Hi, <%= @invoice.client_name %>
===============================================

You have an invoice - <%= @invoice.invoice_number %> with amount <%= @invoice.amount %>
due date is: <%= @invoice.due_date %>.

Thanks!
Miru
2 changes: 2 additions & 0 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test

config.active_job.queue_adapter = :test

# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr

Expand Down
4 changes: 3 additions & 1 deletion config/routes/internal_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
resources :timesheet_entry, only: [:index, :create, :update, :destroy]
resources :reports, only: [:index]
resources :workspaces, only: [:update]
resources :invoices, only: [:index, :create, :update]
resources :invoices, only: [:index, :create, :update] do
post :send_invoice, on: :member
end
resources :generate_invoice, only: [:index, :show]
resources :project_members, only: [:update]
resources :company_users, only: [:index]
Expand Down
22 changes: 22 additions & 0 deletions spec/mailers/invoice_mailer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe InvoiceMailer, type: :mailer do
describe "invoice" do
let(:invoice) { create :invoice }
let(:recipients) { [invoice.client.email, "miru@example.com"] }
let(:subject) { "Invoice (#{invoice.invoice_number}) due on #{invoice.due_date}" }
let(:mail) { InvoiceMailer.with(invoice:, subject:, recipients:).invoice }

it "renders the headers" do
expect(mail.subject).to eq(subject)
expect(mail.to).to eq(recipients)
expect(mail.from).to eq(["from@example.com"])
end

it "renders the body" do
expect(mail.body.encoded).to match("You have an invoice")
end
end
end
13 changes: 13 additions & 0 deletions spec/mailers/previews/invoice_preview.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

# Preview all emails at http://localhost:3000/rails/mailers/invoice

class InvoicePreview < ActionMailer::Preview
def invoice
invoice = Invoice.first
recipients = [invoice.client.email, "miru@example.com"]
subject = "Invoice (#{invoice.invoice_number}) due on #{invoice.due_date}"

InvoiceMailer.with(invoice:, recipients:, subject:).invoice
end
end
10 changes: 10 additions & 0 deletions spec/models/invoice_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,14 @@
describe ".delegate" do
it { is_expected.to delegate_method(:name).to(:client).with_prefix(:client) }
end

describe ".send_to_email" do
let(:invoice) { create :invoice }
let(:recipients) { [invoice.client.email, "miru@example.com"] }
let(:subject) { "Invoice (#{invoice.invoice_number}) due on #{invoice.due_date}" }

it "sends the invoice on email" do
expect { invoice.send_to_email(subject:, recipients:) }.to have_enqueued_mail(InvoiceMailer, :invoice)
end
end
end
71 changes: 71 additions & 0 deletions spec/requests/internal_api/v1/invoices/send_invoice_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe "InternalApi::V1::Invoices#send_invoice", type: :request do
let(:client) { create :client_with_invoices }
let(:company) { client.company }
let(:invoice) { client.invoices.first }
let(:user) { create :user, current_workspace_id: company.id }

context "when user is signed in" do
before do
create(:company_user, company:, user:)
end

let(:invoice_email) do
{ subject: "Some Subject", recipients: [client.email, "miru@example.com"], body: "You have an invoice!" }
end

context "when user is an admin" do
before do
user.add_role :admin, company
sign_in user
end

it "returns a 202 response" do
post send_invoice_internal_api_v1_invoice_path(id: invoice.id), params: { invoice_email: }

expect(response).to have_http_status :accepted
expect(json_response["message"]).to eq("Invoice will be sent!")
end

it "enqueues an email for delivery" do
expect do
post send_invoice_internal_api_v1_invoice_path(id: invoice.id), params: { invoice_email: }
end.to have_enqueued_mail(InvoiceMailer, :invoice)
end

context "when invoice doesn't exist" do
it "returns 404 response" do
post send_invoice_internal_api_v1_invoice_path(id: "random")

expect(response).to have_http_status :not_found
expect(json_response["errors"]).to eq "Couldn't find Invoice with 'id'=random"
end
end
end

context "when user is an employee" do
before do
user.add_role :employee, company
sign_in user
end

it "returns a 403 response" do
post send_invoice_internal_api_v1_invoice_path(id: invoice.id)

expect(response).to have_http_status(:forbidden)
end
end
end

context "when user is logged out" do
it "returns a 401 response" do
post send_invoice_internal_api_v1_invoice_path(id: invoice.id)

expect(response).to have_http_status(:unauthorized)
expect(json_response["error"]).to eq("You need to sign in or sign up before continuing.")
end
end
end
22 changes: 22 additions & 0 deletions spec/support/patch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

# This patches rspec-rails to support rails 7.0 https://github.com/rspec/rspec-rails/issues/2531
module RSpec
module Rails
module Matchers
class HaveEnqueuedMail
def legacy_mail?(job)
defined?(ActionMailer::DeliveryJob) && job[:job] <= ActionMailer::DeliveryJob
end

def parameterized_mail?(job)
RSpec::Rails::FeatureCheck.has_action_mailer_parameterized? && job[:job] <= ActionMailer::MailDeliveryJob
end

def unified_mail?(job)
RSpec::Rails::FeatureCheck.has_action_mailer_unified_delivery? && job[:job] <= ActionMailer::MailDeliveryJob
end
end
end
end
end

0 comments on commit 2e7c5e7

Please sign in to comment.