Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atualização do cálculo de fatura #90

Merged
merged 14 commits into from
Jul 18, 2024
Merged
3 changes: 2 additions & 1 deletion app/models/base_fee.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ class BaseFee < ApplicationRecord
accepts_nested_attributes_for :values
validates :name, :description, :charge_day, presence: true
validates :interest_rate, numericality: { greater_than_or_equal_to: 0 }
validate :date_is_future?
validate :date_is_future?, on: :create
validate :installments_apply?
validates :installments, numericality: { greater_than: 0, allow_nil: true }

enum status: {
active: 0,
paid: 3,
zutin marked this conversation as resolved.
Show resolved Hide resolved
canceled: 5
}

Expand Down
69 changes: 69 additions & 0 deletions app/models/base_fee_calculator.rb
sabinopa marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
class BaseFeeCalculator
def self.total_value(unit_type_id)
get_last_month_values(unit_type_id).sum do |value|
next 0 unless check_recurrence(value.base_fee)

fee = value.base_fee
total = fee.biweekly? ? (value.price_cents * 2) : value.price_cents
update_base_fee_counter(fee) if fee.limited?
total
end
end

def self.get_last_month_values(unit_type_id)
now = Time.zone.now
last_month = now.last_month
end_of_last_month = last_month.end_of_month
Value.where(unit_type_id:)
.joins(:base_fee)
.where(base_fees: { charge_day: ...end_of_last_month })
.where(base_fees: { status: :active })
end

def self.check_recurrence(base_fee)
recurrence = base_fee.recurrence

return check_monthly_recurrence(base_fee) if %w[bimonthly monthly biweekly].include?(recurrence)

check_yearly_recurrence(base_fee) if %w[semi_annual yearly].include?(recurrence)
end

def self.check_yearly_recurrence(base_fee)
current_date = Time.zone.today
charge_day = base_fee.charge_day
recurrence = base_fee.recurrence

last_month = current_date.last_month
return ((last_month.month - charge_day.month) % 6).zero? if recurrence == 'semi_annual'

return last_month.month == charge_day.month if recurrence == 'yearly'

false
end

def self.check_monthly_recurrence(base_fee)
current_date = Time.zone.today
charge_day = base_fee.charge_day
recurrence = base_fee.recurrence

if recurrence == 'bimonthly'
return current_date.last_month.month.even? if charge_day.month.even?
return current_date.last_month.month.odd? if charge_day.month.odd?
end

true
end

def self.update_base_fee_counter(base_fee)
zutin marked this conversation as resolved.
Show resolved Hide resolved
count = base_fee.counter
count += 1
base_fee.counter = count

base_fee.paid! if base_fee.counter == base_fee.installments

base_fee.save
end

private_class_method :check_monthly_recurrence, :check_yearly_recurrence, :check_recurrence,
:get_last_month_values, :update_base_fee_counter
end
77 changes: 25 additions & 52 deletions app/models/bill_calculator.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
class BillCalculator
def self.calculate_total_fees(unit)
base_fees = calculate_base_fees(unit.unit_type_id)
base_fees = BaseFeeCalculator.total_value(unit.unit_type_id)
shared_fees = calculate_shared_fees(unit.id)
base_fees + shared_fees
single_charges = calculate_single_charges(unit.id)
rent_fee = check_rent_fee(unit.id)
base_fees + shared_fees + single_charges + rent_fee
end

def self.calculate_base_fees(unit_type_id)
def self.calculate_shared_fees(unit_id)
total_value = 0

get_last_month_values(unit_type_id).each do |value|
next unless check_recurrence(value.base_fee)

total_value += if value.base_fee.biweekly?
(value.price_cents * 2)
else
value.price_cents
end
fractions = get_last_month_fractions(unit_id)
fractions.each do |fraction|
total_value += fraction.value_cents
end
total_value
end

def self.calculate_shared_fees(unit_id)
def self.calculate_single_charges(unit_id)
total_value = 0
fractions = get_last_month_fractions(unit_id)
fractions.each do |fraction|
total_value += fraction.value_cents
charges = get_last_month_single_charges(unit_id)
charges.each do |charge|
total_value += charge.value_cents
end
total_value
end
Expand All @@ -38,52 +34,29 @@ def self.get_last_month_fractions(unit_id)
.joins(:shared_fee)
.where(shared_fees: { issue_date: ...now })
.where(shared_fees: { issue_date: start_of_last_month..end_of_last_month })
.where(shared_fees: { status: :active })
end

def self.get_last_month_values(unit_type_id)
def self.get_last_month_single_charges(unit_id)
now = Time.zone.now
last_month = now.last_month
start_of_last_month = last_month.beginning_of_month
end_of_last_month = last_month.end_of_month
Value.where(unit_type_id:)
.joins(:base_fee)
.where(base_fees: { charge_day: ...now })
.where(base_fees: { charge_day: start_of_last_month..end_of_last_month })
SingleCharge.where(unit_id:, issue_date: start_of_last_month..end_of_last_month, status: :active)
end

def self.check_recurrence(base_fee)
recurrence = base_fee.recurrence

return check_monthly_recurrence(base_fee) if %w[bimonthly monthly biweekly].include?(recurrence)
return check_yearly_recurrence(base_fee) if %w[semiannual yearly].include?(recurrence)

false
def self.check_rent_fee(unit_id)
rent_fee = get_last_month_rent_fee(unit_id)
rent_fee&.value_cents || 0
end

def self.check_yearly_recurrence(base_fee)
current_date = Time.zone.today
charge_day = base_fee.charge_day
recurrence = base_fee.recurrence

return ((current_date.month - charge_day.month) % 6).zero? if recurrence == 'semiannual'
return current_date.year != charge_day.year if recurrence == 'yearly'

false
end

def self.check_monthly_recurrence(base_fee)
current_date = Time.zone.today
charge_day = base_fee.charge_day
recurrence = base_fee.recurrence

if recurrence == 'bimonthly'
return current_date.month.even? if charge_day.month.even?
return current_date.month.odd? if charge_day.month.odd?
end

true
def self.get_last_month_rent_fee(unit_id)
now = Time.zone.now
last_month = now.last_month
start_of_last_month = last_month.beginning_of_month
end_of_last_month = last_month.end_of_month
RentFee.find_by(unit_id:, issue_date: start_of_last_month..end_of_last_month, status: :active)
end

private_class_method :check_monthly_recurrence, :check_yearly_recurrence, :check_recurrence, :get_last_month_values,
:get_last_month_fractions
private_class_method :get_last_month_fractions, :get_last_month_single_charges, :get_last_month_rent_fee
end
2 changes: 1 addition & 1 deletion app/views/bills/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<div class="px-5">
<div class=" d-flex justify-content-between">
<span class="text-muted text-lowercase fs-8"><%= BaseFee.model_name.human %>:</span>
<span><%= BillCalculator.calculate_base_fees(@unit.unit_type_id) %></span>
<span><%= BaseFeeCalculator.total_value(@unit.unit_type_id) %></span>
</div>
<div class=" d-flex justify-content-between">
<span class="text-muted text-lowercase fs-8"><%= SharedFee.model_name.human %>:</span>
Expand Down
2 changes: 1 addition & 1 deletion app/views/home/tenant_bill.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<div class="px-5">
<div class=" d-flex justify-content-between">
<span class="text-muted text-lowercase fs-8"><%= BaseFee.model_name.human %>:</span>
<span><%= BillCalculator.calculate_base_fees(@tenant.residence["unit_type_id"]) %></span>
<span><%= BaseFeeCalculator.total_value(@tenant.residence["unit_type_id"]) %></span>
</div>
<div class=" d-flex justify-content-between">
<span class="text-muted text-lowercase fs-8"><%= SharedFee.model_name.human %>:</span>
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20240717190806_add_counter_to_base_fees.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddCounterToBaseFees < ActiveRecord::Migration[7.1]
def change
add_column :base_fees, :counter, :integer, default: 0
end
end
1 change: 1 addition & 0 deletions db/schema.rb

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

3 changes: 2 additions & 1 deletion spec/factories/base_fees.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
interest_rate { 2 }
late_fine { 20 }
limited { false }
charge_day { 10.days.from_now }
charge_day { Time.zone.now }
recurrence { :monthly }
condo_id { nil }
installments { nil }
counter { 0 }
end
end
2 changes: 1 addition & 1 deletion spec/factories/shared_fees.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FactoryBot.define do
factory :shared_fee do
description { 'Conta de Luz' }
issue_date { 10.days.from_now.to_date }
issue_date { Time.zone.now }
total_value_cents { 1000 }
condo_id { nil }
end
Expand Down
2 changes: 1 addition & 1 deletion spec/factories/single_charges.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
factory :single_charge do
unit_id { 1 }
value_cents { 5000 }
issue_date { 5.days.from_now.to_date }
issue_date { Time.zone.now }
description { 'Detalhes de uma cobrança avulsa ' }
charge_type { 0 }
condo_id { 1 }
Expand Down
55 changes: 43 additions & 12 deletions spec/jobs/generate_monthly_bill_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
units << Unit.new(id: 1, area: 100, floor: 1, number: '11', unit_type_id: 1, condo_id: 1,
condo_name: 'Prédio lindo', tenant_id: 1, owner_id: 1, description: 'Com varanda')

shared_fee = create(:shared_fee, description: 'Descrição', issue_date: 10.days.from_now.to_date,
shared_fee = create(:shared_fee, description: 'Descrição', issue_date: Time.zone.now,
total_value: 30_000_00, condo_id: condos.first.id)
create(:shared_fee_fraction, shared_fee:, unit_id: 1, value_cents: 300_00)
base_fee = create(:base_fee, condo_id: 1)
Expand All @@ -24,7 +24,7 @@
allow(Unit).to receive(:find).and_return(units.first)
allow(Unit).to receive(:all).and_return(units)

travel_to 35.days.from_now do
travel_to 1.month.from_now do
units.each do |unit|
GenerateMonthlyBillJob.perform_now(unit, condo.id)
end
Expand Down Expand Up @@ -56,9 +56,9 @@
condo_name: 'Outro prédio', tenant_id: 2, owner_id: 3, description: 'Com varanda')
second_units << Unit.new(id: 4, area: 400, floor: 4, number: '41', unit_type_id: 2, condo_id: 2,
condo_name: 'Outro prédio', tenant_id: 2, owner_id: 4, description: 'Com varanda')
first_shared_fee = create(:shared_fee, description: 'Conta de Luz', issue_date: 10.days.from_now.to_date,
first_shared_fee = create(:shared_fee, description: 'Conta de Luz', issue_date: Time.zone.now,
total_value: 30_000_00, condo_id: condos.first.id)
second_shared_fee = create(:shared_fee, description: 'Conta de Agua', issue_date: 5.days.from_now.to_date,
second_shared_fee = create(:shared_fee, description: 'Conta de Agua', issue_date: Time.zone.now,
total_value: 25_000_00, condo_id: condos.last.id)
create(:shared_fee_fraction, shared_fee: first_shared_fee,
unit_id: 1, value_cents: 111_00)
Expand All @@ -81,7 +81,7 @@
second_units.last)
allow(Unit).to receive(:all).and_return(first_units, second_units)

travel_to 35.days.from_now do
travel_to 1.month.from_now do
first_units.each do |unit|
condo_id = first_unit_types.first.condo_id
GenerateMonthlyBillJob.perform_now(unit, condo_id)
Expand Down Expand Up @@ -110,12 +110,12 @@
units = []
units << Unit.new(id: 1, area: 100, floor: 1, number: '11', unit_type_id: 1, condo_id: 1,
condo_name: 'Prédio lindo', tenant_id: 1, owner_id: 1, description: 'Com varanda')
shared_fee = create(:shared_fee, description: 'Descrição', issue_date: 10.days.from_now.to_date,
shared_fee = create(:shared_fee, description: 'Descrição', issue_date: Time.zone.now,
total_value: 30_000_00, condo_id: condos.first.id)
create(:shared_fee_fraction, shared_fee:, unit_id: 1, value_cents: 300_00)
first_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: 10.days.from_now)
second_base_fee = create(:base_fee, condo_id: 1, recurrence: :biweekly, charge_day: 2.days.from_now)
third_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: 45.days.from_now)
first_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: Time.zone.now)
second_base_fee = create(:base_fee, condo_id: 1, recurrence: :biweekly, charge_day: Time.zone.now)
third_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: 2.months.from_now)
create(:value, price_cents: 100_00, base_fee_id: first_base_fee.id)
create(:value, price_cents: 200_00, base_fee_id: second_base_fee.id)
create(:value, price_cents: 90_00, base_fee_id: third_base_fee.id)
Expand All @@ -125,7 +125,7 @@
allow(Unit).to receive(:find).and_return(units.first)
allow(Unit).to receive(:all).and_return(units)

travel_to 35.days.from_now do
travel_to 1.month.from_now do
units.each do |unit|
condo_id = unit_types.first.condo_id
GenerateMonthlyBillJob.perform_now(unit, condo_id)
Expand Down Expand Up @@ -153,11 +153,10 @@
total_value: 13_000_00, condo_id: condos.first.id)
create(:shared_fee_fraction, shared_fee: first_shared_fee, unit_id: 1, value_cents: 200_00)
create(:shared_fee_fraction, shared_fee: second_shared_fee, unit_id: 1, value_cents: 130_00)
first_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: Time.zone.now)
first_base_fee = create(:base_fee, condo_id: 1, recurrence: :yearly, charge_day: Time.zone.now)
second_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: 1.month.from_now)
create(:value, price_cents: 150_00, base_fee_id: first_base_fee.id)
create(:value, price_cents: 111_11, base_fee_id: second_base_fee.id)
allow(Condo).to receive(:all).and_return(condos)
allow(Condo).to receive(:find).and_return(condos.first)
allow(UnitType).to receive(:all).and_return(unit_types)
allow(Unit).to receive(:find).and_return(units.first)
Expand All @@ -176,6 +175,38 @@
end
end

it 'e retorna os valores de todas as contas fixas mensais' do
condos = []
condos << Condo.new(id: 1, name: 'Prédio lindo', city: 'Cidade maravilhosa')
unit_types = []
unit_types << UnitType.new(id: 1, description: 'Apartamento 1 quarto', metreage: 100, fraction: 1.0,
unit_ids: [1])
units = []
units << Unit.new(id: 1, area: 100, floor: 1, number: 1, unit_type_id: 1)
first_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: Time.zone.now)
second_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: 1.month.from_now)
third_base_fee = create(:base_fee, condo_id: 1, recurrence: :monthly, charge_day: 2.months.from_now)
create(:value, price_cents: 100_00, base_fee_id: first_base_fee.id)
create(:value, price_cents: 111_11, base_fee_id: second_base_fee.id)
create(:value, price_cents: 333_33, base_fee_id: third_base_fee.id)
allow(Condo).to receive(:find).and_return(condos.first)
allow(UnitType).to receive(:all).and_return(unit_types)
allow(Unit).to receive(:find).and_return(units.first)
allow(Unit).to receive(:all).and_return(units)

travel_to 5.months.from_now do
units.each do |unit|
unit_types.first.condo_id
GenerateMonthlyBillJob.perform_now(unit, condos.first.id)
end

expect(Bill.count).to eq 1
expect(Bill.first.issue_date).to eq Time.zone.today.beginning_of_month
expect(Bill.first.due_date).to eq Time.zone.today.beginning_of_month + 9.days
expect(Bill.first.total_value_cents).to eq 544_44
end
end

it 'e não possui taxas para a fatura atual' do
condos = []
condos << Condo.new(id: 1, name: 'Prédio lindo', city: 'Cidade maravilhosa')
Expand Down
Loading
Loading