Skip to content

Commit

Permalink
Merge pull request #2030 from FJNR-inc/develop
Browse files Browse the repository at this point in the history
New release
  • Loading branch information
RignonNoel authored Aug 21, 2023
2 parents ddd6e7b + f6eb480 commit c749a58
Show file tree
Hide file tree
Showing 15 changed files with 236 additions and 55 deletions.
54 changes: 34 additions & 20 deletions retirement/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,7 @@ def get_participants(self):
participants.add(reservation.user)
return list(participants)

def process_impacted_users(self, reason, reason_message, force_refund):
def process_impacted_users(self, reason, reason_message, refund_policy):
"""
Notify and potentially refund user for a reason happening on retreat:
Retreat cancelled, deleted ...
Expand All @@ -1010,19 +1010,18 @@ def process_impacted_users(self, reason, reason_message, force_refund):
# retrieve active users before we cancel the reservation
users = self.get_participants()
from .services import send_updated_retreat_email
self.cancel_participants_reservation(force_refund)
self.cancel_participants_reservation(refund_policy)
send_updated_retreat_email(
self,
users,
reason,
reason_message,
)

def cancel_participants_reservation(self, force_refund):
def cancel_participants_reservation(self, refund_policy):
"""
Cancel all participants' reservation
:params force_refund: True if we want to force refund for participants,
otherwise regular refund will be done.
:params refund_policy: Admin policy to apply to refund participants
"""
active_reservations = self.reservations.filter(is_active=True)
refund_data = []
Expand All @@ -1033,7 +1032,7 @@ def cancel_participants_reservation(self, force_refund):
refund_data.append(
reservation.process_refund(
Reservation.CANCELATION_REASON_RETREAT_DELETED,
force_refund)
refund_policy)
)

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

def custom_delete(self, deletion_message=None, force_refund=False):
def custom_delete(self, deletion_message=None, refund_policy=None):
"""
Deleting a retreat sends an email to all registered participants
set the retreat to inactive and hide it from the admin panel.
Expand All @@ -1051,7 +1050,8 @@ def custom_delete(self, deletion_message=None, force_refund=False):
"""
self.is_active = False
self.hide_from_client_admin_panel = True
self.process_impacted_users('deletion', deletion_message, force_refund)
self.process_impacted_users(
'deletion', deletion_message, refund_policy)
self.save()


Expand Down Expand Up @@ -1257,16 +1257,22 @@ class Reservation(SafeDeleteModel):
def __str__(self):
return str(self.user)

def get_refund_value(self, total_refund=False):
if self.order_line is None or self.order_line.is_made_by_admin:
def get_refund_value(self, refund_policy=None):
"""
Refund the user. Admin can specify a refund_policy to fully refund
user, or not refund at all or refund at retreat rate.
"""
if self.order_line is None or \
self.order_line.is_made_by_admin or \
refund_policy == 'no_refund':
return 0

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

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

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

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

def make_refund(self, refund_reason, total_refund=False):
amount_to_refund = self.get_refund_value(total_refund)
def make_refund(self, refund_reason, refund_policy=None):
amount_to_refund = self.get_refund_value(refund_policy)

# paysafe use value without cent
amount_to_refund_paysafe = int(round(amount_to_refund * 100))
Expand Down Expand Up @@ -1314,10 +1320,10 @@ def send_refund_confirmation_email(email_dict):
# Here the price takes the applied coupon into account, if
# applicable.
old_retreat = {
'price': (amount * retreat.refund_rate) / 100,
'price': amount,
'name': "{0}: {1}".format(
_("Retreat"),
retreat.name
retreat.name,
)
}

Expand Down Expand Up @@ -1362,7 +1368,7 @@ def send_refund_confirmation_email(email_dict):
)
raise

def process_refund(self, cancel_reason, force_refund):
def process_refund(self, cancel_reason, refund_policy):
"""
User will be refund the retreat's "refund_rate" if we're at least
"min_day_refund" days before the event.
Expand Down Expand Up @@ -1398,12 +1404,17 @@ def process_refund(self, cancel_reason, force_refund):
# refundable
#
# 2 - An admin want to force a refund and the user paid for
# his reservation
# his reservation or force no refund
#
# In all case, only paid reservation (amount > 0) can be refunded
if self.get_refund_value() > 0:
process_refund = (respects_minimum_days and refundable) or \
force_refund
if refund_policy:
if refund_policy in ['retreat_rate', 'full_rate']:
process_refund = True
else:
process_refund = False
else:
process_refund = respects_minimum_days and refundable
else:
process_refund = False

Expand All @@ -1422,7 +1433,9 @@ def process_refund(self, cancel_reason, force_refund):
if process_refund:
try:
refund = self.make_refund(
self.REFUND_REASON[cancel_reason])
self.REFUND_REASON[cancel_reason],
refund_policy
)
except PaymentAPIError as err:
if str(err) == PAYSAFE_EXCEPTION['3406']:
raise rest_framework_serializers.ValidationError({
Expand Down Expand Up @@ -1486,6 +1499,7 @@ def process_refund(self, cancel_reason, force_refund):
'user': user,
'total_amount': refund.amount,
'amount_tax': round(refund.amount * TAX_RATE, 2),
'refund_policy': refund_policy,
}
return email_data

Expand Down
1 change: 1 addition & 0 deletions retirement/tests/tests_viewset_Reservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,7 @@ def test_delete_scheduler_working(self):
'retreat:reservation-detail',
kwargs={'pk': self.reservation_admin.id},
),
data={"refund_policy": "retreat_rate"},
)

self.assertEqual(
Expand Down
2 changes: 1 addition & 1 deletion retirement/tests/tests_viewset_Retreat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@ def test_delete_with_participants(self, mock_email, mock_cancel):
'deletion',
deletion_message
)
mock_cancel.assert_called_once_with(False)
mock_cancel.assert_called_once_with(None)

self.retreat.refresh_from_db()
self.assertFalse(self.retreat.is_active)
Expand Down
12 changes: 6 additions & 6 deletions retirement/tests/tests_viewset_RetreatDate.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def test_update_no_change_as_admin(self, mock_email, mock_cancel):
'update',
reason_message,
)
mock_cancel.assert_called_once_with(False)
mock_cancel.assert_called_once_with(None)

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

Expand Down Expand Up @@ -258,7 +258,7 @@ def test_update_change_after_as_admin(self, mock_email, mock_cancel):
'update',
reason_message,
)
mock_cancel.assert_called_once_with(False)
mock_cancel.assert_called_once_with(None)

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

Expand Down Expand Up @@ -308,7 +308,7 @@ def test_update_change_before_as_admin(self, mock_email, mock_cancel):
'update',
reason_message,
)
mock_cancel.assert_called_once_with(False)
mock_cancel.assert_called_once_with(None)

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

Expand Down Expand Up @@ -378,7 +378,7 @@ def test_delete_no_change_as_admin(self, mock_email, mock_cancel):
'update',
reason_message,
)
mock_cancel.assert_called_once_with(False)
mock_cancel.assert_called_once_with(None)

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

Expand Down Expand Up @@ -426,7 +426,7 @@ def test_delete_change_after_as_admin(self, mock_email, mock_cancel):
'update',
reason_message,
)
mock_cancel.assert_called_once_with(False)
mock_cancel.assert_called_once_with(None)

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

Expand Down Expand Up @@ -474,7 +474,7 @@ def test_delete_change_before_as_admin(self, mock_email, mock_cancel):
'update',
reason_message,
)
mock_cancel.assert_called_once_with(False)
mock_cancel.assert_called_once_with(None)

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

Expand Down
18 changes: 9 additions & 9 deletions retirement/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def get_queryset(self):
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
deletion_message = request.data.get('deletion_message', None)
force_refund = request.data.get('force_refund', False)
refund_policy = request.data.get('refund_policy', None)
if instance.total_reservations > 0 and not deletion_message:
error = {
'deletion_message': _("There is at least one participant to "
Expand All @@ -226,7 +226,7 @@ def destroy(self, request, *args, **kwargs):
}
return Response(error, status=status.HTTP_400_BAD_REQUEST)
if instance.is_active:
instance.custom_delete(deletion_message, force_refund)
instance.custom_delete(deletion_message, refund_policy)
elif not instance.hide_from_client_admin_panel:
instance.hide_from_client_admin_panel = True
instance.save()
Expand Down Expand Up @@ -632,16 +632,16 @@ def destroy(self, request, *args, **kwargs):

instance = self.get_object()
user = instance.user
force_refund = False
refund_policy = None

if self.request.user.is_staff:
force_refund = request.data.get('force_refund', False)
refund_policy = request.data.get('refund_policy', None)
if self.request.user.id != user.id:
cancel_reason = Reservation.CANCELATION_REASON_ADMIN_CANCELLED
else:
cancel_reason = Reservation.CANCELATION_REASON_USER_CANCELLED

refund_data = instance.process_refund(cancel_reason, force_refund)
refund_data = instance.process_refund(cancel_reason, refund_policy)
if refund_data:
Reservation.send_refund_confirmation_email(refund_data)

Expand Down Expand Up @@ -777,7 +777,7 @@ class RetreatDateViewSet(viewsets.ModelViewSet):
def update(self, request, *args, **kwargs):
retreat_date: RetreatDate = self.get_object()
reason_message = request.data.get('reason_message', None)
force_refund = request.data.get('force_refund', False)
refund_policy = request.data.get('refund_policy', None)
if retreat_date.retreat.total_reservations > 0 and \
not reason_message:
error = {
Expand All @@ -796,13 +796,13 @@ def update(self, request, *args, **kwargs):
update(request, *args, **kwargs)
retreat.set_automatic_email() # Recalculate retreat auto-email
retreat.process_impacted_users(
'update', reason_message, force_refund)
'update', reason_message, refund_policy)
return response

def destroy(self, request, *args, **kwargs):
retreat_date: RetreatDate = self.get_object()
reason_message = request.data.get('reason_message', None)
force_refund = request.data.get('force_refund', False)
refund_policy = request.data.get('refund_policy', None)
if retreat_date.retreat.total_reservations > 0 and \
not reason_message:
error = {
Expand All @@ -827,7 +827,7 @@ def destroy(self, request, *args, **kwargs):
request, *args, **kwargs)
retreat.set_automatic_email() # Recalculate retreat auto-email
retreat.process_impacted_users(
'update', reason_message, force_refund)
'update', reason_message, refund_policy)
return Response(status=status.HTTP_204_NO_CONTENT)


Expand Down
1 change: 1 addition & 0 deletions store/tests/tests_viewset_Order.py
Original file line number Diff line number Diff line change
Expand Up @@ -4152,6 +4152,7 @@ def test_refund(self):
'retreat:reservation-detail',
kwargs={'pk': reservation.id},
),
data={"refund_policy": "retreat_rate"},
)

self.assertEqual(
Expand Down
2 changes: 2 additions & 0 deletions workplace/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ class WorkplaceAdmin(SimpleHistoryAdmin, SafeDeleteAdmin, TranslationAdmin,
list_display = (
'name',
'seats',
'is_accessible',
highlight_deleted,
) + SafeDeleteAdmin.list_display
list_filter = (
'name',
'seats',
'is_accessible',
) + SafeDeleteAdmin.list_filter

actions = ['undelete_selected', 'export_admin_action']
Expand Down
23 changes: 23 additions & 0 deletions workplace/migrations/0026_auto_20230803_0956.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.8 on 2023-08-03 13:56

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('workplace', '0025_auto_20230203_1134'),
]

operations = [
migrations.AddField(
model_name='historicalworkplace',
name='geolocation_link',
field=models.TextField(blank=True, null=True, verbose_name='Geolocation link'),
),
migrations.AddField(
model_name='workplace',
name='geolocation_link',
field=models.TextField(blank=True, null=True, verbose_name='Geolocation link'),
),
]
23 changes: 23 additions & 0 deletions workplace/migrations/0027_auto_20230817_0923.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.8 on 2023-08-17 13:23

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('workplace', '0026_auto_20230803_0956'),
]

operations = [
migrations.AddField(
model_name='historicalworkplace',
name='is_accessible',
field=models.BooleanField(default=True, verbose_name='Is accessible'),
),
migrations.AddField(
model_name='workplace',
name='is_accessible',
field=models.BooleanField(default=True, verbose_name='Is accessible'),
),
]
11 changes: 11 additions & 0 deletions workplace/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ class Meta:
max_length=1000,
)

geolocation_link = models.TextField(
verbose_name=_("Geolocation link"),
null=True,
blank=True,
)

seats = models.IntegerField(
verbose_name=_("Seats"),
)
Expand All @@ -41,6 +47,11 @@ class Meta:
related_name='workplaces',
)

is_accessible = models.BooleanField(
default=True,
verbose_name=_("Is accessible"),
)

# History is registered in translation.py
# history = HistoricalRecords()

Expand Down
Loading

0 comments on commit c749a58

Please sign in to comment.