Skip to content

Commit

Permalink
Merge pull request #131 from TreinaDev/lanca-multas
Browse files Browse the repository at this point in the history
Implementa Lançamento de Multas pelo Síndico
  • Loading branch information
luckslima authored Jul 22, 2024
2 parents 585d451 + 7e17c07 commit 37711bd
Show file tree
Hide file tree
Showing 25 changed files with 784 additions and 7 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ gem 'faraday'
gem 'image_processing', '>= 1.2'
gem 'jbuilder'
gem 'jsbundling-rails'
gem 'money-rails', '~> 1.12'
gem 'puma', '~> 6.0'
gem 'simple_calendar'
gem 'sprockets-rails'
Expand Down
10 changes: 10 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ GEM
mini_magick (4.13.2)
mini_mime (1.1.5)
minitest (5.24.1)
monetize (1.13.0)
money (~> 6.12)
money (6.19.0)
i18n (>= 0.6.4, <= 2)
money-rails (1.15.0)
activesupport (>= 3.0)
monetize (~> 1.9)
money (~> 6.13)
railties (>= 3.0)
msgpack (1.7.2)
mutex_m (0.2.0)
net-http (0.4.1)
Expand Down Expand Up @@ -342,6 +351,7 @@ DEPENDENCIES
image_processing (>= 1.2)
jbuilder
jsbundling-rails
money-rails (~> 1.12)
puma (~> 6.0)
rails (~> 7.1.3.1)
rspec-rails
Expand Down
8 changes: 7 additions & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ def authorize_super_manager
not_authorized_redirect unless current_manager.is_super
end

def authorize_condo_manager_superintendent(condo)
return if authorize_manager(condo) || (authorize_resident(condo) && current_resident&.superintendent)

not_authorized_redirect
end

def authorize_condo_manager(condo)
not_authorized_redirect unless authorize_manager(condo)
end
Expand All @@ -44,7 +50,7 @@ def authorize_manager(condo)
end

def authorize_resident(condo)
resident_signed_in? && condo.residents.include?(current_resident)
resident_signed_in? && condo && condo.residents.include?(current_resident)
end

def not_authorized_redirect
Expand Down
85 changes: 85 additions & 0 deletions app/controllers/fines_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
class FinesController < ApplicationController
before_action :set_condo, only: %i[new create]
before_action :authorize_superintendent, only: %i[new create]
before_action :set_breadcrumbs_for_register, only: %i[new create]

def new
@fine = SingleCharge.new(condo: @condo)
end

def create
@fine = SingleCharge.new(fine_params)

if @fine.valid?
return redirect_to @condo if post_response

flash.now.alert = t('alerts.single_charge.server_error')
else
flash.now.alert = t('alerts.single_charge.fine_not_created')
end
render 'new', status: :unprocessable_entity
end

private

def set_breadcrumbs_for_register
add_breadcrumb @condo.name.to_s, @condo
add_breadcrumb I18n.t('breadcrumb.fine.new')
end

def authorize_superintendent
return if resident_signed_in? && @condo.superintendent && @condo.superintendent.tenant == current_resident

redirect_to root_path
end

def set_condo
@condo = Condo.find params[:condo_id]
end

def find_tower_and_floor
return unless params['single_charge']

tower = Tower.find_by(id: params['single_charge']['tower_id'])
return tower.floors[params['single_charge']['floor'].to_i - 1] if tower

nil
end

def find_unit_id
floor = find_tower_and_floor

return floor.units[params['single_charge']['unit'].to_i - 1].id if floor

nil
end

def fine_params
params.require(:single_charge)
.permit(:value, :description)
.merge({ charge_type: :fine, condo: @condo, unit_id: find_unit_id })
end

def single_charge_params
{ single_charge: {
description: @fine.description,
value_cents: @fine.value_cents,
charge_type: @fine.charge_type,
issue_date: Time.zone.today,
condo_id: @fine.condo.id,
common_area_id: nil,
unit_id: @fine.unit.id
} }
end

def post_response
request = Faraday.new(url: Rails.configuration.api['base_url'].to_s)
.post('/api/v1/single_charges/', single_charge_params.to_json,
'Content-Type' => 'application/json')
return flash.notice = "Multa lançada com sucesso para a #{@fine.unit.print_identifier}" if request.success?

nil
rescue Faraday::ConnectionFailed
nil
end
end
15 changes: 10 additions & 5 deletions app/controllers/residents_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
class ResidentsController < ApplicationController
before_action :authenticate_manager!, only: %i[new create find_towers show]
before_action :authenticate_manager!, only: %i[new create show]
before_action :set_resident, only: %i[update edit_photo update_photo show]
before_action :authenticate_resident!, only: %i[update edit_photo update_photo]
before_action :set_condo, only: %i[find_towers]
before_action -> { authorize_condo_manager_superintendent(@condo) }, only: %i[find_towers]

def show
add_breadcrumb I18n.t('breadcrumb.resident.show')
Expand Down Expand Up @@ -46,13 +48,12 @@ def update
end

def find_towers
condo = Condo.find_by(id: params[:id])
return render status: :not_found, json: [] unless condo
return render status: :not_found, json: [] unless @condo

towers = condo.towers
towers = @condo.towers
return render status: :not_found, json: [] if towers.empty?

render json: condo.towers.to_json(only: %i[id name units_per_floor floor_quantity])
render json: @condo.towers.to_json(only: %i[id name units_per_floor floor_quantity])
end

def confirm
Expand Down Expand Up @@ -93,6 +94,10 @@ def find_unit_id

private

def set_condo
@condo = Condo.find_by(id: params[:id])
end

def authenticate_resident!
return redirect_to root_path if manager_signed_in?

Expand Down
40 changes: 40 additions & 0 deletions app/javascript/controllers/fines_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ['condo', 'tower', 'floor', 'unit']

searchTowers(condoId) {
fetch(`${window.origin}/residents/find_towers?id=${condoId}`)
.then((response) => {
return response.json()
})
.then((towers) => {
this.towerTarget.innerHTML = ""
towers.forEach(tower => {
this.towerTarget.options.add(new Option(tower.name, tower.id))
});
this.towers = towers
this.changeTower()
})
.catch(() => { console.log('Towers not found') })
}

connect() {
this.searchTowers(this.element.getAttribute('condo-id'))
}

changeTower() {
let tower = this.towers[this.towerTarget.selectedIndex]
console.log(tower)
this.unitTarget.innerHTML = ""
this.floorTarget.innerHTML = ""

for (let floor = 1; floor <= tower.floor_quantity; floor++) {
this.floorTarget.options.add(new Option(`${floor}`, `${floor}`))
}

for (let unit = 1; unit <= tower.units_per_floor; unit++) {
this.unitTarget.options.add(new Option(`${unit}`, `${unit}`))
}
}
}
3 changes: 3 additions & 0 deletions app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ application.register("resident", ResidentController)

import UnitsController from "./units_controller"
application.register("units", UnitsController)

import FinesController from "./fines_controller"
application.register("fines", FinesController)
22 changes: 22 additions & 0 deletions app/models/single_charge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class SingleCharge < ApplicationRecord
belongs_to :condo
belongs_to :unit
belongs_to :common_area, optional: true

validates :value_cents, :charge_type, presence: true
validates :description, presence: true, if: -> { charge_type == 'fine' }

validate :unit_valid?

enum charge_type: { fine: 0, common_area_fee: 1 }

monetize :value_cents, as: :value, with_model_currency: :currency

private

def unit_valid?
return true if unit&.owner

errors.add(:unit, 'não possui um proprietário.')
end
end
4 changes: 4 additions & 0 deletions app/models/unit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ def short_identifier
"#{floor.identifier}#{floor.units.index(self) + 1}"
end

def identifier
floor.units.index(self) + 1
end

def tower_identifier
"#{floor.tower.name} - #{short_identifier}"
end
Expand Down
13 changes: 13 additions & 0 deletions app/views/condos/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@
<% end %>
<%= render 'condos/dashboard/common_area_list' %>
<% if resident_signed_in? && current_resident&.superintendent %>
<%= link_to new_condo_fine_path(@condo), class:"btn py-2 rounded-pill d-flex justify-content-center align-items-center mb-2 shadow-sm mt-2", style: "width: 100%; background-color: #FDE879;" do %>
<strong>Lançar Multa</strong>
<svg xmlns="http://www.w3.org/2000/svg" width="19" height="18" fill="currentColor" class="bi bi-cash-coin pb-0 ms-1" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M11 15a4 4 0 1 0 0-8 4 4 0 0 0 0 8m5-4a5 5 0 1 1-10 0 5 5 0 0 1 10 0"/>
<path d="M9.438 11.944c.047.596.518 1.06 1.363 1.116v.44h.375v-.443c.875-.061 1.386-.529 1.386-1.207 0-.618-.39-.936-1.09-1.1l-.296-.07v-1.2c.376.043.614.248.671.532h.658c-.047-.575-.54-1.024-1.329-1.073V8.5h-.375v.45c-.747.073-1.255.522-1.255 1.158 0 .562.378.92 1.007 1.066l.248.061v1.272c-.384-.058-.639-.27-.696-.563h-.668zm1.36-1.354c-.369-.085-.569-.26-.569-.522 0-.294.216-.514.572-.578v1.1zm.432.746c.449.104.655.272.655.569 0 .339-.257.571-.709.614v-1.195z"/>
<path d="M1 0a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4.083q.088-.517.258-1H3a2 2 0 0 0-2-2V3a2 2 0 0 0 2-2h10a2 2 0 0 0 2 2v3.528c.38.34.717.728 1 1.154V1a1 1 0 0 0-1-1z"/>
<path d="M9.998 5.083 10 5a2 2 0 1 0-3.132 1.65 6 6 0 0 1 3.13-1.567"/>
</svg>
<% end %>
<% end %>

</div>

<div class="accordion mt-2">
Expand Down
42 changes: 42 additions & 0 deletions app/views/fines/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<div class="bg-white rounded-5 py-4 px-5 shadow mt-5">
<h2 class="text-center mt-4">Lançar Multa</h2>
<%= form_with model: [@condo, @fine], url: condo_fines_path(@condo, @fine) do |f| %>
<div id="form_data" data-controller="fines" condo-id='<%= @condo.id %>'>
<div class="d-flex p-2">
<div class="form-group col-md-4 pe-3">
<%= f.label :tower_id %>
<%= f.collection_select :tower_id, {}, {}, {}, {}, :'data-fines-target' => "tower", :'data-action' => "change->fines#changeTower", class: "form-control form-select #{'is-invalid' if @fine.errors[:tower_id].any?}" %>
<%= render("shared/errors", model: @fine, attribute: :tower_id) if @fine.errors[:tower_id].any? %>
</div>

<div class="form-group col-md-4 pe-3">
<%= f.label :floor %>
<%= f.collection_select :floor, {}, {}, {}, {}, :'data-fines-target' => "floor", class: "form-control form-select #{'is-invalid' if @fine.errors[:floor].any?}" %>
<%= render("shared/errors", model: @fine, attribute: :floor) if @fine.errors[:floor].any? %>
</div>

<div class="form-group col-md-4 pe-3">
<%= f.label :unit %>
<%= f.collection_select :unit, {}, {}, {}, {}, :'data-fines-target' => "unit", class: "form-control form-select #{'is-invalid' if @fine.errors[:unit].any?}" %>
<%= render("shared/errors", model: @fine, attribute: :unit) if @fine.errors[:unit].any? %>
</div>
</div>
<div class="d-flex p-2">
<div class="form-group col-md-10 pe-2">
<%= f.label :description, class: "form-label" %>
<%= f.text_field :description, placeholder: 'Descreva o motivo da multa...', class: "form-control #{'is-invalid' if @fine.errors[:description].any?}" %>
<%= render("shared/errors", model: @fine, attribute: :description) if @fine.errors[:description].any? %>
</div>

<div class="form-group col-md-2 pe-3">
<%= f.label :value, class: "form-label" %>
<%= f.text_field :value, placeholder: '0,00', step: '0.01', pattern: '\d+(\,\d{2})?', class: "form-control #{'is-invalid' if @fine.errors[:value].any?}" %>
<%= render("shared/errors", model: @fine, attribute: :value) if @fine.errors[:value].any? %>
</div>
</div>
<div class="form-group d-flex justify-content-center">
<%= f.submit 'Lançar Multa', class: "btn btn-dark rounded-pill px-4 mt-3" %>
</div>
</div>
<% end %>
</div>
Loading

0 comments on commit 37711bd

Please sign in to comment.