Skip to content

Commit

Permalink
Merge pull request #238 from TreinaDev/feat/manda-cobranca-assinatura
Browse files Browse the repository at this point in the history
[ FEAT ] Cobrança mensal da assinatura Premium
  • Loading branch information
DanSmaR authored Feb 16, 2024
2 parents b3cb0d2 + 0abb7fd commit f1b2b5f
Show file tree
Hide file tree
Showing 23 changed files with 250 additions and 53 deletions.
1 change: 0 additions & 1 deletion app/controllers/projects_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
class ProjectsController < ApplicationController
before_action :authenticate_user!
before_action :authenticate_subscriber, only: :create_invitation_request

def index
@invitation_request = current_user.invitation_requests.build
@invitation_requests = current_user.invitation_requests.pluck(:project_id).to_json
Expand Down
15 changes: 15 additions & 0 deletions app/jobs/notify_billing_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class NotifyBillingJob < ApplicationJob
queue_as :default

before_perform do |job|
current_billing = job.arguments.first
next_billing = current_billing.subscription.billings.create!(
billing_date: current_billing.billing_date + 1.month, amount: 19.90
)
self.class.set(wait_until: next_billing.billing_date.to_datetime).perform_later(next_billing)
end

def perform(billing)
BillingsMailer.with(billing:).notify_billing.deliver
end
end
10 changes: 10 additions & 0 deletions app/mailers/billings_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class BillingsMailer < ApplicationMailer
default from: '[email protected]'

def notify_billing
@billing = params[:billing]
@user = @billing.subscription.user

mail(subject: default_i18n_subject, to: @user.email)
end
end
3 changes: 3 additions & 0 deletions app/models/billing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Billing < ApplicationRecord
belongs_to :subscription
end
24 changes: 24 additions & 0 deletions app/models/subscription.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
class Subscription < ApplicationRecord
SUBSCRIPTION_AMOUNT = 19.90

belongs_to :user
has_many :billings, dependent: :destroy

enum status: { inactive: 0, active: 10 }

after_update :create_billing, if: :active? && :saved_change_to_status?

def active!
self.start_date = Time.zone.now.to_date
super
Expand All @@ -11,4 +17,22 @@ def inactive!
self.start_date = nil
super
end

private

def create_billing
return if start_date.nil?

billing_date = set_billing_date
billing = billings.create(billing_date:, amount: SUBSCRIPTION_AMOUNT)
NotifyBillingJob.set(wait_until: billing_date.to_datetime).perform_later(billing)
end

def set_billing_date
if start_date.day < 29
start_date + 1.month
else
start_date.next_month.beginning_of_month + 1.month
end
end
end
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class User < ApplicationRecord
has_many :professional_infos, through: :profile
has_many :education_infos, through: :profile
has_many :invitation_requests, through: :profile
has_one :subscription, dependent: :destroy

enum role: { user: 0, admin: 10 }

Expand Down
9 changes: 9 additions & 0 deletions app/views/billings_mailer/notify_billing.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h3><%= t('.greeting', recipient: @user.full_name)%></h3>

<p><%= t('.reminder')%></p>

<p><%= t('.call_to_action')%></p>

<p><%= t('.due_date', due_date: I18n.l(@billing.billing_date)) %></p>

<p><%= t('.amount', amount: number_to_currency(@billing.amount)) %></p>
18 changes: 18 additions & 0 deletions config/locales/models/billings.pt-BR.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pt-BR:
activerecord:
models:
billings:
one: Cobrança
other: Cobranças
attributes:
billings:
billing_date: Data de Cobrança
amount: Valor
billings_mailer:
notify_billing:
subject: Porfoliorrr - Assinatura mensal
greeting: Olá, %{recipient}!
reminder: Sua assinatura mensal do Porfoliorrr Premium vence hoje
call_to_action: Efetue o pagamento para continuar usando os benefícios da sua assinatura
due_date: "Vencimento: %{due_date}"
amount: "Valor: %{amount}"
11 changes: 11 additions & 0 deletions db/migrate/20240215115912_create_billings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateBillings < ActiveRecord::Migration[7.1]
def change
create_table :billings do |t|
t.references :subscription, null: false, foreign_key: true
t.date :billing_date, null: false
t.decimal :amount, precision: 8, scale: 2, null: false

t.timestamps
end
end
end
10 changes: 10 additions & 0 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion spec/factories/subscriptions.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FactoryBot.define do
factory :subscription do
user
start_date { nil }
start_date { Time.zone.now.to_date }
status { 0 }
end
end
4 changes: 0 additions & 4 deletions spec/factories/users.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
email { Faker::Internet.email }
password { '123456' }

after(:create) do |user|
user.subscription.active!
end

trait :seed do
after(:build) do |user|
user.class.skip_callback(:create, :after, :create_profile!, raise: false)
Expand Down
27 changes: 27 additions & 0 deletions spec/jobs/notify_billing_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require 'rails_helper'

RSpec.describe NotifyBillingJob, type: :job do
context 'envia email de cobrança ' do
it 'para usuário com assinatura premium' do
user = create(:user, :paid, full_name: 'Marcos Madeira', email: '[email protected]')
billing = user.subscription.billings.first

mail = double('mail', deliver: true)
mailer = double('BillingsMailer', notifify_billing: mail)

allow(BillingsMailer).to receive(:with).and_return(mailer)
allow(mailer).to receive(:notify_billing).and_return(mail)

NotifyBillingJob.perform_now(billing)

expect(mail).to have_received(:deliver).once
end

it 'A cobrança é recriada para ser enviada via email no próximo mês' do
user = create(:user, :paid, full_name: 'Marcos Madeira', email: '[email protected]')
billing = user.subscription.billings.first

expect { NotifyBillingJob.perform_now(billing) }.to have_enqueued_job(NotifyBillingJob).on_queue('default')
end
end
end
22 changes: 22 additions & 0 deletions spec/mailer/billings_mailer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require 'rails_helper'

RSpec.describe BillingsMailer, type: :mailer do
context '#notify_billing' do
it 'envia e-mail de acordo com data de cobrança da assinatura' do
user = create(:user, :paid, full_name: 'Marcos Madeira', email: '[email protected]')

billing = user.subscription.billings.first

mail = BillingsMailer.with(billing:).notify_billing

expect(mail.subject).to eq 'Porfoliorrr - Assinatura mensal'
expect(mail.to).to eq ['[email protected]']
expect(mail.from).to eq ['[email protected]']
expect(mail.body).to include 'Olá, Marcos Madeira!'
expect(mail.body).to include 'Sua assinatura mensal do Porfoliorrr Premium vence hoje'
expect(mail.body).to include 'Efetue o pagamento para continuar usando os benefícios da sua assinatura'
expect(mail.body).to include "Vencimento: #{I18n.l(billing.billing_date)}"
expect(mail.body).to include 'Valor: R$ 19,90'
end
end
end
6 changes: 3 additions & 3 deletions spec/models/profile_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@
it 'retorna perfis premium primeiro e depois os perfis free' do
create(:user, :free, full_name: 'André Porteira')
create(:user, :free, full_name: 'Eliseu Ramos')
create(:user, full_name: 'Moisés Campus')
create(:user, :paid, full_name: 'Moisés Campus')
user_premium_inactive = create(:user, full_name: 'Joao Almeida')
user_premium_inactive.subscription.inactive!

Expand All @@ -268,8 +268,8 @@
it 'ordena por nome em caso de mesmo status de assinatura' do
create(:user, :free, full_name: 'André Almeida')
create(:user, :free, full_name: 'André Barbosa')
create(:user, full_name: 'André Campus')
create(:user, full_name: 'André Dias')
create(:user, :paid, full_name: 'André Campus')
create(:user, :paid, full_name: 'André Dias')

result = Profile.order_by_premium

Expand Down
75 changes: 62 additions & 13 deletions spec/models/subscription_spec.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,74 @@
require 'rails_helper'

RSpec.describe Subscription, type: :model do
describe '#active!' do
it 'atualiza data de início automaticamente' do
subscription = create(:subscription, status: :inactive, start_date: nil)
context 'assinatura de usuário premium' do
it 'cria cobrança antes do dia 29' do
user = create(:user, :free)

subscription.active!
travel_to Date.parse('2024-02-10') do
user.subscription.active!
end

expect(subscription.start_date).to eq Time.zone.now.to_date
expect(subscription).to be_active
billing = Billing.first
expect(Billing.count).to eq 1
expect(billing.billing_date).to eq Date.parse '2024-03-10'
expect(billing.amount).to eq 19.90
end

context 'recebe cobrança no primeiro dia do mês seguinte' do
it 'quando assinatura for no dia 29' do
user = create(:user, :free)

travel_to Date.parse('2024-03-29') do
user.subscription.active!
end

expect(Billing.count).to eq 1
expect(Billing.first.billing_date).to eq Date.parse '2024-05-01'
end

it 'quando assinatura for no dia 30' do
user = create(:user, :free)

travel_to Date.parse('2024-06-30') do
user.subscription.active!
end

expect(Billing.count).to eq 1
expect(Billing.first.billing_date).to eq Date.parse '2024-08-01'
end

it 'quando assinatura for no dia 31' do
user = create(:user, :free)

travel_to Date.parse('2024-10-31') do
user.subscription.active!
end

expect(Billing.count).to eq 1
expect(Billing.first.billing_date).to eq Date.parse '2024-12-01'
end
end
describe '#active!' do
it 'atualiza data de início automaticamente' do
subscription = create(:subscription, status: :inactive, start_date: nil)

subscription.active!

expect(subscription.start_date).to eq Time.zone.now.to_date
expect(subscription).to be_active
end
end
end

describe '#active!' do
it 'atualiza data de início automaticamente' do
subscription = create(:subscription, status: :active, start_date: Time.zone.now)
describe '#active!' do
it 'atualiza data de início automaticamente' do
subscription = create(:subscription, status: :active, start_date: Time.zone.now)

subscription.inactive!
subscription.inactive!

expect(subscription.start_date).to be_nil
expect(subscription).to be_inactive
expect(subscription.start_date).to be_nil
expect(subscription).to be_inactive
end
end
end
end
6 changes: 3 additions & 3 deletions spec/requests/apis/v1/search_user_by_job_categories_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@
it 'retorna perfis premium primeiro e depois os perfis comuns' do
create(:user, :free, full_name: 'Eliseu Ramos')
create(:user, :free, full_name: 'André Porteira')
create(:user, full_name: 'Moisés Campus')
create(:user, full_name: 'Joao Almeida')
create(:user, :paid, full_name: 'Moisés Campus')
create(:user, :paid, full_name: 'Joao Almeida')

get '/api/v1/profiles'

Expand All @@ -107,7 +107,7 @@
it 'retorna perfis premium primeiro e depois os perfis free na busca com parâmetro' do
ruby = create(:job_category, name: 'Ruby on Rails')

user_premium = create(:user, full_name: 'Moisés Campus')
user_premium = create(:user, :paid, full_name: 'Moisés Campus')
user_premium.profile.profile_job_categories.create(job_category: ruby, description: 'Sou um especialista em Ruby')
user_free = create(:user, :free, full_name: 'André Almeida')
user_free.profile.profile_job_categories.create(job_category: ruby, description: 'Fiz um e-commerce em Ruby')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

describe 'Usuário solicita convite para um projeto' do
it 'com sucesso' do
user = create(:user)

user = create(:user, :paid)
login_as user
post invitation_request_path, params: { invitation_request: { message: 'Me convida', project_id: 1 } }

post invitation_request_path, params: {
'project_id' => 1,
'invitation_request' => {
'message' => 'Me convida'
}
}
user_requests = user.profile.invitation_requests

expect(user_requests.count).to eq 1
expect(user_requests.reload.count).to eq 1
expect(user_requests.last.message).to eq 'Me convida'
end

it 'e falha se já solicitou convite para o mesmo projeto' do
user = create(:user)
user = create(:user, :paid)
create(:invitation_request, profile: user.profile)
login_as user

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

describe 'Solicitação de convite tem status atualizado para "aceita"' do
it 'quando recebe convite para o mesmo projeto' do
user = create(:user)
user = create(:user, :paid)

invitation_request_one = create(:invitation_request, profile: user.profile,
project_id: 1, status: :pending)
Expand Down
Loading

0 comments on commit f1b2b5f

Please sign in to comment.