Add first iteration
This commit is contained in:
parent
f97b01dd43
commit
57e976ef96
2
Gemfile
2
Gemfile
@ -11,9 +11,11 @@ gem "puma", "~> 5.0"
|
||||
gem "rails", "~> 7.0.3"
|
||||
gem "dotenv-rails"
|
||||
gem "pundit"
|
||||
gem "ransack"
|
||||
|
||||
group :development, :test do
|
||||
gem "debug", platforms: %i[ mri mingw x64_mingw ]
|
||||
gem "timecop"
|
||||
end
|
||||
|
||||
group :development do
|
||||
|
@ -159,10 +159,15 @@ GEM
|
||||
thor (~> 1.0)
|
||||
zeitwerk (~> 2.5)
|
||||
rake (13.0.6)
|
||||
ransack (3.2.1)
|
||||
activerecord (>= 6.1.5)
|
||||
activesupport (>= 6.1.5)
|
||||
i18n
|
||||
reline (0.3.1)
|
||||
io-console (~> 0.5)
|
||||
strscan (3.0.3)
|
||||
thor (1.2.1)
|
||||
timecop (0.9.5)
|
||||
timeout (0.3.0)
|
||||
tzinfo (2.0.4)
|
||||
concurrent-ruby (~> 1.0)
|
||||
@ -189,6 +194,8 @@ DEPENDENCIES
|
||||
puma (~> 5.0)
|
||||
pundit
|
||||
rails (~> 7.0.3)
|
||||
ransack
|
||||
timecop
|
||||
web-console
|
||||
|
||||
RUBY VERSION
|
||||
|
@ -1 +1,7 @@
|
||||
/* Application styles */
|
||||
|
||||
table { border: 1px solid black; }
|
||||
|
||||
td, th { padding: 10px; }
|
||||
|
||||
.new_contribution_form { max-width: 600px; }
|
||||
|
54
app/controllers/contributions_controller.rb
Normal file
54
app/controllers/contributions_controller.rb
Normal file
@ -0,0 +1,54 @@
|
||||
class ContributionsController < ApplicationController
|
||||
before_action :set_member, only: %i[ new create ]
|
||||
before_action :set_contribution, only: %i[ edit ]
|
||||
|
||||
# GET /members/new
|
||||
def new
|
||||
@contribution = Contribution.new
|
||||
end
|
||||
|
||||
# GET /members/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# POST /members
|
||||
def create
|
||||
@contribution = @member.contributions.build(contribution_params)
|
||||
|
||||
Contribution.transaction do
|
||||
if @contribution.save
|
||||
@member.handle_new_contribution(@contribution, params.dig(:contribution, :overriden_expires_on))
|
||||
@member.reset_status!
|
||||
|
||||
redirect_to @member, notice: "Contribution was successfully created."
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
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
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_member
|
||||
@member = Member.find(params[:member_id])
|
||||
end
|
||||
|
||||
def set_contribution
|
||||
@contribution = Contribution.find(params[:id])
|
||||
end
|
||||
|
||||
# Only allow a list of trusted parameters through.
|
||||
def contribution_params
|
||||
params.fetch(:contribution, {}).permit(:eurocents, :payment_method, :payment_on, :payment_reference)
|
||||
end
|
||||
end
|
||||
|
72
app/controllers/members_controller.rb
Normal file
72
app/controllers/members_controller.rb
Normal file
@ -0,0 +1,72 @@
|
||||
class MembersController < ApplicationController
|
||||
before_action :set_member, only: %i[ show edit update destroy ]
|
||||
helper_method :sort_params
|
||||
|
||||
# GET /members
|
||||
def index
|
||||
params.delete(:status) if params[:status] == 'any'
|
||||
params.delete(:category) if params[:category] == 'any'
|
||||
|
||||
@members = Member.all.order(sort_params.merge(number: :asc))
|
||||
@members = @members.ransack(display_name_or_email_or_identification_number_i_cont: params[:q], status_cont: params[:status], category_cont: params[:category]).result
|
||||
end
|
||||
|
||||
# GET /members/1
|
||||
def show
|
||||
end
|
||||
|
||||
# GET /members/new
|
||||
def new
|
||||
@member = Member.new
|
||||
end
|
||||
|
||||
# GET /members/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# POST /members
|
||||
def create
|
||||
@member = Member.new(member_params)
|
||||
|
||||
if @member.save
|
||||
@member.reset_status!
|
||||
redirect_to @member, notice: "Member was successfully created."
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /members/1
|
||||
def update
|
||||
if @member.update(member_params)
|
||||
@member.reload.reset_status!
|
||||
redirect_to @member, notice: "Member was successfully updated."
|
||||
else
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_member
|
||||
@member = Member.find(params[:id])
|
||||
end
|
||||
|
||||
# Only allow a list of trusted parameters through.
|
||||
def member_params
|
||||
params.fetch(:member, {}).permit(:display_name, :email, :identification_number, :category, :address, :joined_on, :expires_on)
|
||||
end
|
||||
|
||||
def sort_params
|
||||
field, direction = params.fetch(:sort, "").split(".")
|
||||
|
||||
directions = %w[ asc desc ]
|
||||
fields = %w[ number expires_on joined_on email status display_name ]
|
||||
|
||||
if directions.include?(direction) && fields.include?(field)
|
||||
{ field => direction }
|
||||
else
|
||||
{ number: :asc }
|
||||
end
|
||||
end
|
||||
end
|
@ -1,2 +1,5 @@
|
||||
module ApplicationHelper
|
||||
def member_status(status)
|
||||
t("members.status.#{status}")
|
||||
end
|
||||
end
|
||||
|
24
app/helpers/members_helper.rb
Normal file
24
app/helpers/members_helper.rb
Normal file
@ -0,0 +1,24 @@
|
||||
module MembersHelper
|
||||
def link_to_current_with_sort text, default_sort
|
||||
current_sort = stringify(sort_params)
|
||||
|
||||
pp [default_sort, current_sort]
|
||||
|
||||
if default_sort == current_sort
|
||||
link_to text, members_path(sort: invert_sort_order(current_sort))
|
||||
else
|
||||
link_to text, members_path(sort: default_sort)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stringify(sort)
|
||||
"#{sort.keys.first.to_s}.#{sort.values.first.to_s}"
|
||||
end
|
||||
|
||||
def invert_sort_order(sort)
|
||||
sort.sub(/\.(asc|desc)$/) { |x| x == '.asc' ? '.desc' : '.asc' }
|
||||
end
|
||||
end
|
19
app/lib/if_then_pay.rb
Normal file
19
app/lib/if_then_pay.rb
Normal file
@ -0,0 +1,19 @@
|
||||
require 'net/http'
|
||||
|
||||
module IfThenPay
|
||||
def self.generate_gateway_link(id:, amount:, description:)
|
||||
response = Net::HTTP.post(
|
||||
URI("https://ifthenpay.com/api/gateway/paybylink/#{ENV['IFTHENPAY_KEY']}"),
|
||||
JSON.generate({
|
||||
id: id,
|
||||
amount: amount.to_s,
|
||||
description: description.to_s,
|
||||
"lang": "pt",
|
||||
"expiredate": "",
|
||||
"accounts": ENV['IFTHENPAY_ACCOUNTS'],
|
||||
})
|
||||
)
|
||||
|
||||
JSON.parse(response.body)
|
||||
end
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: "from@example.com"
|
||||
default from: email_address_with_name(ENV['SMTP_FROM_ADDRESS'], ENV['SMTP_FROM_NAME'])
|
||||
layout "mailer"
|
||||
end
|
||||
|
68
app/mailers/notification_mailer.rb
Normal file
68
app/mailers/notification_mailer.rb
Normal file
@ -0,0 +1,68 @@
|
||||
class NotificationMailer < ApplicationMailer
|
||||
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.notification_mailer.expiration_in_60d.subject
|
||||
#
|
||||
def expiration_in_60d
|
||||
@notification = params[:notification]
|
||||
|
||||
mail to: params[:notification].member.email
|
||||
end
|
||||
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.notification_mailer.expiration_in_30d.subject
|
||||
#
|
||||
def expiration_in_30d
|
||||
@notification = params[:notification]
|
||||
|
||||
mail to: params[:notification].member.email
|
||||
end
|
||||
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.notification_mailer.expired.subject
|
||||
#
|
||||
def expired
|
||||
@notification = params[:notification]
|
||||
|
||||
mail to: params[:notification].member.email
|
||||
end
|
||||
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.notification_mailer.expired_30d_ago.subject
|
||||
#
|
||||
def expired_30d_ago
|
||||
@notification = params[:notification]
|
||||
|
||||
mail to: params[:notification].member.email
|
||||
end
|
||||
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.notification_mailer.expired_60d_ago.subject
|
||||
#
|
||||
def expired_60d_ago
|
||||
@notification = params[:notification]
|
||||
|
||||
mail to: params[:notification].member.email
|
||||
end
|
||||
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.notification_mailer.cancelled.subject
|
||||
#
|
||||
def cancelled
|
||||
@notification = params[:notification]
|
||||
|
||||
mail to: params[:notification].member.email
|
||||
end
|
||||
end
|
3
app/models/contribution.rb
Normal file
3
app/models/contribution.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class Contribution < ApplicationRecord
|
||||
belongs_to :member
|
||||
end
|
88
app/models/member.rb
Normal file
88
app/models/member.rb
Normal file
@ -0,0 +1,88 @@
|
||||
class Member < ApplicationRecord
|
||||
has_many :contributions
|
||||
has_many :notifications
|
||||
|
||||
def cancelled_on
|
||||
expires_on + 90.days
|
||||
end
|
||||
|
||||
def reset_status!
|
||||
update(status: expected_status)
|
||||
end
|
||||
|
||||
def expected_status
|
||||
if joined_on.nil?
|
||||
:pending
|
||||
elsif (joined_on + 6.months).future?
|
||||
:passive
|
||||
elsif expires_on.future?
|
||||
:active
|
||||
elsif cancelled_on.future?
|
||||
:expired
|
||||
else
|
||||
:cancelled
|
||||
end
|
||||
end
|
||||
|
||||
def handle_new_contribution(contribution, overriden_expires_on)
|
||||
if joined_on.nil?
|
||||
self.joined_on = contribution.payment_on
|
||||
self.expires_on = overriden_expires_on.presence || (joined_on + 1.year)
|
||||
else
|
||||
self.expires_on = overriden_expires_on.presence || expires_on + 1.year
|
||||
end
|
||||
|
||||
save!
|
||||
end
|
||||
|
||||
def regenerate_notifications
|
||||
notifications.where(status: 'scheduled').delete_all
|
||||
|
||||
return if expires_on.nil?
|
||||
|
||||
[
|
||||
{ to_be_sent_on: expires_on - 90.days, template: "expiration_in_60d" },
|
||||
{ to_be_sent_on: expires_on - 30.days, template: "expiration_in_30d" },
|
||||
{ to_be_sent_on: expires_on + 0.days, template: "expired" },
|
||||
{ to_be_sent_on: expires_on + 30.days, template: "expired_30d_ago" },
|
||||
{ to_be_sent_on: expires_on + 60.days, template: "expired_60d_ago" },
|
||||
{ to_be_sent_on: expires_on + 90.days, template: "cancelled" },
|
||||
].reject { |n| n[:to_be_sent_on].past? }.each do |n|
|
||||
notifications.create(n.merge(status: "scheduled"))
|
||||
end
|
||||
end
|
||||
|
||||
def generate_missing_ifthenpay_links!
|
||||
self.regular_ifthenpay_link = IfThenPay.generate_gateway_link(
|
||||
id: number,
|
||||
amount: "30.00",
|
||||
description: "Quotas ANSOL",
|
||||
) unless self.regular_ifthenpay_link.present?
|
||||
|
||||
self.reduced_ifthenpay_link = IfThenPay.generate_gateway_link(
|
||||
id: number,
|
||||
amount: "6.00",
|
||||
description: "Quotas ANSOL",
|
||||
) unless self.reduced_ifthenpay_link.present?
|
||||
|
||||
save!
|
||||
end
|
||||
|
||||
def self.reset_all_status!
|
||||
Member.all.each do |member|
|
||||
member.reset_status!
|
||||
end
|
||||
end
|
||||
|
||||
def self.generate_all_missing_ifthenpay_links!
|
||||
Member.all.each do |member|
|
||||
member.generate_missing_ifthenpay_links!
|
||||
end
|
||||
end
|
||||
|
||||
def self.regenerate_all_notifications
|
||||
Member.all.each do |member|
|
||||
member.regenerate_notifications
|
||||
end
|
||||
end
|
||||
end
|
20
app/models/notification.rb
Normal file
20
app/models/notification.rb
Normal file
@ -0,0 +1,20 @@
|
||||
class Notification < ApplicationRecord
|
||||
belongs_to :member
|
||||
|
||||
scope :scheduled_for_today, ->() { where(status: 'scheduled', to_be_sent_on: Date.today) }
|
||||
|
||||
def self.send_scheduled_for_today
|
||||
scheduled_for_today.each do |n|
|
||||
n.deliver!
|
||||
end
|
||||
end
|
||||
|
||||
def deliver!
|
||||
# actually send the email.
|
||||
NotificationMailer.with(notification: self).send(template).deliver_now!
|
||||
|
||||
update(status: 'sent', sent_at: Time.current)
|
||||
rescue
|
||||
# TODO: do something about failures
|
||||
end
|
||||
end
|
22
app/views/contributions/edit.html.erb
Normal file
22
app/views/contributions/edit.html.erb
Normal file
@ -0,0 +1,22 @@
|
||||
editando contriboot
|
||||
|
||||
<%= form_with(model: @contribution) do |form| %>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Amount</td>
|
||||
<td>€<%= @contribution.eurocents %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Payment date</td>
|
||||
<td>€<%= @contribution.payment_on %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Payment method</td>
|
||||
<td><%= @contribution.payment_method %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Payment reference</td>
|
||||
<td><%= @contribution.payment_reference %></td>
|
||||
</tr>
|
||||
</table>
|
||||
<% end %>
|
48
app/views/contributions/new.html.erb
Normal file
48
app/views/contributions/new.html.erb
Normal file
@ -0,0 +1,48 @@
|
||||
<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>
|
||||
|
||||
|
||||
<%= form_with(model: [@member, @contribution]) do |form| %>
|
||||
<table class="new_contribution_form">
|
||||
<tr>
|
||||
<td><label for="contribution_eurocents">Amount</label></td>
|
||||
<td><%= form.number_field :eurocents %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="contribution_payment_on">Payment date</label></td>
|
||||
<td><%= form.date_field :payment_on %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="contribution_payment_method">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><%= 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:
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="member_expires_on">Nova data de expiração</label></td>
|
||||
<td>
|
||||
<%= form.date_field :overriden_expires_on %>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<%= form.submit %>
|
||||
</div>
|
||||
<% end %>
|
@ -11,6 +11,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav>
|
||||
<%= link_to t('navigation.members'), members_path %>
|
||||
</nav>
|
||||
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -8,6 +8,12 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="max-width: 600px;">
|
||||
<div style="background-color: #041952; padding: 20px">
|
||||
<img src="https://hugopeixoto.net/images/ansol-logo-white.png?xxx" style="margin: 0px auto; max-width: 400px; display: block; color: white" alt="ANSOL" />
|
||||
</div>
|
||||
|
||||
<%= yield %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
0
app/views/members/_form.html.erb
Normal file
0
app/views/members/_form.html.erb
Normal file
2
app/views/members/_member.html.erb
Normal file
2
app/views/members/_member.html.erb
Normal file
@ -0,0 +1,2 @@
|
||||
<div id="<%= dom_id member %>">
|
||||
</div>
|
59
app/views/members/edit.html.erb
Normal file
59
app/views/members/edit.html.erb
Normal file
@ -0,0 +1,59 @@
|
||||
<h1><%= t('members.edit.title') %></h1>
|
||||
|
||||
<%= form_with(model: @member) do |form| %>
|
||||
<% if @member.errors.any? %>
|
||||
<div style="color: red">
|
||||
<h2><%= pluralize(@member.errors.count, "error") %> prohibited this member from being saved:</h2>
|
||||
|
||||
<ul>
|
||||
<% @member.errors.each do |error| %>
|
||||
<li><%= error.full_message %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><label><%= t('members.attributes.display_name') %></label></td>
|
||||
<td><%= form.text_field :display_name, required: true %></label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label><%= t('members.attributes.email') %></label></td>
|
||||
<td><%= form.email_field :email, required: true %></label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label><%= t('members.attributes.category') %></label></td>
|
||||
<td><%= form.select :category, %w{student retired unemployed employed} %></label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label><%= t('members.attributes.identification_number') %></label></td>
|
||||
<td><%= form.text_field :identification_number %></label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label><%= t('members.attributes.address') %></label></td>
|
||||
<td><%= form.text_area :address %></label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><%= t('members.edit.edit_dates_warning') %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="member_joined_on"><%= t('members.attributes.joined_on') %></label></td>
|
||||
<td><%= form.date_field :joined_on %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="member_expires_on"><%= t('members.attributes.expires_on') %></label></td>
|
||||
<td><%= form.date_field :expires_on %></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<%= form.submit %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<br>
|
||||
|
||||
<div>
|
||||
<%= link_to t('members.edit.actions.back_to_show'), @member %>
|
||||
</div>
|
44
app/views/members/index.html.erb
Normal file
44
app/views/members/index.html.erb
Normal file
@ -0,0 +1,44 @@
|
||||
<p style="color: green"><%= notice %></p>
|
||||
|
||||
<h1><%= t 'members.index.title' %></h1>
|
||||
|
||||
<%= link_to t('members.index.actions.new'), new_member_path %>
|
||||
|
||||
<%= form_with url: members_path, method: :get do |form| %>
|
||||
<%= form.text_field :q %>
|
||||
<%= form.select :status, %w[ any active passive pending expired cancelled ], selected: params[:status] %>
|
||||
<%= form.select :category, %w[ any student employed unemployed retired ], selected: params[:category], multiple: true %>
|
||||
<%= form.submit 'Search', name: '' %>
|
||||
<% if params[:q].present? || params[:status].present? || params[:category].present? %>
|
||||
<%= link_to t('members.index.actions.clear_search'), members_path %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<table id="members">
|
||||
<thead>
|
||||
<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>
|
||||
<th><%= link_to_current_with_sort t('members.attributes.email'), 'email.asc' %></th>
|
||||
<th><%= link_to_current_with_sort t('members.attributes.display_name'), 'display_name.asc' %></th>
|
||||
<th><%= link_to_current_with_sort t('members.attributes.joined_on'), 'joined_on.asc' %></th>
|
||||
<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>
|
||||
<td><%= member_status(member.status) %></td>
|
||||
<td><%= member.email %></td>
|
||||
<td><%= member.display_name %></td>
|
||||
<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) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
27
app/views/members/new.html.erb
Normal file
27
app/views/members/new.html.erb
Normal file
@ -0,0 +1,27 @@
|
||||
<h1><%= t('members.new.title') %></h1>
|
||||
|
||||
<%= form_with(model: @member) do |form| %>
|
||||
<% if @member.errors.any? %>
|
||||
<div style="color: red">
|
||||
<h2><%= pluralize(@member.errors.count, "error") %> prohibited this member from being saved:</h2>
|
||||
|
||||
<ul>
|
||||
<% @member.errors.each do |error| %>
|
||||
<li><%= error.full_message %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<table>
|
||||
<tr><td><label><%= t('members.attributes.display_name') %></label></td><td><%= form.text_field :display_name, required: true %></label></td></tr>
|
||||
<tr><td><label><%= t('members.attributes.email') %></label></td><td><%= form.email_field :email, required: true %></label></td></tr>
|
||||
<tr><td><label><%= t('members.attributes.category') %></label></td><td><%= form.select :category, %w{student retired unemployed employed} %></label></td></tr>
|
||||
<tr><td><label><%= t('members.attributes.identification_number') %></label></td><td><%= form.text_field :identification_number %></label></td></tr>
|
||||
<tr><td><label><%= t('members.attributes.address') %></label></td><td><%= form.text_area :address %></label></td></tr>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<%= form.submit %>
|
||||
</div>
|
||||
<% end %>
|
40
app/views/members/show.html.erb
Normal file
40
app/views/members/show.html.erb
Normal file
@ -0,0 +1,40 @@
|
||||
<p style="color: green"><%= notice %></p>
|
||||
|
||||
<h1><%= t('members.show.title') %></h1>
|
||||
|
||||
<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>
|
||||
|
||||
<div>
|
||||
<%= link_to t('members.show.actions.edit'), edit_member_path(@member) %>
|
||||
</div>
|
||||
|
||||
<h2><%= t('members.show.contribution_history') %></h2>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Payment date</th>
|
||||
<th>Payment method</th>
|
||||
<th>Payment reference</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
<% @member.contributions.each do |contribution| %>
|
||||
<tr>
|
||||
<td><%= contribution.payment_on %></td>
|
||||
<td><%= contribution.payment_method %></td>
|
||||
<td><%= contribution.payment_reference %></td>
|
||||
<td>€<%= contribution.eurocents %></td>
|
||||
<td>
|
||||
<%= link_to t('members.show.actions.edit_contribution'), edit_contribution_path(contribution) %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
22
app/views/notification_mailer/_payment.html.erb
Normal file
22
app/views/notification_mailer/_payment.html.erb
Normal file
@ -0,0 +1,22 @@
|
||||
<p>
|
||||
Aceitamos pagamento via transferência bancária, referência multibanco ou
|
||||
MBWAY:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Valor: 30.00€</li>
|
||||
<li>Transferência bancária: <strong>PT50 0035 2178 00027478430 14</strong></li>
|
||||
<li>Multibanco ou MBWAY: <a href="<%= ifthenpay %>"><%= ifthenpay %></a></li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Caso queiras usufruir da quota reduzida de 6.00€ para estudantes,
|
||||
desempregados e reformados, pedimos que nos envies um comprovativo desse
|
||||
estatuto e faças o pagamento por transferência bancária.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Se optares pelo método de transferência bancária, pedimos que envies o
|
||||
comprovativo de transferência em resposta a este email ou para o endereço
|
||||
direccao@ansol.org.
|
||||
</p>
|
14
app/views/notification_mailer/_payment.text.erb
Normal file
14
app/views/notification_mailer/_payment.text.erb
Normal file
@ -0,0 +1,14 @@
|
||||
Aceitamos pagamento via transferência bancária, referência multibanco ou
|
||||
MBWAY:
|
||||
|
||||
* Valor: 30.00€
|
||||
* Transferência bancária: <strong>PT50 0035 2178 00027478430 14</strong>
|
||||
* Multibanco ou MBWAY: <a href="<%= @link %>"><%= @link %></a>
|
||||
|
||||
Caso queiras usufruir da quota reduzida de 6.00€ para estudantes,
|
||||
desempregados e reformados, pedimos que nos envies um comprovativo desse
|
||||
estatuto e faças o pagamento por transferência bancária.
|
||||
|
||||
Se optares pelo método de transferência bancária, pedimos que envies o
|
||||
comprovativo de transferência em resposta a este email ou para o endereço
|
||||
direccao@ansol.org.
|
30
app/views/notification_mailer/cancelled.html.erb
Normal file
30
app/views/notification_mailer/cancelled.html.erb
Normal file
@ -0,0 +1,30 @@
|
||||
<p>
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Como não recebemos o pagamento anual das quotas da ANSOL, a tua inscrição foi
|
||||
cancelada.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Vamos revogar em breve os teus acessos à infraestrutura da associação
|
||||
exclusiva para membros (nextcloud, mailing list, sala de Matrix, etc).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Esperamos poder voltar a merecer o teu apoio no futuro. Podes reinscrever-te
|
||||
a qualquer altura através do formulário disponível em
|
||||
<a href="https://ansol.org/inscricao">https://ansol.org/inscricao</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Caso consideres que estás a receber esta mensagem indevidamente, contacta-nos
|
||||
através do endereço direccao@ansol.org para resolvermos a situação o mais
|
||||
rápido possível.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Saudações livres,<br>
|
||||
Direcção da ANSOL
|
||||
</p>
|
17
app/views/notification_mailer/cancelled.text.erb
Normal file
17
app/views/notification_mailer/cancelled.text.erb
Normal file
@ -0,0 +1,17 @@
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
|
||||
Como não recebemos o pagamento anual das quotas da ANSOL, a tua inscrição foi
|
||||
cancelada.
|
||||
|
||||
Vamos revogar em breve os teus acessos à infraestrutura da associação exclusiva
|
||||
para membros (nextcloud, mailing list, sala de Matrix, etc).
|
||||
|
||||
Esperamos poder voltar a merecer o teu apoio no futuro. Podes reinscrever-te a
|
||||
qualquer altura através do formulário disponível em https://ansol.org/inscricao
|
||||
|
||||
Caso consideres que estás a receber esta mensagem indevidamente, contacta-nos
|
||||
através do endereço direccao@ansol.org para resolvermos a situação o mais
|
||||
rápido possível.
|
||||
|
||||
Saudações livres,
|
||||
Direcção da ANSOL
|
24
app/views/notification_mailer/expiration_in_30d.html.erb
Normal file
24
app/views/notification_mailer/expiration_in_30d.html.erb
Normal file
@ -0,0 +1,24 @@
|
||||
<p><%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %></p>
|
||||
|
||||
<p>
|
||||
A tua inscrição como membro da ANSOL expira em 30 dias.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Em primeiro lugar, queremos agradecer o teu contributo para a ANSOL.
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades, e gostaríamos de continuar a contar com a tua
|
||||
participação.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Para estender a tua inscrição por mais um ano, pedimos que faças o pagamento
|
||||
das quotas até <strong><%= @notification.member.expires_on %></strong>.
|
||||
</p>
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
<p>
|
||||
Saudações livres,<br>
|
||||
Direcção da ANSOL
|
||||
</p>
|
16
app/views/notification_mailer/expiration_in_30d.text.erb
Normal file
16
app/views/notification_mailer/expiration_in_30d.text.erb
Normal file
@ -0,0 +1,16 @@
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
|
||||
A tua inscrição como membro da ANSOL expira em 30 dias.
|
||||
|
||||
Em primeiro lugar, queremos agradecer o teu contributo para a ANSOL.
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades, e gostaríamos de continuar a contar com a tua
|
||||
participação.
|
||||
|
||||
Para estender a tua inscrição por mais um ano, pedimos que faças o pagamento
|
||||
das quotas até <%= @notification.member.expires_on %>.
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
Saudações livres,
|
||||
Direcção da ANSOL
|
24
app/views/notification_mailer/expiration_in_60d.html.erb
Normal file
24
app/views/notification_mailer/expiration_in_60d.html.erb
Normal file
@ -0,0 +1,24 @@
|
||||
<p><%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %></p>
|
||||
|
||||
<p>
|
||||
A tua inscrição como membro da ANSOL expira em 60 dias.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Em primeiro lugar, queremos agradecer o teu contributo para a ANSOL.
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades, e gostaríamos de continuar a contar com a tua
|
||||
participação.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Para estender a tua inscrição por mais um ano, pedimos que faças o pagamento
|
||||
das quotas até <strong><%= @notification.member.expires_on %></strong>.
|
||||
</p>
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
<p>
|
||||
Saudações livres,<br>
|
||||
Direcção da ANSOL
|
||||
</p>
|
16
app/views/notification_mailer/expiration_in_60d.text.erb
Normal file
16
app/views/notification_mailer/expiration_in_60d.text.erb
Normal file
@ -0,0 +1,16 @@
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
|
||||
A tua inscrição como membro da ANSOL expira em 60 dias.
|
||||
|
||||
Em primeiro lugar, queremos agradecer o teu contributo para a ANSOL.
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades, e gostaríamos de continuar a contar com a tua
|
||||
participação.
|
||||
|
||||
Para estender a tua inscrição por mais um ano, pedimos que faças o pagamento
|
||||
das quotas até <%= @notification.member.expires_on %>.
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
Saudações livres,
|
||||
Direcção da ANSOL
|
26
app/views/notification_mailer/expired.html.erb
Normal file
26
app/views/notification_mailer/expired.html.erb
Normal file
@ -0,0 +1,26 @@
|
||||
<p>
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A tua inscrição como membro da ANSOL vai ser cancelada dentro de 30 dias por
|
||||
falta de pagamento da contribuição anual.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
|
||||
</p>
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
<p>
|
||||
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
|
||||
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
|
||||
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Saudações livres,<br>
|
||||
Direcção da ANSOL
|
||||
</p>
|
16
app/views/notification_mailer/expired.text.erb
Normal file
16
app/views/notification_mailer/expired.text.erb
Normal file
@ -0,0 +1,16 @@
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
|
||||
A tua inscrição como membro da ANSOL expirou hoje e não recebemos a tua
|
||||
contribuição anual.
|
||||
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
|
||||
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
|
||||
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
|
||||
|
||||
Saudações livres,
|
||||
Direcção da ANSOL
|
26
app/views/notification_mailer/expired_30d_ago.html.erb
Normal file
26
app/views/notification_mailer/expired_30d_ago.html.erb
Normal file
@ -0,0 +1,26 @@
|
||||
<p>
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A tua inscrição como membro da ANSOL expirou há um mês e ainda não recebemos
|
||||
a tua contribuição anual.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
|
||||
</p>
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
<p>
|
||||
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
|
||||
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
|
||||
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Saudações livres,
|
||||
Direcção da ANSOL
|
||||
</p>
|
16
app/views/notification_mailer/expired_30d_ago.text.erb
Normal file
16
app/views/notification_mailer/expired_30d_ago.text.erb
Normal file
@ -0,0 +1,16 @@
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
|
||||
A tua inscrição como membro da ANSOL expirou há um mês e ainda não recebemos
|
||||
a tua contribuição anual.
|
||||
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
|
||||
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
|
||||
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
|
||||
|
||||
Saudações livres,
|
||||
Direcção da ANSOL
|
26
app/views/notification_mailer/expired_60d_ago.html.erb
Normal file
26
app/views/notification_mailer/expired_60d_ago.html.erb
Normal file
@ -0,0 +1,26 @@
|
||||
<p>
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A tua inscrição como membro da ANSOL vai ser cancelada dentro de 30 dias por
|
||||
falta de pagamento da contribuição anual.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
|
||||
</p>
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
<p>
|
||||
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
|
||||
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
|
||||
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Saudações livres,<br>
|
||||
Direcção da ANSOL
|
||||
</p>
|
16
app/views/notification_mailer/expired_60d_ago.text.erb
Normal file
16
app/views/notification_mailer/expired_60d_ago.text.erb
Normal file
@ -0,0 +1,16 @@
|
||||
<%= t('notification_mailer.greetings', display_name: @notification.member.display_name) %>
|
||||
|
||||
A tua inscrição como membro da ANSOL expirou há 60 dias e ainda não recebemos
|
||||
a tua contribuição anual.
|
||||
|
||||
Dependemos exclusivamente da contribuição dos nossos membros para suportar as
|
||||
nossas actividades. Gostaríamos de continuar a contar com a tua participação.
|
||||
|
||||
<%= render partial: "payment", locals: { ifthenpay: @link } %>
|
||||
|
||||
Caso não recebamos o pagamento até dia <%= @notification.member.cancelled_on
|
||||
%>, cancelaremos permanentemente a tua inscrição. Estamos disponíveis para
|
||||
esclarecer qualquer dúvida através do endereço direccao@ansol.org.
|
||||
|
||||
Saudações livres,
|
||||
Direcção da ANSOL
|
@ -33,5 +33,6 @@ module Saucy
|
||||
|
||||
# Don't generate system test files.
|
||||
config.generators.system_tests = nil
|
||||
config.i18n.default_locale = :pt
|
||||
end
|
||||
end
|
||||
|
@ -41,6 +41,18 @@ Rails.application.configure do
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
|
||||
config.action_mailer.delivery_method :smtp
|
||||
|
||||
config.action_mailer.smtp_settings = {
|
||||
address: ENV['SMTP_ADDRESS'],
|
||||
port: 587,
|
||||
domain: ENV['SMTP_DOMAIN'],
|
||||
user_name: ENV['SMTP_USERNAME'],
|
||||
password: ENV['SMTP_PASSWORD'],
|
||||
authentication: 'plain',
|
||||
enable_starttls_auto: true,
|
||||
}
|
||||
|
||||
# Print deprecation notices to the Rails logger.
|
||||
config.active_support.deprecation = :log
|
||||
|
||||
|
0
config/initializers/rules.rb
Normal file
0
config/initializers/rules.rb
Normal file
@ -1,33 +1,60 @@
|
||||
# Files in the config/locales directory are used for internationalization
|
||||
# and are automatically loaded by Rails. If you want to use locales other
|
||||
# than English, add the necessary files in this directory.
|
||||
#
|
||||
# To use the locales, use `I18n.t`:
|
||||
#
|
||||
# I18n.t "hello"
|
||||
#
|
||||
# In views, this is aliased to just `t`:
|
||||
#
|
||||
# <%= t("hello") %>
|
||||
#
|
||||
# To use a different locale, set it with `I18n.locale`:
|
||||
#
|
||||
# I18n.locale = :es
|
||||
#
|
||||
# This would use the information in config/locales/es.yml.
|
||||
#
|
||||
# The following keys must be escaped otherwise they will not be retrieved by
|
||||
# the default I18n backend:
|
||||
#
|
||||
# true, false, on, off, yes, no
|
||||
#
|
||||
# Instead, surround them with single quotes.
|
||||
#
|
||||
# en:
|
||||
# "true": "foo"
|
||||
#
|
||||
# To learn more, please read the Rails Internationalization guide
|
||||
# available at https://guides.rubyonrails.org/i18n.html.
|
||||
|
||||
en:
|
||||
hello: "Hello world"
|
||||
navigation:
|
||||
members: "Member list"
|
||||
members:
|
||||
index:
|
||||
title: "Members"
|
||||
actions:
|
||||
new: "New member"
|
||||
clear_search: "Clear search"
|
||||
show: "Show"
|
||||
edit: "Edit"
|
||||
new_contribution: "Register contribution"
|
||||
show:
|
||||
title: "Member details"
|
||||
actions:
|
||||
edit: "Edit"
|
||||
edit:
|
||||
title: "Edit member details"
|
||||
actions:
|
||||
back_to_show: "Show this member"
|
||||
edit_dates_warning: "Warning: changing the join/expiration date may trigger the delivery of email notifications regarding pending payments."
|
||||
new:
|
||||
title: "Register new member"
|
||||
attributes:
|
||||
number: "#"
|
||||
status: "Status"
|
||||
email: "Email address"
|
||||
display_name: "Display name"
|
||||
joined_on: "Joined on"
|
||||
expires_on: "Expires on"
|
||||
category: "Category"
|
||||
identification_number: "ID number"
|
||||
address: "Postal address"
|
||||
status:
|
||||
any: "Any"
|
||||
active: "Active"
|
||||
passive: "Passive"
|
||||
pending: "Pending"
|
||||
expired: "Expired"
|
||||
cancelled: "Cancelled"
|
||||
category:
|
||||
any: "Any"
|
||||
employed: "Employed"
|
||||
unemployed: "Unemployed"
|
||||
student: "Student"
|
||||
retired: "Retired"
|
||||
notification_mailer:
|
||||
expiration_in_60d:
|
||||
subject: "ANSOL - Pagamento anual de quotas"
|
||||
title: "Pagamento anual de quotas"
|
||||
expiration_in_30d:
|
||||
subject: "ANSOL - Prazo para pagamento de quotas vence em 30 dias"
|
||||
expired:
|
||||
subject: "ANSOL - Pagamento de quotas pendente"
|
||||
expired_30d_ago:
|
||||
subject: "ANSOL - Pagamento de quotas em atraso"
|
||||
expired_60d_ago:
|
||||
subject: "ANSOL - Suspensão de inscrição iminente"
|
||||
cancelled:
|
||||
subject: "ANSOL - Inscrição cancelada"
|
||||
|
62
config/locales/pt.yml
Normal file
62
config/locales/pt.yml
Normal file
@ -0,0 +1,62 @@
|
||||
pt:
|
||||
navigation:
|
||||
members: "Lista de membros"
|
||||
members:
|
||||
index:
|
||||
title: "Membros"
|
||||
actions:
|
||||
new: "Registar novo membro"
|
||||
clear_search: ""
|
||||
show: "Mostrar"
|
||||
edit: "Editar"
|
||||
new_contribution: "Registar contribuição"
|
||||
title: "Acções"
|
||||
show:
|
||||
title: "Detalhes de membro"
|
||||
actions:
|
||||
edit: "Editar detalhes"
|
||||
edit_contribution: "Editar"
|
||||
edit:
|
||||
title: "Editar detalhes de membro"
|
||||
actions:
|
||||
back_to_show: "Show this member"
|
||||
edit_dates_warning: "Atenção: a alteração das datas de inscrição/expiração podem causar o envio de emails com notificações de atraso de pagamento."
|
||||
new:
|
||||
title: "Registar novo membro"
|
||||
attributes:
|
||||
number: "#"
|
||||
status: "Estado"
|
||||
email: "Endereço de correio electrónico"
|
||||
display_name: "Nome"
|
||||
joined_on: "Data de inscrição"
|
||||
expires_on: "Data de expiração"
|
||||
category: "Categoria"
|
||||
identification_number: "N.º de identificação"
|
||||
address: "Endereço postal"
|
||||
status:
|
||||
any: "Qualquer"
|
||||
active: "Activo"
|
||||
passive: "Passivo"
|
||||
pending: "Pendente"
|
||||
expired: "Expirado"
|
||||
cancelled: "Cancelado"
|
||||
category:
|
||||
any: "Qualquer"
|
||||
employed: "Empregado"
|
||||
unemployed: "Desempregado"
|
||||
student: "Estudante"
|
||||
retired: "Reformado"
|
||||
notification_mailer:
|
||||
expiration_in_60d:
|
||||
subject: "ANSOL - Pagamento anual de quotas"
|
||||
expiration_in_30d:
|
||||
subject: "ANSOL - Inscrição expira em 30 dias"
|
||||
expired:
|
||||
subject: "ANSOL - Pagamento de quotas pendente"
|
||||
expired_30d_ago:
|
||||
subject: "ANSOL - Pagamento de quotas em atraso"
|
||||
expired_60d_ago:
|
||||
subject: "ANSOL - Suspensão de inscrição iminente"
|
||||
cancelled:
|
||||
subject: "ANSOL - Inscrição cancelada"
|
||||
greetings: "Caro(a) %{display_name}"
|
@ -3,4 +3,10 @@ Rails.application.routes.draw do
|
||||
|
||||
# Defines the root path route ("/")
|
||||
# root "articles#index"
|
||||
|
||||
resources :members do
|
||||
resources :contributions, only: [:new, :create]
|
||||
end
|
||||
|
||||
resources :contributions, only: [:edit, :update]
|
||||
end
|
||||
|
15
db/migrate/20220620195513_create_member.rb
Normal file
15
db/migrate/20220620195513_create_member.rb
Normal file
@ -0,0 +1,15 @@
|
||||
class CreateMember < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :members, id: :uuid do |t|
|
||||
t.serial :number, null: false, index: { unique: true }
|
||||
t.string :email, null: false, index: { unique: true }
|
||||
t.string :display_name, null: false
|
||||
t.string :identification_number
|
||||
t.string :status
|
||||
t.string :category
|
||||
t.text :address
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
8
db/migrate/20220620233944_add_dates_to_members.rb
Normal file
8
db/migrate/20220620233944_add_dates_to_members.rb
Normal file
@ -0,0 +1,8 @@
|
||||
class AddDatesToMembers < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
change_table :members do |t|
|
||||
t.date :joined_on
|
||||
t.date :expires_on
|
||||
end
|
||||
end
|
||||
end
|
14
db/migrate/20220621101236_create_contributions.rb
Normal file
14
db/migrate/20220621101236_create_contributions.rb
Normal file
@ -0,0 +1,14 @@
|
||||
class CreateContributions < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :contributions, id: :uuid do |t|
|
||||
t.references :member, type: :uuid, foreign_key: true, null: false
|
||||
|
||||
t.integer :eurocents, null: false
|
||||
t.date :payment_on, null: false
|
||||
t.string :payment_method
|
||||
t.string :payment_reference
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
14
db/migrate/20220623135702_create_notifications.rb
Normal file
14
db/migrate/20220623135702_create_notifications.rb
Normal file
@ -0,0 +1,14 @@
|
||||
class CreateNotifications < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :notifications, id: :uuid do |t|
|
||||
t.references :member, type: :uuid, foreign_key: true
|
||||
t.date :to_be_sent_on, null: false
|
||||
t.string :template, null: false
|
||||
|
||||
t.string :status, null: false
|
||||
t.timestamp :sent_at
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,8 @@
|
||||
class AddIfthenpayLinksToMember < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
change_table :members do |t|
|
||||
t.string :regular_ifthenpay_link
|
||||
t.string :reduced_ifthenpay_link
|
||||
end
|
||||
end
|
||||
end
|
44
db/schema.rb
generated
44
db/schema.rb
generated
@ -10,9 +10,51 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2022_06_20_195143) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2022_06_24_134509) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pgcrypto"
|
||||
enable_extension "plpgsql"
|
||||
|
||||
create_table "contributions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||
t.uuid "member_id", null: false
|
||||
t.integer "eurocents", null: false
|
||||
t.date "payment_on", null: false
|
||||
t.string "payment_method"
|
||||
t.string "payment_reference"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["member_id"], name: "index_contributions_on_member_id"
|
||||
end
|
||||
|
||||
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 "identification_number"
|
||||
t.string "status"
|
||||
t.string "category"
|
||||
t.text "address"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.date "joined_on"
|
||||
t.date "expires_on"
|
||||
t.string "regular_ifthenpay_link"
|
||||
t.string "reduced_ifthenpay_link"
|
||||
t.index ["email"], name: "index_members_on_email", unique: true
|
||||
t.index ["number"], name: "index_members_on_number", unique: true
|
||||
end
|
||||
|
||||
create_table "notifications", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
|
||||
t.uuid "member_id"
|
||||
t.date "to_be_sent_on", null: false
|
||||
t.string "template", null: false
|
||||
t.string "status", null: false
|
||||
t.datetime "sent_at", precision: nil
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["member_id"], name: "index_notifications_on_member_id"
|
||||
end
|
||||
|
||||
add_foreign_key "contributions", "members"
|
||||
add_foreign_key "notifications", "members"
|
||||
end
|
||||
|
14
lib/tasks/saucy.rake
Normal file
14
lib/tasks/saucy.rake
Normal file
@ -0,0 +1,14 @@
|
||||
desc "Application specific tasks"
|
||||
namespace :saucy do
|
||||
desc "Background sync operations"
|
||||
task sync: :environment do
|
||||
Member.generate_all_missing_ifthenpay_links!
|
||||
Member.reset_all_status!
|
||||
Member.regenerate_all_notifications
|
||||
end
|
||||
|
||||
desc "Send daily email notifications"
|
||||
task notify: :environment do
|
||||
Notification.send_scheduled_for_today
|
||||
end
|
||||
end
|
48
test/controllers/members_controller_test.rb
Normal file
48
test/controllers/members_controller_test.rb
Normal file
@ -0,0 +1,48 @@
|
||||
require "test_helper"
|
||||
|
||||
class MembersControllerTest < ActionDispatch::IntegrationTest
|
||||
#setup do
|
||||
# @member = members(:one)
|
||||
#end
|
||||
|
||||
#test "should get index" do
|
||||
# get members_url
|
||||
# assert_response :success
|
||||
#end
|
||||
|
||||
#test "should get new" do
|
||||
# get new_member_url
|
||||
# assert_response :success
|
||||
#end
|
||||
|
||||
#test "should create member" do
|
||||
# assert_difference("Member.count") do
|
||||
# post members_url, params: { member: { } }
|
||||
# end
|
||||
|
||||
# assert_redirected_to member_url(Member.last)
|
||||
#end
|
||||
|
||||
#test "should show member" do
|
||||
# get member_url(@member)
|
||||
# assert_response :success
|
||||
#end
|
||||
|
||||
#test "should get edit" do
|
||||
# get edit_member_url(@member)
|
||||
# assert_response :success
|
||||
#end
|
||||
|
||||
#test "should update member" do
|
||||
# patch member_url(@member), params: { member: { } }
|
||||
# assert_redirected_to member_url(@member)
|
||||
#end
|
||||
|
||||
#test "should destroy member" do
|
||||
# assert_difference("Member.count", -1) do
|
||||
# delete member_url(@member)
|
||||
# end
|
||||
|
||||
# assert_redirected_to members_url
|
||||
#end
|
||||
end
|
11
test/fixtures/notifications.yml
vendored
Normal file
11
test/fixtures/notifications.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
# This model initially had no columns defined. If you add columns to the
|
||||
# model remove the "{}" from the fixture names and add the columns immediately
|
||||
# below each fixture, per the syntax in the comments below
|
||||
#
|
||||
one: {}
|
||||
# column: value
|
||||
#
|
||||
two: {}
|
||||
# column: value
|
52
test/mailers/notification_mailer_test.rb
Normal file
52
test/mailers/notification_mailer_test.rb
Normal file
@ -0,0 +1,52 @@
|
||||
require "test_helper"
|
||||
|
||||
class NotificationMailerTest < ActionMailer::TestCase
|
||||
test "expiration_in_60d" do
|
||||
mail = NotificationMailer.expiration_in_60d
|
||||
assert_equal "Expiration in 60d", mail.subject
|
||||
assert_equal ["to@example.org"], mail.to
|
||||
assert_equal ["from@example.com"], mail.from
|
||||
assert_match "Hi", mail.body.encoded
|
||||
end
|
||||
|
||||
test "expiration_in_30d" do
|
||||
mail = NotificationMailer.expiration_in_30d
|
||||
assert_equal "Expiration in 30d", mail.subject
|
||||
assert_equal ["to@example.org"], mail.to
|
||||
assert_equal ["from@example.com"], mail.from
|
||||
assert_match "Hi", mail.body.encoded
|
||||
end
|
||||
|
||||
test "expired" do
|
||||
mail = NotificationMailer.expired
|
||||
assert_equal "Expired", mail.subject
|
||||
assert_equal ["to@example.org"], mail.to
|
||||
assert_equal ["from@example.com"], mail.from
|
||||
assert_match "Hi", mail.body.encoded
|
||||
end
|
||||
|
||||
test "expired_30d_ago" do
|
||||
mail = NotificationMailer.expired_30d_ago
|
||||
assert_equal "Expired 30d ago", mail.subject
|
||||
assert_equal ["to@example.org"], mail.to
|
||||
assert_equal ["from@example.com"], mail.from
|
||||
assert_match "Hi", mail.body.encoded
|
||||
end
|
||||
|
||||
test "expired_60d_ago" do
|
||||
mail = NotificationMailer.expired_60d_ago
|
||||
assert_equal "Expired 60d ago", mail.subject
|
||||
assert_equal ["to@example.org"], mail.to
|
||||
assert_equal ["from@example.com"], mail.from
|
||||
assert_match "Hi", mail.body.encoded
|
||||
end
|
||||
|
||||
test "cancelled" do
|
||||
mail = NotificationMailer.cancelled
|
||||
assert_equal "Cancelled", mail.subject
|
||||
assert_equal ["to@example.org"], mail.to
|
||||
assert_equal ["from@example.com"], mail.from
|
||||
assert_match "Hi", mail.body.encoded
|
||||
end
|
||||
|
||||
end
|
34
test/mailers/previews/notification_mailer_preview.rb
Normal file
34
test/mailers/previews/notification_mailer_preview.rb
Normal file
@ -0,0 +1,34 @@
|
||||
# Preview all emails at http://localhost:3000/rails/mailers/notification_mailer
|
||||
class NotificationMailerPreview < ActionMailer::Preview
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/expiration_in_60d
|
||||
def expiration_in_60d
|
||||
NotificationMailer.expiration_in_60d
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/expiration_in_30d
|
||||
def expiration_in_30d
|
||||
NotificationMailer.expiration_in_30d
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/expired
|
||||
def expired
|
||||
NotificationMailer.expired
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/expired_30d_ago
|
||||
def expired_30d_ago
|
||||
NotificationMailer.expired_30d_ago
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/expired_60d_ago
|
||||
def expired_60d_ago
|
||||
NotificationMailer.expired_60d_ago
|
||||
end
|
||||
|
||||
# Preview this email at http://localhost:3000/rails/mailers/notification_mailer/cancelled
|
||||
def cancelled
|
||||
NotificationMailer.cancelled
|
||||
end
|
||||
|
||||
end
|
26
test/models/member_test.rb
Normal file
26
test/models/member_test.rb
Normal file
@ -0,0 +1,26 @@
|
||||
require "test_helper"
|
||||
|
||||
class MemberTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@member = Member.create!(
|
||||
email: 'dsfargeg@example.com',
|
||||
display_name: 'dsfargeg',
|
||||
joined_on: Date.today,
|
||||
expires_on: Date.today + 1.year
|
||||
)
|
||||
end
|
||||
|
||||
test "no expired in the first year and 90 days" do
|
||||
(1.year + 90.days).in_days.to_i.times do |n|
|
||||
Timecop.freeze(Date.today + n.days) do
|
||||
assert_not_equal @member.expected_status, :cancelled
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "expired after 1 year and 90 days" do
|
||||
Timecop.freeze(Date.today + 1.year + 90.days) do
|
||||
assert_equal @member.expected_status, :cancelled
|
||||
end
|
||||
end
|
||||
end
|
7
test/models/notification_test.rb
Normal file
7
test/models/notification_test.rb
Normal file
@ -0,0 +1,7 @@
|
||||
require "test_helper"
|
||||
|
||||
class NotificationTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
Loading…
Reference in New Issue
Block a user