diff --git a/ihatemoney/models.py b/ihatemoney/models.py index c591b85b6..af11da28d 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_member_id_list = owers_id + [self.payer_id] + deactivated_member_number = ( + Person.query.filter(Person.id.in_(bill_member_id_list)) + .filter(Person.activated.is_(False)) + .count() + ) + return deactivated_member_number != 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 093d9d17d..e0e56afd4 100644 --- a/ihatemoney/tests/budget_test.py +++ b/ihatemoney/tests/budget_test.py @@ -870,6 +870,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 0d0bdd201..d3706e408 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -800,6 +800,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")) @@ -814,6 +818,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():