diff --git a/ihatemoney/tests/budget_test.py b/ihatemoney/tests/budget_test.py index 093d9d17..e0e56afd 100644 --- a/ihatemoney/tests/budget_test.py +++ b/ihatemoney/tests/budget_test.py @@ -870,6 +870,122 @@ class TestBudget(IhatemoneyTestCase): 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 0d0bdd20..a3f44389 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -800,6 +800,19 @@ def delete_bill(bill_id): if not bill: return redirect(url_for(".list_bills")) + # Prevent deleting if the bill involves deactivated user. + active_member_id = [m.id for m in g.project.active_members] + owers_id = [int(m.id) for m in bill.owers] + deactivated_members = set([bill.payer_id] + owers_id).difference( + set(active_member_id) + ) + if len(deactivated_members): + flash( + _("Deactivated users involved. This bill cannot be deleted."), + category="warning", + ) + return redirect(url_for(".list_bills")) + db.session.delete(bill) db.session.commit() flash(_("The bill has been deleted")) @@ -816,6 +829,19 @@ def edit_bill(bill_id): form = get_billform_for(g.project, set_default=False) + # Prevent editing if the bill involves deactivated user. + active_member_id = [m.id for m in g.project.active_members] + owers_id = [int(m.id) for m in bill.owers] + deactivated_members = set([bill.payer_id] + owers_id).difference( + set(active_member_id) + ) + if len(deactivated_members): + flash( + _("Deactivated users involved. This bill cannot be edited."), + category="warning", + ) + return redirect(url_for(".list_bills")) + if request.method == "POST" and form.validate(): form.save(bill, g.project) db.session.commit()