From 6404b3a70f323363e499a520a278952544f97bb1 Mon Sep 17 00:00:00 2001 From: Jess Robinson Date: Thu, 1 Jan 2026 10:33:00 +0000 Subject: [PATCH 1/5] Actually send fees email (now), msg display on verify --- lib/AccessSystem/API/Controller/Root.pm | 8 ++++++++ lib/AccessSystem/Schema/ResultSet/Person.pm | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/AccessSystem/API/Controller/Root.pm b/lib/AccessSystem/API/Controller/Root.pm index 3814df8..f106cdd 100644 --- a/lib/AccessSystem/API/Controller/Root.pm +++ b/lib/AccessSystem/API/Controller/Root.pm @@ -286,11 +286,19 @@ sub verify: Chained('base') :PathPart('verify') :Args(0) { my $result = $c->model('AccessDB::Person')->allowed_to_thing ($c->req->params->{token}, $c->req->params->{thing}); if($result && !$result->{error}) { + if($result->{beep}) { + # Send email now (else next automated one) + my $comm = $result->{person}->communications_rs->find({type => 'membership_fees_change'}); + if ($comm) { + $self->emailer->send($comm); + } + } $c->stash( json => { person => { name => $result->{person}->name }, inductor => $result->{person}->allowed->first->is_admin, access => 1, + message => $result->{message} || '', beep => $result->{beep} || 0, cache => $result->{person}->tier->restrictions->{'times'} ? 0 : 1, colour => $result->{person}->door_colour_to_code || 0x01, diff --git a/lib/AccessSystem/Schema/ResultSet/Person.pm b/lib/AccessSystem/Schema/ResultSet/Person.pm index c679545..3cc5aad 100644 --- a/lib/AccessSystem/Schema/ResultSet/Person.pm +++ b/lib/AccessSystem/Schema/ResultSet/Person.pm @@ -164,7 +164,7 @@ sub allowed_to_thing { return { person => $person, beep => $beep, - message => 'Fees have changed. Check email.', + message => 'Fees have changed. Check email.', thing => $person->allowed->first->tool, }; } From 25f171e2df7761324e0a05aea47282b2c8b1c7ee Mon Sep 17 00:00:00 2001 From: Jess Robinson Date: Fri, 2 Jan 2026 11:16:30 +0000 Subject: [PATCH 2/5] Donors should not get the "rejoin" emails Donor conversions only emailed about it if last valid in last 2 months Add missing tier value update/donor tier creation SQL --- lib/AccessSystem/Schema/Result/Person.pm | 20 +++++++++++++------- sql/MembershipFees-2025-12.sql | 5 +++++ 2 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 sql/MembershipFees-2025-12.sql diff --git a/lib/AccessSystem/Schema/Result/Person.pm b/lib/AccessSystem/Schema/Result/Person.pm index 80a0783..ebcd5b2 100644 --- a/lib/AccessSystem/Schema/Result/Person.pm +++ b/lib/AccessSystem/Schema/Result/Person.pm @@ -585,7 +585,7 @@ sub create_payment { if($current_bal < $self->dues) { # Expanded for fees update 2025: # If previously valid, and payment (intention) is less than dues (which have changed!) - # and actually expired (mid overlap), convert to donor-tier, fees to 0, store old tier/fees + # and actually expired (during overlap), convert to donor-tier, fees to 0, store old tier/fees # this should happens before the reminder_email (which is not sent to donors) if($valid_date && $valid_date->clone()->subtract(days => int($OVERLAP_DAYS / 2)) < $now @@ -596,8 +596,9 @@ sub create_payment { my $old_fees = $self->payment_override; my $min_dues = $self->dues; my $token = 'old_tier'; # findable! + # find_or_create in case it crashes during payment run(!) $schema->txn_do(sub { - my $confirm = $self->confirmations->create({ + my $confirm = $self->confirmations->find_or_create({ token => $token, storage => { tier_id => $old_tier_id, @@ -610,7 +611,11 @@ sub create_payment { payment_override => 0, }); # Send an email (force renew): - $self->create_communication('Your Swindon Makerspace membership is now Donor Only', 'move_to_donation_tier', { current_balance => $current_bal, min_dues => $min_dues}, 1); + # But only if this is within a few months? if you have + # balance for years then.. sorry its a donation! + if ($valid_date > $now->clone->subtract(months => 2)) { + $self->create_communication('Your Swindon Makerspace membership is now Donor Only', 'move_to_donation_tier', { current_balance => $current_bal, min_dues => $min_dues}, 1); + } }); warn "Member " . $self->bank_ref . " not covered fees, converted to Donor Tier"; return; @@ -645,6 +650,10 @@ sub create_payment { } return; } + + # Donor tier members should not make "payments" + return if $self->is_donor; + if (!$valid_date) { $self->create_communication('Your Swindon Makerspace membership has started', 'first_payment'); } @@ -657,10 +666,7 @@ sub create_payment { my $r_email = $self->communications_rs->find({type => 'reminder_email'}); $r_email->delete if $r_email; } - - # Donor tier members should not make "payments" - return if $self->is_donor; - + # Only add $OVERLAP extra days if a first or renewal payment - these # ensure member remains valid if standing order is not an # exact month due to weekends and bank holidays diff --git a/sql/MembershipFees-2025-12.sql b/sql/MembershipFees-2025-12.sql new file mode 100644 index 0000000..20566d3 --- /dev/null +++ b/sql/MembershipFees-2025-12.sql @@ -0,0 +1,5 @@ +INSERT INTO tiers (name, description, price, in_use, concessions_allowed) values ('Donation Only', 'Making voluntary contributions to help support the Makerspace', 0, true, false); +UPDATE tiers SET price=600 where id=1; +UPDATE tiers SET price=1800 where id=2; +UPDATE tiers SET price=3000 where id=3; +UPDATE tiers SET price=4200 where id=4; From 95cf12439f72acaa7252228f6c0de2ac57d73213 Mon Sep 17 00:00:00 2001 From: Jess Robinson Date: Fri, 2 Jan 2026 11:32:02 +0000 Subject: [PATCH 3/5] Confirm tokens need to be unique! (change to person id + old_tier) --- lib/AccessSystem/Schema/Result/Person.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/AccessSystem/Schema/Result/Person.pm b/lib/AccessSystem/Schema/Result/Person.pm index ebcd5b2..2fff98b 100644 --- a/lib/AccessSystem/Schema/Result/Person.pm +++ b/lib/AccessSystem/Schema/Result/Person.pm @@ -563,7 +563,7 @@ sub create_payment { # If Donor tier and balance is now enough and only donor-ified after 1 month ago - revert # (one month else they'll keep flipping in/out of donor) if($self->is_donor) { - my $old_data = $self->confirmations->find({ token => 'old_tier'}); + my $old_data = $self->confirmations->find({ token => $self->id . '_old_tier'}); my $when = $dt_parser->parse_datetime($old_data->storage->{changed_on}); my $old_tier = $old_data->storage->{tier_id}; my $one_month_ago = $now->clone->subtract(months => 1); @@ -595,7 +595,7 @@ sub create_payment { my $old_tier_id = $self->tier_id; my $old_fees = $self->payment_override; my $min_dues = $self->dues; - my $token = 'old_tier'; # findable! + my $token = $self->id . '_old_tier'; # findable! # find_or_create in case it crashes during payment run(!) $schema->txn_do(sub { my $confirm = $self->confirmations->find_or_create({ From d663b48c04a5826cad5f3cffc38a82511a3eacb9 Mon Sep 17 00:00:00 2001 From: Jess Robinson Date: Thu, 8 Jan 2026 21:56:12 +0000 Subject: [PATCH 4/5] Only send donor-change email if member was active in last 2 months Catch error where old tier data is missing Fix typo in email --- lib/AccessSystem/Schema/Result/Person.pm | 10 +++++++++- .../move_to_donation_tier/move_to_donation_tier.html | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/AccessSystem/Schema/Result/Person.pm b/lib/AccessSystem/Schema/Result/Person.pm index 2fff98b..295a551 100644 --- a/lib/AccessSystem/Schema/Result/Person.pm +++ b/lib/AccessSystem/Schema/Result/Person.pm @@ -564,6 +564,10 @@ sub create_payment { # (one month else they'll keep flipping in/out of donor) if($self->is_donor) { my $old_data = $self->confirmations->find({ token => $self->id . '_old_tier'}); + if (!$old_data) { + warn "Can't find pre-donor tier data\n!"; + return; + } my $when = $dt_parser->parse_datetime($old_data->storage->{changed_on}); my $old_tier = $old_data->storage->{tier_id}; my $one_month_ago = $now->clone->subtract(months => 1); @@ -613,7 +617,11 @@ sub create_payment { # Send an email (force renew): # But only if this is within a few months? if you have # balance for years then.. sorry its a donation! - if ($valid_date > $now->clone->subtract(months => 2)) { + my $last_usage = $self->usage_by_date->search({}, { rows => 1 })->first; + if ($valid_date > $now->clone->subtract(months => 2) + && $last_usage + && $last_usage->accessed_date > $now->clone->subtract(months => 2) + ) { $self->create_communication('Your Swindon Makerspace membership is now Donor Only', 'move_to_donation_tier', { current_balance => $current_bal, min_dues => $min_dues}, 1); } }); diff --git a/root/src/emails/move_to_donation_tier/move_to_donation_tier.html b/root/src/emails/move_to_donation_tier/move_to_donation_tier.html index b64bcdf..56de6cd 100644 --- a/root/src/emails/move_to_donation_tier/move_to_donation_tier.html +++ b/root/src/emails/move_to_donation_tier/move_to_donation_tier.html @@ -1,7 +1,7 @@ [% WRAPPER layout.html.tt %]

Dear [% member.name %],

-

Your membership has been moved to the donation tier.\n +

Your membership has been moved to the donation tier.
This is because our current membership fees have changed and you have not updated your payments to reflect this.
This donation tier does not have access to the space

From e67d34aa03e8f42b8700b48a3d13a8853d81af30 Mon Sep 17 00:00:00 2001 From: Jess Robinson Date: Sun, 1 Mar 2026 10:29:44 +0000 Subject: [PATCH 5/5] Display recent payment and sum of recent payments per tier (crud) --- lib/AccessSystem.pm | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/AccessSystem.pm b/lib/AccessSystem.pm index 3c0313e..7cdd19c 100644 --- a/lib/AccessSystem.pm +++ b/lib/AccessSystem.pm @@ -62,6 +62,9 @@ __PACKAGE__->config( 'Tool' => { display_column => 'name', }, + 'Tier' => { + display_column => 'name', + }, }, virtual_columns => { 'Person' => { @@ -75,6 +78,11 @@ __PACKAGE__->config( is_nullable => 0, sql => 'SELECT CASE WHEN max_valid >= CURRENT_TIMESTAMP THEN 1 ELSE 0 END FROM (SELECT max(dues.expires_on_date) as max_valid FROM dues WHERE dues.person_id=self.id) as valid', }, + 'last_payment' => { + data_type => 'datetime', + is_nullable => 1, + sql => 'SELECT max(added_on) FROM transactions WHERE transactions.person_id = self.id AND transactions.amount_p > 0', + }, }, 'MessageLog' => { @@ -82,7 +90,14 @@ __PACKAGE__->config( data_type => 'varchar', sql => "SELECT substr(message, 24) as token FROM message_log WHERE message_log.tool_id = self.tool_id and message_log.written_date = self.written_date AND message like 'Permission granted to: %'", } - } + }, + 'Tier' => { + 'amount_last_30days' => { + data_type => 'decimal', + is_nullable => 1, + sql => 'SELECT sum(amount_p)/100 FROM transactions WHERE transactions.person_id IN (SELECT id from people where people.tier_id = self.id) AND transactions.amount_p > 0 AND transactions.added_on > CURRENT_TIMESTAMP - interval \'30 days\'', + }, + } } # ... }