Skip to content

Commit ae69425

Browse files
committed
Merge branch 'develop'
2 parents 49ccc20 + 444c251 commit ae69425

File tree

18 files changed

+176
-164
lines changed

18 files changed

+176
-164
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.8.0] - 2025-05-15
11+
12+
### Added
13+
14+
- Remind permit expiration in 1 week or 1 day ([68f240e](https://github.com/City-of-Helsinki/parking-permits/commit/68f240e0b65514d83af7a4e2c0423eb604ec01b1))
15+
- Add permit end time to expiration remind ([0539567](https://github.com/City-of-Helsinki/parking-permits/commit/0539567202fcc2e1937a39cb1706da5305b7ab1b))
16+
- Remove dates from active temporary vehicle ([a3275ec](https://github.com/City-of-Helsinki/parking-permits/commit/a3275ec159fefd06b0e5ea1f1fc5d30426cc0ff5))
17+
- Limit webshop temporary vehicle creation ([e1a9e89](https://github.com/City-of-Helsinki/parking-permits/commit/e1a9e893d85b3d0038656ada71e06f8d2cd49034))
18+
- Improve expiration remind email messages ([6586d19](https://github.com/City-of-Helsinki/parking-permits/commit/6586d193b146b770d4cdbb8c4efd21c6e0aca422))
19+
- Refund emails via admin request for approval ([9c5bd3c](https://github.com/City-of-Helsinki/parking-permits/commit/9c5bd3c76a57fa2251e1e9ff1c53dba978d716be))
20+
21+
### Fixed
22+
23+
- Call correct temporary vehicle event function ([78bee6c](https://github.com/City-of-Helsinki/parking-permits/commit/78bee6c2e0599da7b81cd2b49000034f7bbddfe3))
24+
1025
## [1.7.2] - 2025-05-08
1126

1227
### Fixed

locale/fi/LC_MESSAGES/django.po

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ msgid ""
88
msgstr ""
99
"Project-Id-Version: PACKAGE VERSION\n"
1010
"Report-Msgid-Bugs-To: \n"
11-
"POT-Creation-Date: 2024-12-13 08:15+0200\n"
11+
"POT-Creation-Date: 2025-05-13 23:16+0300\n"
1212
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -212,6 +212,9 @@ msgstr ""
212212
msgid "You can buy permit only for address %(primary_address)s."
213213
msgstr "Voit ostaa pysäköintitunnuksen vain osoitteelle %(primary_address)s."
214214

215+
msgid "Source: Transport register, Traficom"
216+
msgstr "Lähde: Liikenneasioidenrekisteri, Traficom"
217+
215218
msgid "Forbidden"
216219
msgstr "Lupa evätty"
217220

@@ -341,6 +344,9 @@ msgstr "Palautukset"
341344
msgid "Refund ID"
342345
msgstr "Palautusnumero"
343346

347+
msgid "incl. VAT"
348+
msgstr "sis. ALV"
349+
344350
msgid "Extra info"
345351
msgstr "Lisätiedot"
346352

@@ -1094,9 +1100,6 @@ msgstr ""
10941100
"* Tunnus on voimassa valitsemastasi alkamispäivästä lähtien, kun "
10951101
"maksusuoritus on hyväksytty"
10961102

1097-
msgid "Source: Transport register, Traficom"
1098-
msgstr "Lähde: Liikenneasioidenrekisteri, Traficom"
1099-
11001103
msgid "Parking permit expiration date"
11011104
msgstr "Pysäköintitunnuksen päättymispäivä"
11021105

@@ -1125,12 +1128,15 @@ msgstr "Palaute"
11251128
msgid "Customer service contact info"
11261129
msgstr "Asiakaspalvelu ja yhteystiedot"
11271130

1131+
msgid "Your parking permit expires at"
1132+
msgstr "Pysäköintitunnuksesi voimassaoloaika päättyy"
1133+
11281134
msgid ""
1129-
"Your parking permit will expire soon. You can renew your permit by logging "
1130-
"in to the service again."
1135+
"You can renew your permit by logging in to the service and selecting 'Extend "
1136+
"parking permit'."
11311137
msgstr ""
1132-
"Pysäköintitunnuksesi voimassaoloaika päättyy lähiaikoina. Voit uusia "
1133-
"tunnuksen kirjautumalla palveluun"
1138+
"Voit uusia tunnuksen kirjautumalla palveluun ja valitsemalla 'Jatka "
1139+
"pysäköintitunnuksen voimassaoloa'."
11341140

11351141
msgid "Thank you for your order!"
11361142
msgstr "Kiitos tilauksestasi!"
@@ -1166,9 +1172,6 @@ msgstr ""
11661172
msgid "Total refundable amount"
11671173
msgstr "Palautettava summa yhteensä"
11681174

1169-
msgid "incl. VAT"
1170-
msgstr "sis. ALV"
1171-
11721175
msgid ""
11731176
"You are entitled to a refund and it has been registered. You will receive a "
11741177
"separate notification when the return has been approved."

locale/sv/LC_MESSAGES/django.po

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ msgid ""
88
msgstr ""
99
"Project-Id-Version: PACKAGE VERSION\n"
1010
"Report-Msgid-Bugs-To: \n"
11-
"POT-Creation-Date: 2024-12-13 08:15+0200\n"
11+
"POT-Creation-Date: 2025-05-13 23:16+0300\n"
1212
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -208,6 +208,9 @@ msgstr "Du kan ha maximalt %(max_allowed_permit)s tillstånd."
208208
msgid "You can buy permit only for address %(primary_address)s."
209209
msgstr "Du kan endast köpa tillstånd för adressen %(primary_address)s."
210210

211+
msgid "Source: Transport register, Traficom"
212+
msgstr "Källä: Trafik- och transportregistret, Traficom"
213+
211214
msgid "Forbidden"
212215
msgstr "Förbjuden"
213216

@@ -337,6 +340,9 @@ msgstr "Återbetalning"
337340
msgid "Refund ID"
338341
msgstr "Återbetalning ID"
339342

343+
msgid "incl. VAT"
344+
msgstr "inkl. MOMS"
345+
340346
msgid "Extra info"
341347
msgstr "Ytterligare uppgifter"
342348

@@ -1094,9 +1100,6 @@ msgstr ""
10941100
"* Parkeringpermit är i kraft från den valda startdatum, när betalningen är "
10951101
"godkännt"
10961102

1097-
msgid "Source: Transport register, Traficom"
1098-
msgstr "Källä: Trafik- och transportregistret, Traficom"
1099-
11001103
msgid "Parking permit expiration date"
11011104
msgstr "Tillståndet upphör att gälla"
11021105

@@ -1125,12 +1128,14 @@ msgstr "Ge respons"
11251128
msgid "Customer service contact info"
11261129
msgstr "Kundtjänst kontaktuppgifter"
11271130

1131+
msgid "Your parking permit expires at"
1132+
msgstr "Ditt parkeringstillstånd går ut vid"
1133+
11281134
msgid ""
1129-
"Your parking permit will expire soon. You can renew your permit by logging "
1130-
"in to the service again."
1131-
msgstr ""
1132-
"Ditt parkeringstillstånd går snart ut. Du kan förnya ditt tillstånd genom "
1133-
"att logga in på tjänsten."
1135+
"You can renew your permit by logging in to the service and selecting 'Extend "
1136+
"parking permit'."
1137+
msgstr "Du kan förnya ditt tillstånd genom att logga in på tjänsten och välja "
1138+
"'Förläng parkeringstillstånd'."
11341139

11351140
msgid "Thank you for your order!"
11361141
msgstr "Tack för din beställning!"
@@ -1170,9 +1175,6 @@ msgstr ""
11701175
msgid "Total refundable amount"
11711176
msgstr "Totalt återbetalningsbart belopp"
11721177

1173-
msgid "incl. VAT"
1174-
msgstr "inkl. MOMS"
1175-
11761178
msgid ""
11771179
"You are entitled to a refund and it has been registered. You will receive a "
11781180
"separate notification when the return has been approved."

parking_permits/admin_resolvers.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -873,8 +873,6 @@ def resolve_update_resident_permit(
873873
)
874874
logger.info(f"Refund for lowered permit price created: {refund}")
875875
refunds.append(refund)
876-
if refunds:
877-
send_refund_email(RefundEmailType.CREATED, customer, refunds)
878876

879877
bypass_traficom_validation = permit_info.get("bypass_traficom_validation", False)
880878

@@ -1178,6 +1176,16 @@ def resolve_refunds(obj, info, page_input, order_by=None, search_params=None):
11781176
def resolve_request_for_approval(obj, info, ids):
11791177
qs = Refund.objects.filter(id__in=ids, status=RefundStatus.OPEN)
11801178
qs.update(status=RefundStatus.REQUEST_FOR_APPROVAL)
1179+
# send emails to customers
1180+
refunds = Refund.objects.filter(
1181+
id__in=ids, status=RefundStatus.REQUEST_FOR_APPROVAL
1182+
).prefetch_related("orders__customer")
1183+
for refund in refunds:
1184+
send_refund_email(
1185+
RefundEmailType.CREATED,
1186+
refund.orders.first().customer,
1187+
[refund],
1188+
)
11811189
return qs.count()
11821190

11831191

parking_permits/cron.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ def automatic_expiration_of_permits():
7272
def automatic_expiration_remind_notification_of_permits():
7373
logger.info("Automatically sending remind notifications for permits started...")
7474
count = 0
75-
now = tz.localdate(tz.now())
75+
current_date = tz.localdate(tz.now())
7676
expiring_permits = ParkingPermit.objects.filter(
77-
end_time__lt=now + relativedelta(weeks=1),
77+
Q(end_time__date=current_date + relativedelta(weeks=1))
78+
| Q(end_time__date=current_date + relativedelta(days=1)),
7879
status=ParkingPermitStatus.VALID,
7980
contract_type=ContractType.FIXED_PERIOD,
8081
)

parking_permits/customer_permit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def remove_temporary_vehicle(self, permit_id):
134134
prev_active_temp_vehicles = list(active_temp_vehicles)
135135
active_temp_vehicles.update(is_active=False)
136136
for temp_vehicle in prev_active_temp_vehicles:
137-
ParkingPermitEventFactory.make_add_temporary_vehicle_event(
137+
ParkingPermitEventFactory.make_remove_temporary_vehicle_event(
138138
permit, temp_vehicle, created_by=self.customer.user
139139
)
140140
sync_with_parkkihubi(permit)

parking_permits/forms.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from django.db import models
88
from django.db.models import OuterRef, Q, Subquery, Value
99
from django.db.models.functions import Concat
10-
from django.utils import timezone
1110

1211
from parking_permits.models import (
1312
Address,
@@ -137,8 +136,6 @@ def filter_queryset(self, qs):
137136
if not q:
138137
return self.get_empty_queryset()
139138

140-
now = timezone.now()
141-
142139
fields = [
143140
"vehicle__registration_number",
144141
"temp_vehicles__vehicle__registration_number",
@@ -170,8 +167,6 @@ def filter_queryset(self, qs):
170167
TemporaryVehicle.objects.filter(
171168
parkingpermit__pk=OuterRef("pk"),
172169
is_active=True,
173-
start_time__lte=now,
174-
end_time__gte=now,
175170
).values("vehicle__registration_number")[:1]
176171
)
177172
).filter(query)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 5.1.8 on 2025-05-13 10:21
2+
3+
import django.db.models.deletion
4+
from django.conf import settings
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
("parking_permits", "0070_announcement_emails_handled"),
12+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13+
]
14+
15+
operations = [
16+
migrations.AddField(
17+
model_name="temporaryvehicle",
18+
name="created_by",
19+
field=models.ForeignKey(
20+
blank=True,
21+
null=True,
22+
on_delete=django.db.models.deletion.PROTECT,
23+
related_name="+",
24+
to=settings.AUTH_USER_MODEL,
25+
verbose_name="Created by",
26+
),
27+
),
28+
]

parking_permits/models/order.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
SubscriptionCancelError,
1919
SubscriptionValidationError,
2020
)
21-
from ..services.mail import RefundEmailType, send_refund_email
2221
from ..utils import (
2322
calc_net_price,
2423
calc_vat_price,
@@ -675,7 +674,7 @@ def cancel(self, cancel_reason, cancel_from_talpa=True, iban=""):
675674
logger.info(f"Creating Refund for permit {str(permit.id)}")
676675
from ..resolver_utils import create_refund
677676

678-
refund = create_refund(
677+
create_refund(
679678
user=permit.customer.user,
680679
permits=[permit],
681680
orders=[order],
@@ -687,7 +686,6 @@ def cancel(self, cancel_reason, cancel_from_talpa=True, iban=""):
687686
# Mark the order item as refunded
688687
order_item.is_refunded = True
689688
order_item.save()
690-
send_refund_email(RefundEmailType.CREATED, permit.customer, [refund])
691689
logger.info(f"Refund for permit {str(permit.id)} created successfully")
692690

693691
# Try to cancel subscription from Talpa as well

parking_permits/models/parking_permit.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -291,12 +291,10 @@ def is_order_confirmed(self):
291291

292292
@property
293293
def active_temporary_vehicle(self):
294-
now = timezone.now()
294+
"""Get the active temporary vehicle for the permit"""
295295
return (
296296
self.temporary_vehicles.filter(
297297
is_active=True,
298-
start_time__lte=now,
299-
end_time__gte=now,
300298
)
301299
.select_related("vehicle")
302300
.first()
@@ -908,11 +906,18 @@ def parse_temporary_vehicle_times(
908906
_("Temporary vehicle start time has to be after permit start time")
909907
)
910908

909+
# prevent end time from being less than start time + 1 hour
911910
end_dt = max(
912911
timezone.localtime(isoparse(end_time)),
913912
start_dt + timezone.timedelta(hours=1),
914913
)
915914

915+
# prevent end time from being more than permit end time
916+
end_dt = min(
917+
end_dt,
918+
self.end_time,
919+
)
920+
916921
return start_dt, end_dt
917922

918923
def add_temporary_vehicle(
@@ -929,7 +934,7 @@ def add_temporary_vehicle(
929934
If `check_limit` is `True` and limit is exceeded, will raise a TemporaryVehicleValidationError.
930935
931936
"""
932-
if check_limit and self.is_temporary_vehicle_limit_exceeded():
937+
if check_limit and self.is_temporary_vehicle_limit_exceeded(user):
933938
raise TemporaryVehicleValidationError(
934939
_(
935940
"Can not have more than 2 temporary vehicles in 365 days from first one."
@@ -942,6 +947,8 @@ def add_temporary_vehicle(
942947
vehicle=vehicle,
943948
end_time=end_time,
944949
start_time=start_time,
950+
created_by=user,
951+
created_at=timezone.now(),
945952
)
946953

947954
self.temp_vehicles.add(temp_vehicle)
@@ -952,18 +959,17 @@ def add_temporary_vehicle(
952959

953960
return temp_vehicle
954961

955-
def is_temporary_vehicle_limit_exceeded(self) -> bool:
956-
"""Check limit of temporary vehicles. A user can only
957-
have max 2 temp vehicles over 12 months."""
962+
def is_temporary_vehicle_limit_exceeded(self, user) -> bool:
963+
"""Check limit of temporary vehicles.
964+
A user can only create max 2 temp vehicles over 12 months."""
958965
return (
959-
len(
960-
self.temp_vehicles.filter(
961-
start_time__gte=get_end_time(timezone.now(), -12)
962-
)
963-
.order_by("-start_time")
964-
.values("pk")[:2]
966+
self.temp_vehicles.filter(
967+
start_time__gte=get_end_time(timezone.now(), -12),
968+
created_by=user,
965969
)
966-
== 2
970+
.order_by("-start_time")
971+
.count()
972+
>= 2
967973
)
968974

969975
def get_unused_order_items(self):

0 commit comments

Comments
 (0)