diff options
Diffstat (limited to 'budget')
| -rw-r--r-- | budget/models.py | 9 | ||||
| -rw-r--r-- | budget/templates/list_bills.html | 6 | ||||
| -rw-r--r-- | budget/templates/settle_bills.html | 6 | ||||
| -rw-r--r-- | budget/tests.py | 40 | ||||
| -rw-r--r-- | budget/web.py | 4 |
5 files changed, 54 insertions, 11 deletions
diff --git a/budget/models.py b/budget/models.py index 852b3e1..3aac120 100644 --- a/budget/models.py +++ b/budget/models.py @@ -37,7 +37,7 @@ class Project(db.Model): # for each person for person in self.members: # get the list of bills he has to pay - bills = Bill.query.filter(Bill.owers.contains(person)) + bills = Bill.query.options(orm.subqueryload(Bill.owers)).filter(Bill.owers.contains(person)) for bill in bills.all(): if person != bill.payer: share = bill.pay_each() * person.weight @@ -61,9 +61,9 @@ class Project(db.Model): credits, debts, transactions = [],[],[] # Create lists of credits and debts for person in self.members: - if balance[person.id] > 0: + if round(balance[person.id], 2) > 0: credits.append({"person": person, "balance": balance[person.id]}) - elif balance[person.id] < 0: + elif round(balance[person.id], 2) < 0: debts.append({"person": person, "balance": -balance[person.id]}) # Try and find exact matches for credit in credits: @@ -111,7 +111,8 @@ class Project(db.Model): .filter(Bill.payer_id == Person.id)\ .filter(Person.project_id == Project.id)\ .filter(Project.id == self.id)\ - .order_by(Bill.date.desc()) + .order_by(Bill.date.desc())\ + .order_by(Bill.id.desc()) def remove_member(self, member_id): """Remove a member from the project. diff --git a/budget/templates/list_bills.html b/budget/templates/list_bills.html index f081334..72b9032 100644 --- a/budget/templates/list_bills.html +++ b/budget/templates/list_bills.html @@ -65,7 +65,7 @@ <div id="table_overflow"> <table class="balance table"> {% set balance = g.project.balance %} - {% for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id] != 0 %} + {% for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2) != 0 %} <tr id="bal-member-{{ member.id }}" action={% if member.activated %}delete{% else %}reactivate{% endif %}> <td class="balance-name">{{ member.name }} <span class="light{% if not g.project.uses_weights %} extra-info{% endif %}">(x{{ member.weight|minimal_round(1) }})</span> @@ -82,8 +82,8 @@ <form class="action reactivate" action="{{ url_for(".reactivate", member_id=member.id) }}" method="POST"> <button type="submit">{{ _("reactivate") }}</button></form></td> {% endif %} - <td class="balance-value {% if balance[member.id] > 0 %}positive{% elif balance[member.id] < 0 %}negative{% endif %}"> - {% if balance[member.id] > 0 %}+{% endif %}{{ "%.2f" | format(balance[member.id]) }} + <td class="balance-value {% if balance[member.id]|round(2) > 0 %}positive{% elif balance[member.id]|round(2) < 0 %}negative{% endif %}"> + {% if balance[member.id]|round(2) > 0 %}+{% endif %}{{ "%.2f" | format(balance[member.id]) }} </td> </tr> {% endfor %} diff --git a/budget/templates/settle_bills.html b/budget/templates/settle_bills.html index 4066b16..16c60b3 100644 --- a/budget/templates/settle_bills.html +++ b/budget/templates/settle_bills.html @@ -11,11 +11,11 @@ <div id="table_overflow"> <table class="balance table"> {% set balance = g.project.balance %} - {% for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id] != 0 %} + {% for member in g.project.members | sort(attribute='name') if member.activated or balance[member.id]|round(2) != 0 %} <tr id="bal-member-{{ member.id }}" action={% if member.activated %}delete{% else %}reactivate{% endif %}> <td class="balance-name">{{ member.name }}</td> - <td class="balance-value {% if balance[member.id] > 0 %}positive{% elif balance[member.id] < 0 %}negative{% endif %}"> - {% if balance[member.id] > 0 %}+{% endif %}{{ "%.2f" | format(balance[member.id]) }} + <td class="balance-value {% if balance[member.id]|round(2) > 0 %}positive{% elif balance[member.id]|round(2) < 0 %}negative{% endif %}"> + {% if balance[member.id]|round(2) > 0 %}+{% endif %}{{ "%.2f" | format(balance[member.id]) }} </td> </tr> {% endfor %} diff --git a/budget/tests.py b/budget/tests.py index 8c1f973..0e40825 100644 --- a/budget/tests.py +++ b/budget/tests.py @@ -580,6 +580,46 @@ class BudgetTestCase(TestCase): self.assertEqual(a, balance[m.id]) return + def test_settle_zero(self): + self.post_project("raclette") + + # add members + self.app.post("/raclette/members/add", data={'name': 'alexis'}) + self.app.post("/raclette/members/add", data={'name': 'fred'}) + self.app.post("/raclette/members/add", data={'name': 'tata'}) + + # create bills + self.app.post("/raclette/add", data={ + 'date': '2016-12-31', + 'what': u'fromage à raclette', + 'payer': 1, + 'payed_for': [1, 2, 3], + 'amount': '10.0', + }) + + self.app.post("/raclette/add", data={ + 'date': '2016-12-31', + 'what': u'red wine', + 'payer': 2, + 'payed_for': [1, 3], + 'amount': '20', + }) + + self.app.post("/raclette/add", data={ + 'date': '2017-01-01', + 'what': u'refund', + 'payer': 3, + 'payed_for': [2], + 'amount': '13.33', + }) + project = models.Project.query.get('raclette') + transactions = project.get_transactions_to_settle_bill() + members = defaultdict(int) + # There should not be any zero-amount transfer after rounding + for t in transactions: + rounded_amount = round(t['amount'], 2) + self.assertNotEqual(0.0, rounded_amount, + msg='%f is equal to zero after rounding' % t['amount']) class APITestCase(TestCase): diff --git a/budget/web.py b/budget/web.py index 63fbe4d..87aef26 100644 --- a/budget/web.py +++ b/budget/web.py @@ -15,6 +15,7 @@ from flask.ext.mail import Mail, Message from flask.ext.babel import get_locale, gettext as _ from smtplib import SMTPRecipientsRefused import werkzeug +from sqlalchemy import orm # local modules from models import db, Project, Person, Bill @@ -277,7 +278,8 @@ def list_bills(): # set the last selected payer as default choice if exists if 'last_selected_payer' in session: bill_form.payer.data = session['last_selected_payer'] - bills = g.project.get_bills() + # Preload the "owers" relationship for all bills + bills = g.project.get_bills().options(orm.subqueryload(Bill.owers)) return render_template("list_bills.html", bills=bills, member_form=MemberForm(g.project), |
