From 6f7bb4652ad58775275eafa487cfd6e50fe3815d Mon Sep 17 00:00:00 2001
From: enric4000 Hey {{ app.user.get_full_name }}, HackUPC has officially come to an end, and we hope you enjoyed the event as much as we did! The next step to get your travel reimbursement is to upload the link to your project on Devpost. This will allow us
+ to validate your submission and verify that the project is valid and that it complies with the requirements that
+ were previously communicated. Upload your link here: https://my.hackupc.com/reimbursement/dash_board/.
+ Thank you for your collaboration!! Hey {{ app.user.get_full_name }}, The receipt you submitted is invalid. Comment on receipt submitted: {{ reimb.public_comment }} Rejected reason: {{ reimb.public_comment }}
diff --git a/reimbursement/templates/mails/ticket_accepted_message.html b/reimbursement/templates/mails/ticket_accepted_message.html
new file mode 100644
index 000000000..12d2f87d7
--- /dev/null
+++ b/reimbursement/templates/mails/ticket_accepted_message.html
@@ -0,0 +1,31 @@
+{% extends 'base_email.html' %}
+{% block preheader %}Your ticket has been accepted{% endblock %}
+
+{% block content %}
+ Hey {{ app.user.get_full_name }}, If you are receiving this mail is because your travel ticket has been accepted. However, the process requires you to follow the below instructions during the event to consider you eligible for a
+ reimbursement. Regarding the project: Regarding the attendance: The money will be transferred to the participant using PayPal in the following 60 days after the event. The paypal
+ account MUST exist. Hey {{ app.user.get_full_name }}, For us to accept your travel reimbursement request, you need to upload VALID travel tickets in https://my.hackupc.com/reimbursement/dash_board/.
+ A ticket is considered valid if and only if:
❗ Before filling out the application, read this article about
- How To Do a Good Application..
+ How To Do a Good Application.
Hey {{ app.user.get_full_name }}, HackUPC has officially come to an end, and we hope you enjoyed the event as much as we did! The next step to get your travel reimbursement is to upload the link to your project on Devpost. This will allow us
+ The next step to get your travel reimbursement is to upload the link to your project on Devpost. This will allow us
to validate your submission and verify that the project is valid and that it complies with the requirements that
were previously communicated. Upload your link here: https://my.hackupc.com/reimbursement/dash_board/.
- Upload your link here: Regarding the project: Regarding the attendance: The money will be transferred to the participant using PayPal in the following 60 days after the event. The paypal
- account MUST exist.Personal
{% include 'include/field.html' with desc='Name' value=app.name %}
{% elif app.user.is_volunteer %}
{% include 'include/field.html' with desc='Name' value=app.user.name %}
- {% include 'include/field.html' with desc='Is over 18?' value=app.under_age|yesno %}
+ {% include 'include/field.html' with desc='Is underage?' value=app.under_age|yesno %}
{% include 'include/field.html' with desc='Studies and course/graduation' value=app.studies_and_course %}
{% else %}
{% include 'include/field.html' with desc='Name' value=app.user.name %}
@@ -35,7 +35,7 @@ Personal
{% include 'include/field.html' with desc='Gender' value=app.get_gender_display %}
{% include 'include/field.html' with desc='Other gender' value=app.other_gender %}
{% include 'include/field.html' with desc='In BCN Apr-May' value=app.lennyface|yesno %}
-
+
Validation
Validation
-
+
Volunteering
{{ comment.text }}
{% csrf_token %}
+ rows="3" required maxlength="500">
@@ -198,28 +200,29 @@ {{ comment.text }}
{% block out_panel %}
{% if app and not app.user.is_sponsor %}
-
-
+ {% if app and app.user.is_volunteer and not user.has_volunteer_access %}
+ {% else %}
+
+ {% endif %}
{% endif %}
{% endblock %}
diff --git a/organizers/views.py b/organizers/views.py
index 01c6a18d0..723c2377b 100644
--- a/organizers/views.py
+++ b/organizers/views.py
@@ -85,7 +85,9 @@ def add_vote(application, user, tech_rat, pers_rat):
v.save()
votes_count = application.vote_set.count()
if votes_count >= 5 and not application.cv_flagged:
- AcceptedResume.objects.update_or_create(application=application, defaults={'accepted': True})
+ AcceptedResume.objects.update_or_create(
+ application=application, defaults={"accepted": True}
+ )
return v
@@ -359,12 +361,12 @@ def post(self, request, *args, **kwargs):
id_ = request.POST.get("app_id")
application = models.HackerApplication.objects.get(pk=id_)
- comment_text = request.POST.get('comment_text', None)
- motive_of_ban = request.POST.get('motive_of_ban', None)
- dubious_type = request.POST.get('dubious_type', None)
- dubious_comment_text = request.POST.get('dubious_comment_text', None)
+ comment_text = request.POST.get("comment_text", None)
+ motive_of_ban = request.POST.get("motive_of_ban", None)
+ dubious_type = request.POST.get("dubious_type", None)
+ dubious_comment_text = request.POST.get("dubious_comment_text", None)
- if request.POST.get('add_comment'):
+ if request.POST.get("add_comment"):
add_comment(application, request.user, comment_text)
elif request.POST.get("invite") and request.user.is_director:
self.invite_application(application)
@@ -376,7 +378,7 @@ def post(self, request, *args, **kwargs):
self.waitlist_application(application)
elif request.POST.get("slack") and request.user.is_organizer:
self.slack_invite(application)
- elif request.POST.get('set_dubious') and request.user.is_organizer:
+ elif request.POST.get("set_dubious") and request.user.is_organizer:
application.set_dubious(request.user, dubious_type, dubious_comment_text)
elif request.POST.get("set_flagged_cv") and request.user.is_organizer:
application.set_flagged_cv()
@@ -510,8 +512,8 @@ def post(self, request, *args, **kwargs):
tech_vote = request.POST.get("tech_rat", None)
pers_vote = request.POST.get("pers_rat", None)
comment_text = request.POST.get("comment_text", None)
- dubious_type = request.POST.get('dubious_type', None)
- dubious_comment_text = request.POST.get('dubious_comment_text', None)
+ dubious_type = request.POST.get("dubious_type", None)
+ dubious_comment_text = request.POST.get("dubious_comment_text", None)
application = models.HackerApplication.objects.get(
pk=request.POST.get("app_id")
@@ -525,7 +527,9 @@ def post(self, request, *args, **kwargs):
"/applications/hacker/review/" + application.uuid_str
)
elif request.POST.get("set_dubious"):
- application.set_dubious(request.user, dubious_type, dubious_comment_text)
+ application.set_dubious(
+ request.user, dubious_type, dubious_comment_text
+ )
elif request.POST.get("unset_dubious"):
application.unset_dubious()
elif request.POST.get("set_flagged_cv") and request.user.is_organizer:
@@ -615,8 +619,8 @@ def post(self, request, *args, **kwargs):
tech_vote = request.POST.get("tech_rat", None)
pers_vote = request.POST.get("pers_rat", None)
comment_text = request.POST.get("comment_text", None)
- dubious_type = request.POST.get('dubious_type', None)
- dubious_comment_text = request.POST.get('dubious_comment_text', None)
+ dubious_type = request.POST.get("dubious_type", None)
+ dubious_comment_text = request.POST.get("dubious_comment_text", None)
application = models.HackerApplication.objects.get(
pk=request.POST.get("app_id")
@@ -630,7 +634,9 @@ def post(self, request, *args, **kwargs):
"/applications/hacker/review/" + application.uuid_str
)
elif request.POST.get("set_dubious"):
- application.set_dubious(request.user, dubious_type, dubious_comment_text)
+ application.set_dubious(
+ request.user, dubious_type, dubious_comment_text
+ )
elif request.POST.get("unset_dubious"):
application.unset_dubious()
elif request.POST.get("set_flagged_cv") and request.user.is_organizer:
@@ -846,9 +852,7 @@ def get_context_data(self, **kwargs):
return context
-class VolunteerApplicationsListView(
- HaveVolunteerPermissionMixin, _OtherApplicationsListView
-):
+class VolunteerApplicationsListView(IsOrganizerMixin, _OtherApplicationsListView):
table_class = VolunteerListTable
filterset_class = VolunteerFilter
@@ -912,9 +916,7 @@ def get_current_tabs(self):
return mentor_tabs(self.request.user)
-class ReviewVolunteerApplicationView(
- TabsViewMixin, HaveVolunteerPermissionMixin, TemplateView
-):
+class ReviewVolunteerApplicationView(IsOrganizerMixin, TabsViewMixin, TemplateView):
template_name = "other_application_detail.html"
def get_application(self, kwargs):
From dfc20d1ec97c6b8c3cbcd7777997ede0cd4680a8 Mon Sep 17 00:00:00 2001
From: enric4000
+
+
+
+
+
+
+
+
+
+
+
+{% include 'mails/include/email_button.html' with text='Upload tickets' url=form_url %}
+
+{% include 'mails/include/closing.html' with travel="true" %}
+{% endblock %}
\ No newline at end of file
diff --git a/reimbursement/templates/mails/travel_tickets_upload_subject.txt b/reimbursement/templates/mails/travel_tickets_upload_subject.txt
new file mode 100644
index 000000000..b96ef8343
--- /dev/null
+++ b/reimbursement/templates/mails/travel_tickets_upload_subject.txt
@@ -0,0 +1 @@
+[ACTION REQUIRED] Upload your travel tickets
From a2e8ebb0e85ea2835e290028fabf7ffa4584a78b Mon Sep 17 00:00:00 2001
From: polmf <99polmf@gmail.com>
Date: Thu, 19 Feb 2026 18:43:16 +0100
Subject: [PATCH 05/23] mails autoamtics
---
app/hackathon_variables.py | 9 +++-
applications/views.py | 6 +++
cronjob | 2 +
reimbursement/emails.py | 49 ++++++++++++++++---
.../commands/send_devpost_emails.py | 38 ++++++++++++++
.../migrations/0015_auto_20260219_1731.py | 30 ++++++++++++
reimbursement/models.py | 8 ++-
reimbursement/views.py | 4 +-
8 files changed, 134 insertions(+), 12 deletions(-)
create mode 100644 reimbursement/management/commands/send_devpost_emails.py
create mode 100644 reimbursement/migrations/0015_auto_20260219_1731.py
diff --git a/app/hackathon_variables.py b/app/hackathon_variables.py
index 66f089726..92a280615 100644
--- a/app/hackathon_variables.py
+++ b/app/hackathon_variables.py
@@ -60,6 +60,11 @@
# (OPTIONAL) When to arrive at the hackathon
HACKATHON_LEAVE = ""
+# (OPTIONAL) When the event ends (to send Devpost link emails)
+HACKATHON_EVENT_END = timezone.datetime(
+ 2026, 4, 26, 20, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE)
+)
+
# (OPTIONAL) Hackathon live page
HACKATHON_LIVE_PAGE = "https://live.hackupc.com"
@@ -80,11 +85,11 @@
DEFAULT_REIMBURSEMENT_AMOUNT = 100
CURRENCY = "€"
REIMBURSEMENT_EXPIRY_DATE = timezone.datetime(
- 2025, 5, 2, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE)
+ 2026, 5, 2, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE)
)
REIMBURSEMENT_REQUIREMENTS = "You have to submit a project and demo it during the event in order to get reimbursed"
REIMBURSEMENT_DEADLINE = timezone.datetime(
- 2025, 5, 5, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE)
+ 2026, 5, 5, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE)
)
# (OPTIONAL) Max team members. Defaults to 4
diff --git a/applications/views.py b/applications/views.py
index b61dd5936..63877e8af 100644
--- a/applications/views.py
+++ b/applications/views.py
@@ -80,6 +80,12 @@ def get(self, request, *args, **kwargs):
if msg:
msg.send()
+ if application.user.is_hacker() and application.reimb:
+ from reimbursement import emails as reimb_emails
+ reimb_msg = reimb_emails.create_travel_tickets_upload_email(
+ application.user.reimbursement, request
+ )
+ reimb_msg.send()
try:
slack.send_slack_invite(request.user.email)
# Ignore if we can't send, it's only optional
diff --git a/cronjob b/cronjob
index eb4a12213..9e7e140db 100644
--- a/cronjob
+++ b/cronjob
@@ -2,3 +2,5 @@
6 */2 * * * root /usr/local/bin/python /code/manage.py expire_applications
# Run expire reimbursements job at 6 minutes past the hour, every 2 hours
36 */2 * * * root /usr/local/bin/python /code/manage.py expire_reimbursements
+# Run send devpost emails job every Sunday at 20:00
+0 20 * * 0 root /usr/local/bin/python /code/manage.py send_devpost_emails
diff --git a/reimbursement/emails.py b/reimbursement/emails.py
index 8a2dd1055..59085cc7a 100644
--- a/reimbursement/emails.py
+++ b/reimbursement/emails.py
@@ -25,15 +25,50 @@ def create_no_reimbursement_email(reimb, request):
return emails.render_mail("mails/no_reimbursement", reimb.hacker.email, c)
+def create_travel_tickets_upload_email(reimb, request):
+ app = reimb.hacker.application
+ c = _get_context(app, reimb, request)
+ return emails.render_mail(
+ "mails/travel_tickets_upload", reimb.hacker.email, c, action_required=True
+ )
+
+
+def create_ticket_accepted_email(reimb, request):
+ app = reimb.hacker.application
+ c = _get_context(app, reimb, request)
+ return emails.render_mail("mails/ticket_accepted", reimb.hacker.email, c)
+
+
+def create_devpost_upload_email(reimb, request):
+ app = reimb.hacker.application
+ c = _get_context(app, reimb, request)
+ return emails.render_mail(
+ "mails/devpost_upload", reimb.hacker.email, c, action_required=True
+ )
+
+
+def create_project_invalidated_email(reimb, request):
+ app = reimb.hacker.application
+ c = _get_context(app, reimb, request)
+ return emails.render_mail("mails/project_invalidated", reimb.hacker.email, c)
+
+
def _get_context(app, reimb, request):
+ from django.conf import settings
+
+ confirm_url = reverse("confirm_app", kwargs={"id": app.uuid_str}, request=request)
+ form_url = reverse("reimbursement_dashboard", request=request)
+ cancel_url = reverse("cancel_app", kwargs={"id": app.uuid_str}, request=request)
+ if not request:
+ base_url = "https://" + settings.HACKATHON_DOMAIN
+ confirm_url = base_url + confirm_url
+ form_url = base_url + form_url
+ cancel_url = base_url + cancel_url
+
return {
"app": app,
"reimb": reimb,
- "confirm_url": str(
- reverse("confirm_app", kwargs={"id": app.uuid_str}, request=request)
- ),
- "form_url": str(reverse("reimbursement_dashboard", request=request)),
- "cancel_url": str(
- reverse("cancel_app", kwargs={"id": app.uuid_str}, request=request)
- ),
+ "confirm_url": str(confirm_url),
+ "form_url": str(form_url),
+ "cancel_url": str(cancel_url),
}
diff --git a/reimbursement/management/commands/send_devpost_emails.py b/reimbursement/management/commands/send_devpost_emails.py
new file mode 100644
index 000000000..dd66def47
--- /dev/null
+++ b/reimbursement/management/commands/send_devpost_emails.py
@@ -0,0 +1,38 @@
+from django.core.management.base import BaseCommand
+from django.utils import timezone
+from django.conf import settings
+from reimbursement.models import Reimbursement, RE_APPROVED
+from reimbursement import emails
+
+class Command(BaseCommand):
+ help = 'Sends automatic emails to upload Devpost link to approved reimbursements'
+
+ def handle(self, *args, **options):
+ # Check if the event has ended
+ if timezone.now() < settings.HACKATHON_EVENT_END:
+ self.stdout.write(self.style.WARNING('Event has not ended yet. Skipping Devpost emails.'))
+ return
+
+ # We target reimbursements that have the receipt approved (RE_APPROVED),
+ # hasn't uploaded a devpost link yet (devpost=''),
+ # and we haven't sent the reminder email yet (devpost_email_sent=False).
+ reimbursements = Reimbursement.objects.filter(
+ status=RE_APPROVED,
+ devpost='',
+ devpost_email_sent=False
+ )
+
+ self.stdout.write(f'Found {reimbursements.count()} reimbursements pending Devpost link.')
+
+ sent_count = 0
+ for reimb in reimbursements:
+ try:
+ msg = emails.create_devpost_upload_email(reimb, None)
+ msg.send()
+ reimb.devpost_email_sent = True
+ reimb.save()
+ sent_count += 1
+ except Exception as e:
+ self.stderr.write(f'Error sending email to {reimb.hacker.email}: {str(e)}')
+
+ self.stdout.write(self.style.SUCCESS(f'Successfully sent {sent_count} emails.'))
diff --git a/reimbursement/migrations/0015_auto_20260219_1731.py b/reimbursement/migrations/0015_auto_20260219_1731.py
new file mode 100644
index 000000000..b8ddb4405
--- /dev/null
+++ b/reimbursement/migrations/0015_auto_20260219_1731.py
@@ -0,0 +1,30 @@
+# Generated by Django 3.2.23 on 2026-02-19 17:31
+
+import datetime
+from django.db import migrations, models
+from django.utils.timezone import utc
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('reimbursement', '0014_alter_reimbursement_expiration_time'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='reimbursement',
+ name='devpost_email_sent',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AlterField(
+ model_name='reimbursement',
+ name='expiration_time',
+ field=models.DateTimeField(default=datetime.datetime(2026, 5, 2, 16, 0, tzinfo=utc)),
+ ),
+ migrations.AlterField(
+ model_name='reimbursement',
+ name='status',
+ field=models.CharField(choices=[('W', 'Wait listed'), ('PT', 'Pending receipt submission'), ('PA', 'Pending receipt approval'), ('A', 'Receipt approved'), ('X', 'Expired'), ('FS', 'Friend submission'), ('F', 'Reimbursement Approved'), ('PD', 'Pending demo validation'), ('I', 'Invalid')], default='PT', max_length=2),
+ ),
+ ]
diff --git a/reimbursement/models.py b/reimbursement/models.py
index c89fa6575..0fd238db8 100644
--- a/reimbursement/models.py
+++ b/reimbursement/models.py
@@ -19,6 +19,7 @@
RE_FINALIZED = "F"
RE_EXPIRED = "X"
RE_FRIEND_SUBMISSION = "FS"
+RE_INVALID = "I"
RE_STATUS = [
(RE_WAITLISTED, "Wait listed"),
@@ -29,6 +30,7 @@
(RE_FRIEND_SUBMISSION, "Friend submission"),
(RE_FINALIZED, "Reimbursement Approved"),
(RE_PEND_DEMO_VAL, "Pending demo validation"),
+ (RE_INVALID, "Invalid"),
]
@@ -65,6 +67,7 @@ class Reimbursement(models.Model):
public_comment = models.CharField(max_length=300, null=True, blank=True)
devpost = models.URLField(blank=True, null=True, default="")
+ devpost_email_sent = models.BooleanField(default=False)
# User controlled
paypal_email = models.EmailField(null=True, blank=True)
@@ -157,9 +160,10 @@ def validate(self, user):
self.reimbursed_by = user
self.save()
- def invalidate(self, user):
+ def invalidate(self, user, reason):
if self.status == RE_PEND_DEMO_VAL:
- self.status = RE_WAITLISTED
+ self.status = RE_INVALID
+ self.public_comment = reason
self.status_update_date = timezone.now()
self.reimbursed_by = user
self.save()
diff --git a/reimbursement/views.py b/reimbursement/views.py
index 993e93357..890c32f32 100644
--- a/reimbursement/views.py
+++ b/reimbursement/views.py
@@ -170,7 +170,8 @@ def post(self, request, *args, **kwargs):
elif "invalidate" in request.POST:
id_ = kwargs.get("id", None)
reimb = models.Reimbursement.objects.get(pk=id_)
- reimb.invalidate(request.user)
+ reimb.invalidate(request.user, request.POST.get("invalidate_reason"))
+ emails.create_project_invalidated_email(reimb, request).send()
messages.success(request, "Reimbursement invalidated")
else:
id_ = request.POST.get("id", None)
@@ -183,6 +184,7 @@ def post(self, request, *args, **kwargs):
if a_form.is_valid():
a_form.save(commit=False)
a_form.instance.accept_receipt(request.user)
+ emails.create_ticket_accepted_email(a_form.instance, request).send()
a_form.save()
messages.success(request, "Receipt accepted")
else:
From 81dba97b04504490ec2f3ed542bc4aa3c2af838f Mon Sep 17 00:00:00 2001
From: polmf <99polmf@gmail.com>
Date: Thu, 19 Feb 2026 19:56:47 +0100
Subject: [PATCH 06/23] fix
---
reimbursement/management/commands/send_devpost_emails.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/reimbursement/management/commands/send_devpost_emails.py b/reimbursement/management/commands/send_devpost_emails.py
index dd66def47..12c7b101c 100644
--- a/reimbursement/management/commands/send_devpost_emails.py
+++ b/reimbursement/management/commands/send_devpost_emails.py
@@ -4,6 +4,7 @@
from reimbursement.models import Reimbursement, RE_APPROVED
from reimbursement import emails
+
class Command(BaseCommand):
help = 'Sends automatic emails to upload Devpost link to approved reimbursements'
@@ -21,9 +22,9 @@ def handle(self, *args, **options):
devpost='',
devpost_email_sent=False
)
-
+
self.stdout.write(f'Found {reimbursements.count()} reimbursements pending Devpost link.')
-
+
sent_count = 0
for reimb in reimbursements:
try:
@@ -34,5 +35,5 @@ def handle(self, *args, **options):
sent_count += 1
except Exception as e:
self.stderr.write(f'Error sending email to {reimb.hacker.email}: {str(e)}')
-
+
self.stdout.write(self.style.SUCCESS(f'Successfully sent {sent_count} emails.'))
From f437349a5a14f1404002c6107d2e03d3f3b4dede Mon Sep 17 00:00:00 2001
From: polmf <99polmf@gmail.com>
Date: Thu, 19 Feb 2026 19:59:17 +0100
Subject: [PATCH 07/23] fix
---
reimbursement/management/commands/send_devpost_emails.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reimbursement/management/commands/send_devpost_emails.py b/reimbursement/management/commands/send_devpost_emails.py
index 12c7b101c..2f24f6bd6 100644
--- a/reimbursement/management/commands/send_devpost_emails.py
+++ b/reimbursement/management/commands/send_devpost_emails.py
@@ -24,7 +24,7 @@ def handle(self, *args, **options):
)
self.stdout.write(f'Found {reimbursements.count()} reimbursements pending Devpost link.')
-
+
sent_count = 0
for reimb in reimbursements:
try:
From 9fba62db170cec5c830b00a503c82130280999bc Mon Sep 17 00:00:00 2001
From: polmf <99polmf@gmail.com>
Date: Thu, 19 Feb 2026 21:02:07 +0100
Subject: [PATCH 08/23] fix alguns errors
---
reimbursement/templates/mails/project_invalidated_subject.txt | 1 +
reimbursement/views.py | 1 +
2 files changed, 2 insertions(+)
create mode 100644 reimbursement/templates/mails/project_invalidated_subject.txt
diff --git a/reimbursement/templates/mails/project_invalidated_subject.txt b/reimbursement/templates/mails/project_invalidated_subject.txt
new file mode 100644
index 000000000..eafe9d603
--- /dev/null
+++ b/reimbursement/templates/mails/project_invalidated_subject.txt
@@ -0,0 +1 @@
+[IMPORTANT INFORMATION] Your reimbursement has been invalidated
diff --git a/reimbursement/views.py b/reimbursement/views.py
index 890c32f32..e1e847a1a 100644
--- a/reimbursement/views.py
+++ b/reimbursement/views.py
@@ -249,6 +249,7 @@ def post(self, request, *args, **kwargs):
if a_form.is_valid():
a_form.save(commit=False)
a_form.instance.accept_receipt(request.user)
+ emails.create_ticket_accepted_email(a_form.instance, request).send()
a_form.save()
messages.success(request, "Receipt accepted")
else:
From 2e608118b10a4788a41ad36e0dd630ccdb82efcc Mon Sep 17 00:00:00 2001
From: polmf <99polmf@gmail.com>
Date: Thu, 19 Feb 2026 23:36:49 +0100
Subject: [PATCH 09/23] imatges wallet
---
app/utils.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/utils.py b/app/utils.py
index bbee6463e..b0ce14b98 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -235,7 +235,7 @@ def generateGTicketUrl(qrValue: str):
},
"logo": {
"sourceUri": {
- "uri": "https://i.ibb.co/b5TQV4md/hackupc2025.png",
+ "uri": "https://i.ibb.co/g2GGdMB/Copy-of-Isotipo-B-6.png",
},
"contentDescription": {
"defaultValue": {
@@ -253,7 +253,7 @@ def generateGTicketUrl(qrValue: str):
"imageModulesData": [
{
"mainImage": {
- "sourceUri": {"uri": "https://hackupc.com/ogimage.png"},
+ "sourceUri": {"uri": "https://hackupc.com/og_image2026.png"},
"contentDescription": {
"defaultValue": {"language": "en-US", "value": "Event picture"}
},
From d56573e19a68c49a7125a41c5a2ce2b820f31494 Mon Sep 17 00:00:00 2001
From: EncryptEx <41539618+EncryptEx@users.noreply.github.com>
Date: Fri, 20 Feb 2026 13:00:25 +0100
Subject: [PATCH 10/23] style: updated wallet card for 2026 edition
---
app/utils.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/utils.py b/app/utils.py
index b0ce14b98..b5dd4fdda 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -300,10 +300,10 @@ def generateGTicketUrl(qrValue: str):
},
"dateTime": {"start": "2026-04-24T16:00", "end": "2026-04-26T17:00"},
"reviewStatus": "UNDER_REVIEW",
- "hexBackgroundColor": "#231F20",
+ "hexBackgroundColor": "#5B5340",
"heroImage": {
"sourceUri": {
- "uri": "https://i.ibb.co/LL5T8d0/walletv2.png",
+ "uri": "https://i.ibb.co/6Rkp3P6M/og-image2026.png",
},
"contentDescription": {
"defaultValue": {
From 37e90dba7e3e45bf3364f78f49f660daf3c179c8 Mon Sep 17 00:00:00 2001
From: polmf <99polmf@gmail.com>
Date: Fri, 20 Feb 2026 13:02:21 +0100
Subject: [PATCH 11/23] wallet final
---
app/utils.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/utils.py b/app/utils.py
index b0ce14b98..a356f9fdf 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -300,10 +300,10 @@ def generateGTicketUrl(qrValue: str):
},
"dateTime": {"start": "2026-04-24T16:00", "end": "2026-04-26T17:00"},
"reviewStatus": "UNDER_REVIEW",
- "hexBackgroundColor": "#231F20",
+ "hexBackgroundColor": "#AADEFE",
"heroImage": {
"sourceUri": {
- "uri": "https://i.ibb.co/LL5T8d0/walletv2.png",
+ "uri": "https://i.ibb.co/6Rkp3P6M/og-image2026.png",
},
"contentDescription": {
"defaultValue": {
From e02e424144db0cd885d2a45676314d032184948e Mon Sep 17 00:00:00 2001
From: EncryptEx <41539618+EncryptEx@users.noreply.github.com>
Date: Fri, 20 Feb 2026 13:17:16 +0100
Subject: [PATCH 12/23] style: fix alt on banner
---
app/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/utils.py b/app/utils.py
index a356f9fdf..a686d9ee6 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -308,7 +308,7 @@ def generateGTicketUrl(qrValue: str):
"contentDescription": {
"defaultValue": {
"language": "en-US",
- "value": "HackUPC galactic banner",
+ "value": "HackUPC 2026 Banner",
},
},
},
From 7758f3e681001d2b958f09a0c23fad4bc05e915f Mon Sep 17 00:00:00 2001
From: EncryptEx <41539618+EncryptEx@users.noreply.github.com>
Date: Fri, 20 Feb 2026 13:44:53 +0100
Subject: [PATCH 13/23] final version
---
app/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/utils.py b/app/utils.py
index a686d9ee6..d784b52cb 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -303,7 +303,7 @@ def generateGTicketUrl(qrValue: str):
"hexBackgroundColor": "#AADEFE",
"heroImage": {
"sourceUri": {
- "uri": "https://i.ibb.co/6Rkp3P6M/og-image2026.png",
+ "uri": "https://i.ibb.co/nN4GxfFt/imatge-wallet.png",
},
"contentDescription": {
"defaultValue": {
From db59b85216a9808709cbd9f2038cae3fc5e9cea3 Mon Sep 17 00:00:00 2001
From: Yaoyao
-
-
Hey {{ app.user.get_full_name }},
For us to accept your travel reimbursement request, you need to upload VALID travel tickets in https://my.hackupc.com/reimbursement/dash_board/. -
-A ticket is considered valid if and only if:
+For us to accept your travel reimbursement request, you need to upload VALID travel tickets here:
+ +{% include 'mails/include/email_button.html' with text='Upload tickets' url=form_url %} + +A ticket is considered valid if and only if:
Remember! The paypal account MUST exist.
+ {% include 'mails/include/closing.html' with travel="true" %} {% endblock %} \ No newline at end of file From efa602ed1c403de4ff72f4949fd7e4a61689f097 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Wed, 25 Feb 2026 17:47:50 +0100 Subject: [PATCH 18/23] cronjob diumenges a les 20 --- cronjob | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cronjob b/cronjob index 9e7e140db..574ab4244 100644 --- a/cronjob +++ b/cronjob @@ -2,5 +2,5 @@ 6 */2 * * * root /usr/local/bin/python /code/manage.py expire_applications # Run expire reimbursements job at 6 minutes past the hour, every 2 hours 36 */2 * * * root /usr/local/bin/python /code/manage.py expire_reimbursements -# Run send devpost emails job every Sunday at 20:00 -0 20 * * 0 root /usr/local/bin/python /code/manage.py send_devpost_emails +# Run send devpost emails job every Sunday at 20:00 server va 1 hora abans +0 19 * * 0 root /usr/local/bin/python /code/manage.py send_devpost_emails From bf49141bf2bc3134bdbe65d501e6e74132c8c0b7 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Thu, 26 Feb 2026 17:43:59 +0100 Subject: [PATCH 19/23] mail actualitzat --- .../mails/travel_tickets_upload_message.html | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/reimbursement/templates/mails/travel_tickets_upload_message.html b/reimbursement/templates/mails/travel_tickets_upload_message.html index d799a3a8d..4f33175fb 100644 --- a/reimbursement/templates/mails/travel_tickets_upload_message.html +++ b/reimbursement/templates/mails/travel_tickets_upload_message.html @@ -1,24 +1,34 @@ {% extends 'base_email.html' %} -{% block preheader %}Upload your travel tickets{% endblock %} +{% block preheader %}Upload your travel ticket{% endblock %} {% block content %} -Hey {{ app.user.get_full_name }},
+Hey {{ app.user.get_full_name }},
For us to accept your travel reimbursement request, you need to upload VALID travel tickets here:
-{% include 'mails/include/email_button.html' with text='Upload tickets' url=form_url %} +Congratulations on being accepted to HackUPC 2026! 🎉
-A ticket is considered valid if and only if:
+We noticed that you selected the travel reimbursement option. To proceed, please upload a valid travel ticket to your MyHackUPC dashboard as soon as possible.
+ +{% include 'mails/include/email_button.html' with text='Upload ticket' url=form_url %} + +A ticket will be considered valid only if:
Remember! The paypal account MUST exist.
+Reimbursement Policy
+The reimbursed amount will be the minimum of:
+Congratulations {{ app.user.get_full_name }}!
+Your travel reimbursement request has been approved by our Finance Team. You will receive the reimbursed amount within two months after the event. Please note that minor delays may occur due to the high volume of travel reimbursement requests this year.
+ +You have been assigned: {{ reimb.assigned_money }} €
+ +The money will be transferred to your PayPal account. Make sure the PayPal account you provided is correct and active.
+ +{% include 'mails/include/closing.html' with travel="true" %} +{% endblock %} diff --git a/reimbursement/templates/mails/devpost_approved_subject.txt b/reimbursement/templates/mails/devpost_approved_subject.txt new file mode 100644 index 000000000..92c21d31b --- /dev/null +++ b/reimbursement/templates/mails/devpost_approved_subject.txt @@ -0,0 +1 @@ +Travel Reimbursement – Approved! \ No newline at end of file diff --git a/reimbursement/templates/mails/project_invalidated_message.html b/reimbursement/templates/mails/project_invalidated_message.html new file mode 100644 index 000000000..19c08743d --- /dev/null +++ b/reimbursement/templates/mails/project_invalidated_message.html @@ -0,0 +1,17 @@ +{% extends 'base_email.html' %} +{% block preheader %}Your travel reimbursement request – Not Approved{% endblock %} + +{% block content %} +Dear {{ app.user.get_full_name }},
+After reviewing your travel reimbursement request, we regret to inform you that your application cannot be approved. This may be due to one of the following reasons:
+ +If you believe this decision has been made in error or would like further clarification, please feel free to contact us.
+ +{% include 'mails/include/closing.html' with travel="true" %} +{% endblock %} diff --git a/reimbursement/templates/mails/project_invalidated_subject.txt b/reimbursement/templates/mails/project_invalidated_subject.txt index eafe9d603..080f7d800 100644 --- a/reimbursement/templates/mails/project_invalidated_subject.txt +++ b/reimbursement/templates/mails/project_invalidated_subject.txt @@ -1 +1 @@ -[IMPORTANT INFORMATION] Your reimbursement has been invalidated +Travel Reimbursement Request – Not Approved diff --git a/reimbursement/views.py b/reimbursement/views.py index e1e847a1a..99a63af11 100644 --- a/reimbursement/views.py +++ b/reimbursement/views.py @@ -145,6 +145,7 @@ def post(self, request, *args, **kwargs): if form.is_valid(): form.save(commit=False) reimb.validate(request.user) + emails.create_devpost_approved_email(reimb, request).send() form.save() messages.success(self.request, "Reimbursement successfully validated!") else: From d866a7d5a0e1bb5ebedb62a3ec8e7ba9938db5fe Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Sat, 28 Feb 2026 16:33:10 +0100 Subject: [PATCH 23/23] correcio mails --- reimbursement/templates/mails/devpost_approved_message.html | 2 +- reimbursement/templates/mails/project_invalidated_message.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reimbursement/templates/mails/devpost_approved_message.html b/reimbursement/templates/mails/devpost_approved_message.html index 83613f611..50497be18 100644 --- a/reimbursement/templates/mails/devpost_approved_message.html +++ b/reimbursement/templates/mails/devpost_approved_message.html @@ -6,7 +6,7 @@Your travel reimbursement request has been approved by our Finance Team. You will receive the reimbursed amount within two months after the event. Please note that minor delays may occur due to the high volume of travel reimbursement requests this year.
-You have been assigned: {{ reimb.assigned_money }} €
+You have been assigned: {{ h_currency }}{{ reimb.reimbursement_money }}
The money will be transferred to your PayPal account. Make sure the PayPal account you provided is correct and active.
diff --git a/reimbursement/templates/mails/project_invalidated_message.html b/reimbursement/templates/mails/project_invalidated_message.html index 19c08743d..73c52b8a7 100644 --- a/reimbursement/templates/mails/project_invalidated_message.html +++ b/reimbursement/templates/mails/project_invalidated_message.html @@ -12,6 +12,6 @@If you believe this decision has been made in error or would like further clarification, please feel free to contact us.
+Best regards,
-{% include 'mails/include/closing.html' with travel="true" %} {% endblock %}