aboutsummaryrefslogtreecommitdiff
path: root/budget
diff options
context:
space:
mode:
Diffstat (limited to 'budget')
-rw-r--r--budget/models.py49
-rw-r--r--budget/templates/layout.html1
-rw-r--r--budget/templates/list_bills.html12
-rw-r--r--budget/templates/settle_bill.html35
-rw-r--r--budget/web.py7
5 files changed, 94 insertions, 10 deletions
diff --git a/budget/models.py b/budget/models.py
index cac9664..1210e7c 100644
--- a/budget/models.py
+++ b/budget/models.py
@@ -45,10 +45,57 @@ class Project(db.Model):
for person in self.members:
balance = should_receive[person] - should_pay[person]
- balances[person.id] = round(balance, 2)
+ balances[person] = round(balance, 2)
return balances
+ def settle_bill(self):
+ """Return a list of transactions that could be made to settle the bill"""
+ balances = self.balance
+ credits, debts = list(), list()
+ transactions = list()
+ # Create lists of credits and debts
+ for person in balances.keys():
+ if balances[person] > 0:
+ credits.append({"person": person, "balance": balances[person]})
+ elif balances[person] < 0:
+ debts.append({"person": person, "balance": -balances[person]})
+ # Try and find exact matches
+ for credit in credits:
+ match = self.exactmatch(credit["balance"], debts)
+ if match:
+ for m in match:
+ transactions.append({"ower": m["person"], "payer": credit["person"], "amount": m["balance"]})
+ debts.remove(m)
+ credits.remove(credit)
+ # Split any remaining debts & credits
+ while credits and debts:
+ if credits[0]["balance"] > debts[0]["balance"]:
+ transactions.append({"ower": debts[0]["person"], "payer": credits[0]["person"], "amount": debts[0]["balance"]})
+ credits[0]["balance"] = credits[0]["balance"] - debts[0]["balance"]
+ del debts[0]
+ else:
+ transactions.append({"ower": debts[0]["person"], "payer": credits[0]["person"], "amount": credits[0]["balance"]})
+ debts[0]["balance"] = debts[0]["balance"] - credits[0]["balance"]
+ del credits[0]
+ return transactions
+
+ def exactmatch(self, credit, debts):
+ """Recursively try and find subsets of 'debts' whose sum is equal to credit"""
+ if not debts:
+ return []
+ if debts[0]["balance"] > credit:
+ return self.exactmatch(credit, debts[1:])
+ elif debts[0]["balance"] == credit:
+ return [debts[0]]
+ else:
+ match = self.exactmatch(credit-debts[0]["balance"], debts[1:])
+ if match:
+ match.append(debts[0])
+ else:
+ match = self.exactmatch(credit, debts[1:])
+ return match
+
def has_bills(self):
"""return if the project do have bills or not"""
return self.get_bills().count() > 0
diff --git a/budget/templates/layout.html b/budget/templates/layout.html
index 8e6d3b2..27f5b5b 100644
--- a/budget/templates/layout.html
+++ b/budget/templates/layout.html
@@ -42,6 +42,7 @@
{% if g.project %}
<ul class="nav primary-nav">
<li class="active"><a href="{{ url_for(".list_bills") }}">{{ _("Bills") }}</a></li>
+ <li><a href="{{ url_for(".settle_bill") }}">{{ _("Settle") }}</a></li>
</ul>
{% endif %}
<ul class="nav pull-right secondary-nav">
diff --git a/budget/templates/list_bills.html b/budget/templates/list_bills.html
index 389e73f..a39a78a 100644
--- a/budget/templates/list_bills.html
+++ b/budget/templates/list_bills.html
@@ -61,20 +61,20 @@
<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 %}
- <tr id="bal-member-{{ member.id }}" action={% if member.activated %}delete{% else %}reactivate{% endif %}>
+ {% for member in g.project.members | sort(attribute='name') if member.activated or balance[member] != 0 %}
+ <tr id="bal-member-{{ member }}" action={% if member.activated %}delete{% else %}reactivate{% endif %}>
<td class="balance-name">{{ member.name }}</td>
{% if member.activated %}
<td>
- <form class="action delete" action="{{ url_for(".remove_member", member_id=member.id) }}" method="POST">
+ <form class="action delete" action="{{ url_for(".remove_member", member_id=member) }}" method="POST">
<button type="submit">{{ _("delete") }}</button></form></td>
{% else %}
<td>
- <form class="action reactivate" action="{{ url_for(".reactivate", member_id=member.id) }}" method="POST">
+ <form class="action reactivate" action="{{ url_for(".reactivate", member_id=member) }}" 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 %}{{ balance[member.id] }}
+ <td class="balance-value {% if balance[member] > 0 %}positive{% elif balance[member] < 0 %}negative{% endif %}">
+ {% if balance[member] > 0 %}+{% endif %}{{ balance[member] }}
</td>
</tr>
{% endfor %}
diff --git a/budget/templates/settle_bill.html b/budget/templates/settle_bill.html
new file mode 100644
index 0000000..c91b03d
--- /dev/null
+++ b/budget/templates/settle_bill.html
@@ -0,0 +1,35 @@
+{% extends "layout.html" %}
+
+{% block head %}
+ <script src="{{ url_for("static", filename="js/jquery-ui.js") }}"></script>
+ {% if g.lang != "en" %}
+ <script src="{{ url_for("static", filename="js/i18n/jquery.ui.datepicker-%s.js" % g.lang ) }}"></script>
+ {% endif %}
+{% endblock %}
+{% block js %}
+ $('#cancel-form').click(function(){location.href={{ url_for(".list_bills") }};});
+ $.datepicker.setDefaults({'dateFormat': 'yy-mm-dd'});
+ $(".datepicker").datepicker($.datepicker.regional['{{ g.lang }}']);
+
+{% endblock %}
+{% block navbar %}
+ <li><a href="{{ url_for(".list_bills") }}">{{ _("Bills") }}</a></li>
+ <li class="active"><a href="{{ url_for(".settle_bill") }}">{{ _("Settle") }}</a></li>
+{% endblock %}
+
+
+{% block content %}
+ <table id="bill_table" class="split_bills common-table zebra-striped">
+ <thead><tr><th>{{ _("Who pays?") }}</th><th>{{ _("To whom?") }}</th><th>{{ _("How much?") }}</th></tr></thead>
+ <tbody>
+ {% for bill in bills %}
+ <tr class="{{ loop.cycle("odd", "even") }}" owers={{bill.owers|join(',','id')}} payer={{bill.payer.id}}>
+ <td>{{ bill.ower }}</td>
+ <td>{{ bill.payer }}</td>
+ <td>{{ "%0.2f"|format(bill.amount) }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+
+{% endblock %}
diff --git a/budget/web.py b/budget/web.py
index 489874c..b5a68a6 100644
--- a/budget/web.py
+++ b/budget/web.py
@@ -383,10 +383,11 @@ def change_lang(lang):
return redirect(request.headers.get('Referer') or url_for('.home'))
-@main.route("/<project_id>/compute")
-def compute_bills():
+@main.route("/<project_id>/settle_bill")
+def settle_bill():
"""Compute the sum each one have to pay to each other and display it"""
- return render_template("compute_bills.html")
+ bills = g.project.settle_bill()
+ return render_template("settle_bill.html", bills=bills)
@main.route("/<project_id>/archives/create")