From 1621a453364b470af917df41813503a58b57e5b5 Mon Sep 17 00:00:00 2001 From: chestnutFan Date: Sat, 10 Dec 2022 16:57:17 -0500 Subject: [PATCH] Do not allow the edition of bills involving deactivated users This is also true for deletion. Once a user is deactivated, all the related bills shouldn't be able to move. --- ihatemoney/models.py | 16 ++++ ihatemoney/templates/list_bills.html | 16 +++- ihatemoney/tests/budget_test.py | 116 +++++++++++++++++++++++++++ ihatemoney/web.py | 8 ++ 4 files changed, 154 insertions(+), 2 deletions(-) diff --git a/ihatemoney/models.py b/ihatemoney/models.py index c591b85b6..f8f67e961 100644 --- a/ihatemoney/models.py +++ b/ihatemoney/models.py @@ -752,6 +752,22 @@ def pay_each_default(self, amount): else: return 0 + @property + def involves_deactivated_members(self): + """Check whether the bill contains deactivated member. + Return: + True if it contains deactivated member, + False if not. + """ + owers_id = [int(m.id) for m in self.owers] + bill_members = owers_id + [self.payer_id] + deactivated_members_count = ( + Person.query.filter(Person.id.in_(bill_members)) + .filter(Person.activated.is_(False)) + .count() + ) + return deactivated_member_count != 0 + def __str__(self): return self.what diff --git a/ihatemoney/templates/list_bills.html b/ihatemoney/templates/list_bills.html index 79e252625..7f4350e81 100644 --- a/ihatemoney/templates/list_bills.html +++ b/ihatemoney/templates/list_bills.html @@ -148,10 +148,22 @@ - {{ _('edit') }} + {{ _('edit') }}
{{ csrf_form.csrf_token }} - +
{% if bill.external_link %} {{ _('show') }} diff --git a/ihatemoney/tests/budget_test.py b/ihatemoney/tests/budget_test.py index a3fc813f8..81f2e850a 100644 --- a/ihatemoney/tests/budget_test.py +++ b/ihatemoney/tests/budget_test.py @@ -872,6 +872,122 @@ def test_weighted_balance(self): balance = self.get_project("raclette").balance assert set(balance.values()) == set([6, -6]) + def test_edit_bill_with_deactivated_member(self): + """ + Bills involving deactivated members should not allowed to be edited or deleted. + """ + self.post_project("raclette") + + # add two participants + self.client.post("/raclette/members/add", data={"name": "zorglub"}) + self.client.post("/raclette/members/add", data={"name": "fred"}) + + members_ids = [m.id for m in self.get_project("raclette").members] + + # create one bill + self.client.post( + "/raclette/add", + data={ + "date": "2011-08-10", + "what": "fromage à raclette", + "payer": members_ids[0], + "payed_for": members_ids, + "amount": "25", + }, + ) + bill = models.Bill.query.one() + self.assertEqual(bill.amount, 25) + + # deactivate one user + self.client.post( + "/raclette/members/%s/delete" % self.get_project("raclette").members[-1].id + ) + self.assertEqual(len(self.get_project("raclette").members), 2) + self.assertEqual(len(self.get_project("raclette").active_members), 1) + + # editing would fail because the bill involves deactivated user + self.client.post( + f"/raclette/edit/{bill.id}", + data={ + "date": "2011-08-10", + "what": "fromage à raclette", + "payer": members_ids[0], + "payed_for": members_ids, + "amount": "10", + }, + ) + bill = models.Bill.query.one() + self.assertNotEqual(bill.amount, 10, "bill edition") + + # reactivate the user + self.client.post( + "/raclette/members/%s/reactivate" + % self.get_project("raclette").members[-1].id + ) + self.assertEqual(len(self.get_project("raclette").active_members), 2) + + # try to edit the bill again. It should succeed + self.client.post( + f"/raclette/edit/{bill.id}", + data={ + "date": "2011-08-10", + "what": "fromage à raclette", + "payer": members_ids[0], + "payed_for": members_ids, + "amount": "10", + }, + ) + bill = models.Bill.query.one() + self.assertEqual(bill.amount, 10, "bill edition") + + def test_delete_bill_with_deactivated_member(self): + """ + Bills involving deactivated members should not allowed to be edited or deleted. + """ + self.post_project("raclette") + + # add two participants + self.client.post("/raclette/members/add", data={"name": "zorglub"}) + self.client.post("/raclette/members/add", data={"name": "fred"}) + + members_ids = [m.id for m in self.get_project("raclette").members] + + # create one bill + self.client.post( + "/raclette/add", + data={ + "date": "2011-08-10", + "what": "fromage à raclette", + "payer": members_ids[0], + "payed_for": members_ids, + "amount": "25", + }, + ) + bill = models.Bill.query.one() + self.assertEqual(bill.amount, 25) + + # deactivate one user + self.client.post( + "/raclette/members/%s/delete" % self.get_project("raclette").members[-1].id + ) + self.assertEqual(len(self.get_project("raclette").active_members), 1) + + # deleting should fail because the bill involves deactivated user + response = self.client.get(f"/raclette/delete/{bill.id}") + self.assertEqual(response.status_code, 405) + self.assertEqual(1, len(models.Bill.query.all()), "bill deletion") + + # reactivate the user + self.client.post( + "/raclette/members/%s/reactivate" + % self.get_project("raclette").members[-1].id + ) + self.assertEqual(len(self.get_project("raclette").active_members), 2) + + # try to delete the bill again. It should succeed + self.client.post(f"/raclette/delete/{bill.id}") + self.assertEqual(0, len(models.Bill.query.all()), "bill deletion") + def test_trimmed_members(self): self.post_project("raclette") diff --git a/ihatemoney/web.py b/ihatemoney/web.py index 43b04c213..7653018e3 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -806,6 +806,10 @@ def delete_bill(bill_id): if not bill: return redirect(url_for(".list_bills")) + # Check if the bill contains deactivated member. If yes, stop deleting. + if bill.involves_deactivated_members: + return redirect(url_for(".list_bills")) + db.session.delete(bill) db.session.commit() flash(_("The bill has been deleted")) @@ -820,6 +824,10 @@ def edit_bill(bill_id): if not bill: raise NotFound() + # Check if the bill contains deactivated member. If yes, stop editing. + if bill.involves_deactivated_members: + return redirect(url_for(".list_bills")) + form = get_billform_for(g.project, set_default=False) if request.method == "POST" and form.validate():