Compare commits

...

5 commits

Author SHA1 Message Date
Yida Chen
a68cebbd3c
Merge 1621a45336 into 05742347c3 2025-01-05 15:49:25 +01:00
singeltary
05742347c3
UI/navigation changes to in "Edit this participant" (#1323)
Some checks are pending
CI / lint (push) Waiting to run
CI / test (mariadb, minimal, 3.11) (push) Blocked by required conditions
CI / test (mariadb, normal, 3.11) (push) Blocked by required conditions
CI / test (mariadb, normal, 3.9) (push) Blocked by required conditions
CI / test (postgresql, minimal, 3.11) (push) Blocked by required conditions
CI / test (postgresql, normal, 3.11) (push) Blocked by required conditions
CI / test (postgresql, normal, 3.9) (push) Blocked by required conditions
CI / test (sqlite, minimal, 3.10) (push) Blocked by required conditions
CI / test (sqlite, minimal, 3.11) (push) Blocked by required conditions
CI / test (sqlite, minimal, 3.12) (push) Blocked by required conditions
CI / test (sqlite, minimal, 3.9) (push) Blocked by required conditions
CI / test (sqlite, normal, 3.10) (push) Blocked by required conditions
CI / test (sqlite, normal, 3.11) (push) Blocked by required conditions
CI / test (sqlite, normal, 3.12) (push) Blocked by required conditions
CI / test (sqlite, normal, 3.9) (push) Blocked by required conditions
CI / docs (push) Waiting to run
Docker build / test (push) Waiting to run
Docker build / build_upload (push) Blocked by required conditions
* Update forms.html

Added navigation element in 'Edit Participants' window

* Update forms.html

Updated button on form for editing participants--"Save" for edits instead of "Add."
2025-01-05 15:49:20 +01:00
chestnutFan
1621a45336
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.
2025-01-05 15:43:37 +01:00
bd689f931a
Merge branch 'fix-1336' 2025-01-05 13:01:16 +01:00
67938eabbc
Do not display deactivated users when their balance is really small
Cases has been reported of rounding issues making deactivated users
reapparing. This is due to the fact we're using floats (see #528 for
details)

Fixes #1336
2025-01-05 13:00:54 +01:00
6 changed files with 158 additions and 5 deletions

View file

@ -752,6 +752,22 @@ class Bill(db.Model):
else: else:
return 0 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): def __str__(self):
return self.what return self.what

View file

@ -236,7 +236,8 @@
{{ input(form.weight) }} {{ input(form.weight) }}
</fieldset> </fieldset>
<div class="actions"> <div class="actions">
{{ form.submit(class="btn btn-primary") }} <button class="btn btn-secondary input-group-addon" type="submit">{{ _("Save") }}</button>
<a href="{{ url_for(".list_bills") }}" class="btn btn-outline-secondary"> {{_("Cancel") }} </a>
</div> </div>
{% endmacro %} {% endmacro %}

View file

@ -148,10 +148,22 @@
</span> </span>
</td> </td>
<td class="bill-actions d-flex align-items-center"> <td class="bill-actions d-flex align-items-center">
<a class="edit" href="{{ url_for(".edit_bill", bill_id=bill.id) }}" title="{{ _("edit") }}">{{ _('edit') }}</a> <a class="edit" href="{{ url_for(".edit_bill", bill_id=bill.id) }}" data-toggle="tooltip"
{% if bill.involves_deactivated_members %}
title="Cannot be edited as deactivated members involved"
{% else %}
title="Click to edit this bill"
{% endif %}
>{{ _('edit') }}</a>
<form class="delete-bill" action="{{ url_for(".delete_bill", bill_id=bill.id) }}" method="POST"> <form class="delete-bill" action="{{ url_for(".delete_bill", bill_id=bill.id) }}" method="POST">
{{ csrf_form.csrf_token }} {{ csrf_form.csrf_token }}
<button class="action delete" type="submit" title="{{ _("delete") }}"></button> <button class="action delete" type="submit" data-toggle="tooltip"
{% if bill.involves_deactivated_members %}
title="Cannot be deleted as deactivated members involved"
{% else %}
title="Click to delete this bill"
{% endif %}
></button>
</form> </form>
{% if bill.external_link %} {% if bill.external_link %}
<a class="show" href="{{ bill.external_link }}" ref="noopener" target="_blank" title="{{ _("show") }}">{{ _('show') }} </a> <a class="show" href="{{ bill.external_link }}" ref="noopener" target="_blank" title="{{ _("show") }}">{{ _('show') }} </a>

View file

@ -11,7 +11,7 @@
</tr> </tr>
</thead> </thead>
{%- endif %} {%- endif %}
{%- for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2) != 0 %} {%- for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2)|abs > 0.01 %}
<tr id="bal-member-{{ member.id }}" action="{% if member.activated %}delete{% else %}reactivate{% endif %}"> <tr id="bal-member-{{ member.id }}" action="{% if member.activated %}delete{% else %}reactivate{% endif %}">
<td class="balance-name">{{ member.name }} <td class="balance-name">{{ member.name }}
{%- if show_weight -%} {%- if show_weight -%}

View file

@ -872,6 +872,122 @@ class TestBudget(IhatemoneyTestCase):
balance = self.get_project("raclette").balance balance = self.get_project("raclette").balance
assert set(balance.values()) == set([6, -6]) 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): def test_trimmed_members(self):
self.post_project("raclette") self.post_project("raclette")

View file

@ -806,6 +806,10 @@ def delete_bill(bill_id):
if not bill: if not bill:
return redirect(url_for(".list_bills")) 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.delete(bill)
db.session.commit() db.session.commit()
flash(_("The bill has been deleted")) flash(_("The bill has been deleted"))
@ -820,6 +824,10 @@ def edit_bill(bill_id):
if not bill: if not bill:
raise NotFound() 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) form = get_billform_for(g.project, set_default=False)
if request.method == "POST" and form.validate(): if request.method == "POST" and form.validate():