Add contribution actions and member deletion

This commit is contained in:
Hugo Peixoto 2022-06-25 21:27:02 +01:00
parent 57e976ef96
commit 882d0a51ed
17 changed files with 302 additions and 75 deletions

View File

@ -1,7 +1,25 @@
/* Application styles */
table { border: 1px solid black; }
body {
max-width: 800px;
margin: 0 auto;
}
td, th { padding: 10px; }
table {
border: 1px solid black;
border-collapse: collapse;
margin: 20px 0;
width: calc(100% - 42px);
}
.new_contribution_form { max-width: 600px; }
td, th { padding: 10px; text-align: left; }
table.zebra tr:nth-child(2n) { background-color: #eee; }
table.lined tr:nth-child(n + 2) td { border-top: 1px solid #eee; }
ul {
padding-left: 5px;
margin: 0;
}
li { padding: 5px 0px; }

View File

@ -1,6 +1,6 @@
class ContributionsController < ApplicationController
before_action :set_member, only: %i[ new create ]
before_action :set_contribution, only: %i[ edit ]
before_action :set_contribution, only: %i[ edit update delete destroy ]
# GET /members/new
def new
@ -27,14 +27,26 @@ class ContributionsController < ApplicationController
end
end
## PATCH/PUT /members/1
#def update
# if @member.update(member_params)
# redirect_to @member, notice: "Member was successfully updated."
# else
# render :edit, status: :unprocessable_entity
# end
#end
# PATCH/PUT /members/1
def update
if @contribution.update(contribution_params)
redirect_to @contribution.member, notice: "Contribution was successfully updated."
else
render :edit, status: :unprocessable_entity
end
end
#
# GET /members/1/delete
def delete
end
# DELETE /members/1
def destroy
@member = @contribution.member
@contribution.destroy
redirect_to @member, notice: "Member personal data permanently removed."
end
private
# Use callbacks to share common setup or constraints between actions.

View File

@ -1,5 +1,5 @@
class MembersController < ApplicationController
before_action :set_member, only: %i[ show edit update destroy ]
before_action :set_member, only: %i[ show edit update delete destroy ]
helper_method :sort_params
# GET /members
@ -46,6 +46,17 @@ class MembersController < ApplicationController
end
end
# GET /members/1/delete
def delete
end
# DELETE /members/1
def destroy
@member.remove_personal_information!
redirect_to members_path, notice: "Member personal data permanently removed."
end
private
# Use callbacks to share common setup or constraints between actions.
def set_member

View File

@ -2,4 +2,8 @@ module ApplicationHelper
def member_status(status)
t("members.status.#{status}")
end
def notification_status(status)
t("notifications.status.#{status}")
end
end

View File

@ -1,4 +1,5 @@
class Member < ApplicationRecord
default_scope { where(excluded: false) }
has_many :contributions
has_many :notifications
@ -10,6 +11,17 @@ class Member < ApplicationRecord
update(status: expected_status)
end
def remove_personal_information!
update(
excluded: true,
display_name: "",
email: nil,
identification_number: "",
category: "",
address: "",
)
end
def expected_status
if joined_on.nil?
:pending

View File

@ -0,0 +1,28 @@
<p style="color: green"><%= notice %></p>
<h1><%= t('contributions.delete.title') %></h1>
<p><%= t('contributions.delete.confirmation_message') %></p>
<table>
<tr><td><%= t('members.attributes.display_name') %></td><td><%= @contribution.member.display_name %></td></tr>
<tr><td><%= t('members.attributes.email') %></td><td><%= @contribution.member.email %></td></tr>
<tr><td><%= t('members.attributes.category') %></td><td><%= @contribution.member.category %></td></tr>
<tr><td><%= t('members.attributes.identification_number') %></td><td><%= @contribution.member.identification_number %></td></tr>
<tr><td><%= t('members.attributes.address') %></td><td><%= simple_format @contribution.member.address %></td></tr>
<tr><td><%= t('members.attributes.joined_on') %></td><td><%= @contribution.member.joined_on %></td></tr>
<tr><td><%= t('members.attributes.expires_on') %></td><td><%= @contribution.member.expires_on %></td></tr>
<tr><td><%= t('members.attributes.status') %></td><td><%= @contribution.member.status %></td></tr>
</table>
<%= form_with model: @contribution, method: :delete do |form| %>
<div>
<%= form.submit t('contributions.delete.actions.submit') %>
</div>
<div>
<%= link_to t('contributions.delete.actions.go_back'), members_path %>
</div>
<% end %>

View File

@ -1,22 +1,36 @@
editando contriboot
<h1><%= t('contributions.edit.title') %></h1>
<%= form_with(model: @contribution) do |form| %>
<table>
<table>
<tr>
<td>Amount</td>
<td>€<%= @contribution.eurocents %></td>
<td><%= t('contributions.edit.member') %></td>
<td>
<%= link_to @contribution.member do %>
<%= @contribution.member.number %>.
<%= @contribution.member.display_name %>
&lt;<%= @contribution.member.email %>&gt;
<% end %>
</td>
</tr>
<tr>
<td>Payment date</td>
<td>€<%= @contribution.payment_on %></td>
<td><label><%= t('contributions.attributes.amount') %></label></td>
<td><%= form.number_field :eurocents, required: true %></td>
</tr>
<tr>
<td>Payment method</td>
<td><%= @contribution.payment_method %></td>
<td><label><%= t('contributions.attributes.payment_on') %></label></td>
<td><%= form.date_field :payment_on %></td>
</tr>
<tr>
<td>Payment reference</td>
<td><%= @contribution.payment_reference %></td>
<td><label><%= t('contributions.attributes.payment_method') %></label></td>
<td><%= form.select :payment_method, %w[iban mbway multibanco] %></td>
</tr>
</table>
<tr>
<td><label><%= t('contributions.attributes.payment_reference') %></label></td>
<td><%= form.text_field :payment_reference %></td>
</tr>
</table>
<div>
<%= form.submit t('contributions.edit.actions.submit') %>
</div>
<% end %>

View File

@ -1,39 +1,46 @@
<h1>Registering contribution for <%= @member.display_name %></h1>
<table>
<tr><td>Member number</td><td><%= @member.number %></td></tr>
<tr><td>Joined on</td><td><%= @member.joined_on %></td></tr>
<tr><td>Expires on</td><td><%= @member.expires_on %></td></tr>
</table>
<h1><%= t('contributions.new.title') %></h1>
<%= form_with(model: [@member, @contribution]) do |form| %>
<table class="new_contribution_form">
<tr>
<td><label for="contribution_eurocents">Amount</label></td>
<td><%= t('contributions.new.member') %></td>
<td>
<%= link_to @member do %>
<%= @member.number %>.
<%= @member.display_name %>
&lt;<%= @member.email %>&gt;
<% end %>
</td>
</tr>
<tr>
<td><label for="contribution_eurocents"><%= t('contributions.attributes.amount') %></label></td>
<td><%= form.number_field :eurocents %></td>
</tr>
<tr>
<td><label for="contribution_payment_on">Payment date</label></td>
<td><label for="contribution_payment_on"><%= t('contributions.attributes.payment_on') %></label></td>
<td><%= form.date_field :payment_on %></td>
</tr>
<tr>
<td><label for="contribution_payment_method">Payment method</label></td>
<td><label for="contribution_payment_method"><%= t('contributions.attributes.payment_method') %></label></td>
<td><%= form.select :payment_method, %w[iban mbway multibanco] %></td>
</tr>
<tr>
<td><label for="contribution_payment_reference">Referência</label></td>
<td><label for="contribution_payment_reference"><%= t('contributions.attributes.payment_reference') %></label></td>
<td><%= form.text_field :payment_reference %></td>
</tr>
<tr>
<td colspan=2>
Adding a contribution will automatically bump the membership expiration
date by one year. If it's the first contribution for this member, the
join date will be set to the payment date and the expiration date one
year after that. You can override the expiration date by setting a date
below:
<%= t('contributions.new.expires_on_warning') %>
</td>
</tr>
<tr>
<td><%= t('members.attributes.joined_on') %></td>
<td><%= @member.joined_on %></td>
</tr>
<tr>
<td><%= t('members.attributes.expires_on') %></td>
<td><%= @member.expires_on %></td>
</tr>
<tr>
<td><label for="member_expires_on">Nova data de expiração</label></td>
<td>

View File

@ -0,0 +1,27 @@
<p style="color: green"><%= notice %></p>
<h1><%= t('members.delete.title') %></h1>
<p><%= t('members.delete.confirmation_message') %></p>
<table>
<tr><td><%= t('members.attributes.display_name') %></td><td><%= @member.display_name %></td></tr>
<tr><td><%= t('members.attributes.email') %></td><td><%= @member.email %></td></tr>
<tr><td><%= t('members.attributes.category') %></td><td><%= @member.category %></td></tr>
<tr><td><%= t('members.attributes.identification_number') %></td><td><%= @member.identification_number %></td></tr>
<tr><td><%= t('members.attributes.address') %></td><td><%= simple_format @member.address %></td></tr>
<tr><td><%= t('members.attributes.joined_on') %></td><td><%= @member.joined_on %></td></tr>
<tr><td><%= t('members.attributes.expires_on') %></td><td><%= @member.expires_on %></td></tr>
<tr><td><%= t('members.attributes.status') %></td><td><%= @member.status %></td></tr>
</table>
<%= form_with model: @member, method: :delete do |form| %>
<div>
<%= form.submit t('members.delete.actions.submit') %>
</div>
<div>
<%= link_to t('members.delete.actions.go_back'), members_path %>
</div>
<% end %>

View File

@ -14,8 +14,7 @@
<% end %>
<% end %>
<table id="members">
<thead>
<table class='zebra'>
<tr>
<th><%= link_to_current_with_sort t('members.attributes.number'), 'number.asc' %></th>
<th><%= link_to_current_with_sort t('members.attributes.status'), 'status.asc' %></th>
@ -25,7 +24,6 @@
<th><%= link_to_current_with_sort t('members.attributes.expires_on'), 'expires_on.asc' %></th>
<th><%= t('members.index.actions.title') %></th>
</tr>
</thead>
<% @members.each do |member| %>
<tr id="<%= dom_id member %>">
<td><%= member.number %></td>
@ -35,9 +33,12 @@
<td><%= member.joined_on %></td>
<td><%= member.expires_on %></td>
<td>
<%= link_to t('members.index.actions.show'), member %> |
<%= link_to t('members.index.actions.edit'), edit_member_path(member) %> |
<%= link_to t('members.index.actions.new_contribution'), new_member_contribution_path(member) %>
<ul>
<li><%= link_to t('members.index.actions.show'), member %></li>
<li><%= link_to t('members.index.actions.edit'), edit_member_path(member) %></li>
<li><%= link_to t('members.index.actions.new_contribution'), new_member_contribution_path(member) %></li>
<li><%= link_to t('members.index.actions.delete'), delete_member_path(member) %></li>
</ul>
</td>
</tr>
<% end %>

View File

@ -2,7 +2,7 @@
<h1><%= t('members.show.title') %></h1>
<table>
<table class='lined'>
<tr><td><%= t('members.attributes.display_name') %></td><td><%= @member.display_name %></td></tr>
<tr><td><%= t('members.attributes.email') %></td><td><%= @member.email %></td></tr>
<tr><td><%= t('members.attributes.category') %></td><td><%= @member.category %></td></tr>
@ -19,12 +19,12 @@
<h2><%= t('members.show.contribution_history') %></h2>
<table>
<table class='zebra'>
<tr>
<th>Payment date</th>
<th>Payment method</th>
<th>Payment reference</th>
<th>Amount</th>
<th><%= t('contributions.attributes.payment_on') %></th>
<th><%= t('contributions.attributes.payment_method') %></th>
<th><%= t('contributions.attributes.payment_reference') %></th>
<th><%= t('contributions.attributes.amount') %></th>
</tr>
<% @member.contributions.each do |contribution| %>
<tr>
@ -33,8 +33,31 @@
<td><%= contribution.payment_reference %></td>
<td>€<%= contribution.eurocents %></td>
<td>
<%= link_to t('members.show.actions.edit_contribution'), edit_contribution_path(contribution) %>
<ul>
<li><%= link_to t('members.show.actions.edit_contribution'), edit_contribution_path(contribution) %></div>
<li><%= link_to t('members.show.actions.delete_contribution'), delete_contribution_path(contribution) %></li>
</ul>
</td>
</tr>
<% end %>
</table>
<h2></h2>
<h2><%= t('members.show.notifications') %></h2>
<table class='zebra'>
<tr>
<th><%= t('notifications.attributes.to_be_sent_on') %></th>
<th><%= t('notifications.attributes.template') %></th>
<th><%= t('notifications.attributes.status') %></th>
</tr>
<% @member.notifications.order(to_be_sent_on: :desc).each do |notification| %>
<tr>
<td><%= notification.to_be_sent_on %></td>
<td><code><%= notification.template %></code></td>
<td><%= notification_status(notification.status) %></td>
</tr>
<% end %>
</table>

View File

@ -44,6 +44,14 @@ en:
unemployed: "Unemployed"
student: "Student"
retired: "Retired"
contributions:
new:
expires_on_warning: |
Adding a contribution will automatically bump the membership expiration
date by one year. If it's the first contribution for this member, the
join date will be set to the payment date and the expiration date one
year after that. You can override the expiration date by setting a date
below.
notification_mailer:
expiration_in_60d:
subject: "ANSOL - Pagamento anual de quotas"

View File

@ -2,6 +2,16 @@ pt:
navigation:
members: "Lista de membros"
members:
delete:
confirmation_message: |
Tens a certeza que queres apagar o registo deste membro
permanentemente? Esta acção vai apagar todos os dados pessoais da
pessoa em questão, mas não irá apagar o registo de contribuições, caso
estas sejam necessárias por motivos legais/fiscais.
title: "Remover membro permanentemente"
actions:
submit: "Confirmar remoção"
go_back: "Cancelar"
index:
title: "Membros"
actions:
@ -16,6 +26,9 @@ pt:
actions:
edit: "Editar detalhes"
edit_contribution: "Editar"
delete_contribution: "Apagar"
contribution_history: "Histórico de contribuições"
notifications: "Notificações por correio electrónico"
edit:
title: "Editar detalhes de membro"
actions:
@ -46,6 +59,34 @@ pt:
unemployed: "Desempregado"
student: "Estudante"
retired: "Reformado"
notifications:
attributes:
to_be_sent_on: Data de envio
template: Modelo
status: Estado
status:
sent: Enviada
scheduled: Agendada
contributions:
attributes:
amount: Montante
payment_on: Data de pagamento
payment_method: Método de pagamento
payment_reference: Referência
edit:
member: "Membro"
title: "Editar dados de contribuição"
actions:
submit: "Gravar"
new:
member: "Membro"
title: "Registar contribuição"
expires_on_warning: |
Ao registar uma contribuição, o sistema estende a data de expiração por
um ano automaticamente. Se for a primeira contribuição deste membro, a
data de inscrição é automaticamente definida como a data de pagamento,
e a data de expiração passa a ser um ano após essa data. Podes definir
uma data de expiração manualmente usando o próximo campo.
notification_mailer:
expiration_in_60d:
subject: "ANSOL - Pagamento anual de quotas"

View File

@ -5,8 +5,15 @@ Rails.application.routes.draw do
# root "articles#index"
resources :members do
member do
get :delete
end
resources :contributions, only: [:new, :create]
end
resources :contributions, only: [:edit, :update]
resources :contributions, only: [:edit, :update, :destroy] do
member do
get :delete
end
end
end

View File

@ -0,0 +1,7 @@
class AddExcludedToMembers < ActiveRecord::Migration[7.0]
def change
change_table :members do |t|
t.boolean :excluded, default: false
end
end
end

View File

@ -0,0 +1,6 @@
class RemoveNullConstraintsFromMember < ActiveRecord::Migration[7.0]
def change
change_column_null :members, :email, true
change_column_null :members, :display_name, true
end
end

7
db/schema.rb generated
View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2022_06_24_134509) do
ActiveRecord::Schema[7.0].define(version: 2022_06_25_181824) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
@ -28,8 +28,8 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_24_134509) do
create_table "members", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.serial "number", null: false
t.string "email", null: false
t.string "display_name", null: false
t.string "email"
t.string "display_name"
t.string "identification_number"
t.string "status"
t.string "category"
@ -40,6 +40,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_06_24_134509) do
t.date "expires_on"
t.string "regular_ifthenpay_link"
t.string "reduced_ifthenpay_link"
t.boolean "excluded", default: false
t.index ["email"], name: "index_members_on_email", unique: true
t.index ["number"], name: "index_members_on_number", unique: true
end