Skip to content

Commit c749a58

Browse files
authored
Merge pull request #2030 from FJNR-inc/develop
New release
2 parents ddd6e7b + f6eb480 commit c749a58

15 files changed

+236
-55
lines changed

retirement/models.py

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,7 @@ def get_participants(self):
999999
participants.add(reservation.user)
10001000
return list(participants)
10011001

1002-
def process_impacted_users(self, reason, reason_message, force_refund):
1002+
def process_impacted_users(self, reason, reason_message, refund_policy):
10031003
"""
10041004
Notify and potentially refund user for a reason happening on retreat:
10051005
Retreat cancelled, deleted ...
@@ -1010,19 +1010,18 @@ def process_impacted_users(self, reason, reason_message, force_refund):
10101010
# retrieve active users before we cancel the reservation
10111011
users = self.get_participants()
10121012
from .services import send_updated_retreat_email
1013-
self.cancel_participants_reservation(force_refund)
1013+
self.cancel_participants_reservation(refund_policy)
10141014
send_updated_retreat_email(
10151015
self,
10161016
users,
10171017
reason,
10181018
reason_message,
10191019
)
10201020

1021-
def cancel_participants_reservation(self, force_refund):
1021+
def cancel_participants_reservation(self, refund_policy):
10221022
"""
10231023
Cancel all participants' reservation
1024-
:params force_refund: True if we want to force refund for participants,
1025-
otherwise regular refund will be done.
1024+
:params refund_policy: Admin policy to apply to refund participants
10261025
"""
10271026
active_reservations = self.reservations.filter(is_active=True)
10281027
refund_data = []
@@ -1033,7 +1032,7 @@ def cancel_participants_reservation(self, force_refund):
10331032
refund_data.append(
10341033
reservation.process_refund(
10351034
Reservation.CANCELATION_REASON_RETREAT_DELETED,
1036-
force_refund)
1035+
refund_policy)
10371036
)
10381037

10391038
# Send emails to each participant having a refund
@@ -1042,7 +1041,7 @@ def cancel_participants_reservation(self, force_refund):
10421041
if data:
10431042
Reservation.send_refund_confirmation_email(data)
10441043

1045-
def custom_delete(self, deletion_message=None, force_refund=False):
1044+
def custom_delete(self, deletion_message=None, refund_policy=None):
10461045
"""
10471046
Deleting a retreat sends an email to all registered participants
10481047
set the retreat to inactive and hide it from the admin panel.
@@ -1051,7 +1050,8 @@ def custom_delete(self, deletion_message=None, force_refund=False):
10511050
"""
10521051
self.is_active = False
10531052
self.hide_from_client_admin_panel = True
1054-
self.process_impacted_users('deletion', deletion_message, force_refund)
1053+
self.process_impacted_users(
1054+
'deletion', deletion_message, refund_policy)
10551055
self.save()
10561056

10571057

@@ -1257,16 +1257,22 @@ class Reservation(SafeDeleteModel):
12571257
def __str__(self):
12581258
return str(self.user)
12591259

1260-
def get_refund_value(self, total_refund=False):
1261-
if self.order_line is None or self.order_line.is_made_by_admin:
1260+
def get_refund_value(self, refund_policy=None):
1261+
"""
1262+
Refund the user. Admin can specify a refund_policy to fully refund
1263+
user, or not refund at all or refund at retreat rate.
1264+
"""
1265+
if self.order_line is None or \
1266+
self.order_line.is_made_by_admin or \
1267+
refund_policy == 'no_refund':
12621268
return 0
12631269

12641270
# First get net pay: total cost
12651271
refund_value = float(self.order_line.total_cost)
12661272
# Add the tax rate, so we have the real value pay by the user
12671273
refund_value *= TAX_RATE + 1.0
12681274

1269-
if not total_refund:
1275+
if refund_policy in ['retreat_rate', None]:
12701276
# keep only the part that the retreat allow to refund
12711277
refund_value *= self.retreat.refund_rate / 100
12721278

@@ -1279,8 +1285,8 @@ def get_refund_value(self, total_refund=False):
12791285

12801286
return round(refund_value, 2) if refund_value > 0 else 0
12811287

1282-
def make_refund(self, refund_reason, total_refund=False):
1283-
amount_to_refund = self.get_refund_value(total_refund)
1288+
def make_refund(self, refund_reason, refund_policy=None):
1289+
amount_to_refund = self.get_refund_value(refund_policy)
12841290

12851291
# paysafe use value without cent
12861292
amount_to_refund_paysafe = int(round(amount_to_refund * 100))
@@ -1314,10 +1320,10 @@ def send_refund_confirmation_email(email_dict):
13141320
# Here the price takes the applied coupon into account, if
13151321
# applicable.
13161322
old_retreat = {
1317-
'price': (amount * retreat.refund_rate) / 100,
1323+
'price': amount,
13181324
'name': "{0}: {1}".format(
13191325
_("Retreat"),
1320-
retreat.name
1326+
retreat.name,
13211327
)
13221328
}
13231329

@@ -1362,7 +1368,7 @@ def send_refund_confirmation_email(email_dict):
13621368
)
13631369
raise
13641370

1365-
def process_refund(self, cancel_reason, force_refund):
1371+
def process_refund(self, cancel_reason, refund_policy):
13661372
"""
13671373
User will be refund the retreat's "refund_rate" if we're at least
13681374
"min_day_refund" days before the event.
@@ -1398,12 +1404,17 @@ def process_refund(self, cancel_reason, force_refund):
13981404
# refundable
13991405
#
14001406
# 2 - An admin want to force a refund and the user paid for
1401-
# his reservation
1407+
# his reservation or force no refund
14021408
#
14031409
# In all case, only paid reservation (amount > 0) can be refunded
14041410
if self.get_refund_value() > 0:
1405-
process_refund = (respects_minimum_days and refundable) or \
1406-
force_refund
1411+
if refund_policy:
1412+
if refund_policy in ['retreat_rate', 'full_rate']:
1413+
process_refund = True
1414+
else:
1415+
process_refund = False
1416+
else:
1417+
process_refund = respects_minimum_days and refundable
14071418
else:
14081419
process_refund = False
14091420

@@ -1422,7 +1433,9 @@ def process_refund(self, cancel_reason, force_refund):
14221433
if process_refund:
14231434
try:
14241435
refund = self.make_refund(
1425-
self.REFUND_REASON[cancel_reason])
1436+
self.REFUND_REASON[cancel_reason],
1437+
refund_policy
1438+
)
14261439
except PaymentAPIError as err:
14271440
if str(err) == PAYSAFE_EXCEPTION['3406']:
14281441
raise rest_framework_serializers.ValidationError({
@@ -1486,6 +1499,7 @@ def process_refund(self, cancel_reason, force_refund):
14861499
'user': user,
14871500
'total_amount': refund.amount,
14881501
'amount_tax': round(refund.amount * TAX_RATE, 2),
1502+
'refund_policy': refund_policy,
14891503
}
14901504
return email_data
14911505

retirement/tests/tests_viewset_Reservation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,7 @@ def test_delete_scheduler_working(self):
13321332
'retreat:reservation-detail',
13331333
kwargs={'pk': self.reservation_admin.id},
13341334
),
1335+
data={"refund_policy": "retreat_rate"},
13351336
)
13361337

13371338
self.assertEqual(

retirement/tests/tests_viewset_Retreat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,7 @@ def test_delete_with_participants(self, mock_email, mock_cancel):
11151115
'deletion',
11161116
deletion_message
11171117
)
1118-
mock_cancel.assert_called_once_with(False)
1118+
mock_cancel.assert_called_once_with(None)
11191119

11201120
self.retreat.refresh_from_db()
11211121
self.assertFalse(self.retreat.is_active)

retirement/tests/tests_viewset_RetreatDate.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def test_update_no_change_as_admin(self, mock_email, mock_cancel):
208208
'update',
209209
reason_message,
210210
)
211-
mock_cancel.assert_called_once_with(False)
211+
mock_cancel.assert_called_once_with(None)
212212

213213
self.assertEqual(response.status_code, status.HTTP_200_OK)
214214

@@ -258,7 +258,7 @@ def test_update_change_after_as_admin(self, mock_email, mock_cancel):
258258
'update',
259259
reason_message,
260260
)
261-
mock_cancel.assert_called_once_with(False)
261+
mock_cancel.assert_called_once_with(None)
262262

263263
self.assertEqual(response.status_code, status.HTTP_200_OK)
264264

@@ -308,7 +308,7 @@ def test_update_change_before_as_admin(self, mock_email, mock_cancel):
308308
'update',
309309
reason_message,
310310
)
311-
mock_cancel.assert_called_once_with(False)
311+
mock_cancel.assert_called_once_with(None)
312312

313313
self.assertEqual(response.status_code, status.HTTP_200_OK)
314314

@@ -378,7 +378,7 @@ def test_delete_no_change_as_admin(self, mock_email, mock_cancel):
378378
'update',
379379
reason_message,
380380
)
381-
mock_cancel.assert_called_once_with(False)
381+
mock_cancel.assert_called_once_with(None)
382382

383383
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
384384

@@ -426,7 +426,7 @@ def test_delete_change_after_as_admin(self, mock_email, mock_cancel):
426426
'update',
427427
reason_message,
428428
)
429-
mock_cancel.assert_called_once_with(False)
429+
mock_cancel.assert_called_once_with(None)
430430

431431
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
432432

@@ -474,7 +474,7 @@ def test_delete_change_before_as_admin(self, mock_email, mock_cancel):
474474
'update',
475475
reason_message,
476476
)
477-
mock_cancel.assert_called_once_with(False)
477+
mock_cancel.assert_called_once_with(None)
478478

479479
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
480480

retirement/views.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def get_queryset(self):
217217
def destroy(self, request, *args, **kwargs):
218218
instance = self.get_object()
219219
deletion_message = request.data.get('deletion_message', None)
220-
force_refund = request.data.get('force_refund', False)
220+
refund_policy = request.data.get('refund_policy', None)
221221
if instance.total_reservations > 0 and not deletion_message:
222222
error = {
223223
'deletion_message': _("There is at least one participant to "
@@ -226,7 +226,7 @@ def destroy(self, request, *args, **kwargs):
226226
}
227227
return Response(error, status=status.HTTP_400_BAD_REQUEST)
228228
if instance.is_active:
229-
instance.custom_delete(deletion_message, force_refund)
229+
instance.custom_delete(deletion_message, refund_policy)
230230
elif not instance.hide_from_client_admin_panel:
231231
instance.hide_from_client_admin_panel = True
232232
instance.save()
@@ -632,16 +632,16 @@ def destroy(self, request, *args, **kwargs):
632632

633633
instance = self.get_object()
634634
user = instance.user
635-
force_refund = False
635+
refund_policy = None
636636

637637
if self.request.user.is_staff:
638-
force_refund = request.data.get('force_refund', False)
638+
refund_policy = request.data.get('refund_policy', None)
639639
if self.request.user.id != user.id:
640640
cancel_reason = Reservation.CANCELATION_REASON_ADMIN_CANCELLED
641641
else:
642642
cancel_reason = Reservation.CANCELATION_REASON_USER_CANCELLED
643643

644-
refund_data = instance.process_refund(cancel_reason, force_refund)
644+
refund_data = instance.process_refund(cancel_reason, refund_policy)
645645
if refund_data:
646646
Reservation.send_refund_confirmation_email(refund_data)
647647

@@ -777,7 +777,7 @@ class RetreatDateViewSet(viewsets.ModelViewSet):
777777
def update(self, request, *args, **kwargs):
778778
retreat_date: RetreatDate = self.get_object()
779779
reason_message = request.data.get('reason_message', None)
780-
force_refund = request.data.get('force_refund', False)
780+
refund_policy = request.data.get('refund_policy', None)
781781
if retreat_date.retreat.total_reservations > 0 and \
782782
not reason_message:
783783
error = {
@@ -796,13 +796,13 @@ def update(self, request, *args, **kwargs):
796796
update(request, *args, **kwargs)
797797
retreat.set_automatic_email() # Recalculate retreat auto-email
798798
retreat.process_impacted_users(
799-
'update', reason_message, force_refund)
799+
'update', reason_message, refund_policy)
800800
return response
801801

802802
def destroy(self, request, *args, **kwargs):
803803
retreat_date: RetreatDate = self.get_object()
804804
reason_message = request.data.get('reason_message', None)
805-
force_refund = request.data.get('force_refund', False)
805+
refund_policy = request.data.get('refund_policy', None)
806806
if retreat_date.retreat.total_reservations > 0 and \
807807
not reason_message:
808808
error = {
@@ -827,7 +827,7 @@ def destroy(self, request, *args, **kwargs):
827827
request, *args, **kwargs)
828828
retreat.set_automatic_email() # Recalculate retreat auto-email
829829
retreat.process_impacted_users(
830-
'update', reason_message, force_refund)
830+
'update', reason_message, refund_policy)
831831
return Response(status=status.HTTP_204_NO_CONTENT)
832832

833833

store/tests/tests_viewset_Order.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4152,6 +4152,7 @@ def test_refund(self):
41524152
'retreat:reservation-detail',
41534153
kwargs={'pk': reservation.id},
41544154
),
4155+
data={"refund_policy": "retreat_rate"},
41554156
)
41564157

41574158
self.assertEqual(

workplace/admin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ class WorkplaceAdmin(SimpleHistoryAdmin, SafeDeleteAdmin, TranslationAdmin,
2424
list_display = (
2525
'name',
2626
'seats',
27+
'is_accessible',
2728
highlight_deleted,
2829
) + SafeDeleteAdmin.list_display
2930
list_filter = (
3031
'name',
3132
'seats',
33+
'is_accessible',
3234
) + SafeDeleteAdmin.list_filter
3335

3436
actions = ['undelete_selected', 'export_admin_action']
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 3.2.8 on 2023-08-03 13:56
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('workplace', '0025_auto_20230203_1134'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='historicalworkplace',
15+
name='geolocation_link',
16+
field=models.TextField(blank=True, null=True, verbose_name='Geolocation link'),
17+
),
18+
migrations.AddField(
19+
model_name='workplace',
20+
name='geolocation_link',
21+
field=models.TextField(blank=True, null=True, verbose_name='Geolocation link'),
22+
),
23+
]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 3.2.8 on 2023-08-17 13:23
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('workplace', '0026_auto_20230803_0956'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='historicalworkplace',
15+
name='is_accessible',
16+
field=models.BooleanField(default=True, verbose_name='Is accessible'),
17+
),
18+
migrations.AddField(
19+
model_name='workplace',
20+
name='is_accessible',
21+
field=models.BooleanField(default=True, verbose_name='Is accessible'),
22+
),
23+
]

workplace/models.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class Meta:
3030
max_length=1000,
3131
)
3232

33+
geolocation_link = models.TextField(
34+
verbose_name=_("Geolocation link"),
35+
null=True,
36+
blank=True,
37+
)
38+
3339
seats = models.IntegerField(
3440
verbose_name=_("Seats"),
3541
)
@@ -41,6 +47,11 @@ class Meta:
4147
related_name='workplaces',
4248
)
4349

50+
is_accessible = models.BooleanField(
51+
default=True,
52+
verbose_name=_("Is accessible"),
53+
)
54+
4455
# History is registered in translation.py
4556
# history = HistoricalRecords()
4657

0 commit comments

Comments
 (0)