diff --git a/ihatemoney/forms.py b/ihatemoney/forms.py index e1ddabe8..ca51d6f1 100644 --- a/ihatemoney/forms.py +++ b/ihatemoney/forms.py @@ -14,6 +14,8 @@ from wtforms.fields import ( BooleanField, DateField, DecimalField, + HiddenField, + IntegerField, Label, PasswordField, SelectField, @@ -438,6 +440,22 @@ class BillForm(FlaskForm): raise ValidationError(msg) +class HiddenCommaDecimalField(HiddenField, CommaDecimalField): + pass + + +class HiddenIntegerField(HiddenField, IntegerField): + pass + + +class SettlementForm(FlaskForm): + """Used internally for validation, not directly visible to users""" + + amount = HiddenCommaDecimalField("Amount", validators=[DataRequired()]) + sender_id = HiddenIntegerField("Sender", validators=[DataRequired()]) + receiver_id = HiddenIntegerField("Receiver", validators=[DataRequired()]) + + class MemberForm(FlaskForm): name = StringField(_("Name"), validators=[DataRequired()], filters=[strip_filter]) diff --git a/ihatemoney/templates/settle_bills.html b/ihatemoney/templates/settle_bills.html index d23f9c06..df48958f 100644 --- a/ihatemoney/templates/settle_bills.html +++ b/ihatemoney/templates/settle_bills.html @@ -11,15 +11,20 @@
{{ _("Who pays?") }} | {{ _("To whom?") }} | {{ _("How much?") }} | {{ _("Settled?") }} |
---|---|---|---|
{{ bill.ower }} | -{{ bill.receiver }} | -{{ bill.amount|currency }} | + {% for transaction in transactions %} +|
{{ transaction.ower }} | +{{ transaction.receiver }} | +{{ transaction.amount|currency }} | - - {{ ("Settle") }} + | diff --git a/ihatemoney/tests/budget_test.py b/ihatemoney/tests/budget_test.py index 093d9d17..03f08991 100644 --- a/ihatemoney/tests/budget_test.py +++ b/ihatemoney/tests/budget_test.py @@ -1358,23 +1358,25 @@ class TestBudget(IhatemoneyTestCase): count = 0 for t in transactions: count += 1 - self.client.get( - "/raclette/settle" - + "/" - + str(t["amount"]) - + "/" - + str(t["ower"].id) - + "/" - + str(t["receiver"].id) + self.client.post( + "/raclette/settle", + data={ + "amount": t["amount"], + "sender_id": t["ower"].id, + "receiver_id": t["receiver"].id, + }, ) temp_transactions = project.get_transactions_to_settle_bill() # test if the one has disappeared assert len(temp_transactions) == len(transactions) - count - # test if theres a new one with bill_type reimbursement + # test if there is a new one with bill_type reimbursement bill = project.get_newest_bill() assert bill.bill_type == models.BillType.REIMBURSEMENT - return + + # There should be no more settlement to do at the end + transactions = project.get_transactions_to_settle_bill() + assert len(transactions) == 0 def test_settle_zero(self): self.post_project("raclette") @@ -1463,6 +1465,78 @@ class TestBudget(IhatemoneyTestCase): # Create and log in as another project self.post_project("tartiflette") + # Add a participant in this second project + self.client.post("/tartiflette/members/add", data={"name": "pirate"}) + pirate = models.Person.query.filter(models.Person.id == 5).one() + assert pirate.name == "pirate" + + # Try to add a new bill in another project + self.client.post( + "/raclette/add", + data={ + "date": "2017-01-01", + "what": "fromage frelaté", + "payer": 2, + "payed_for": [2, 3, 4], + "bill_type": "Expense", + "amount": "100.0", + }, + ) + # Ensure it has not been created + raclette = self.get_project("raclette") + assert raclette.get_bills().count() == 1 + + # Try to add a new bill in our project that references members of another project. + # First with invalid payed_for IDs. + self.client.post( + "/tartiflette/add", + data={ + "date": "2017-01-01", + "what": "soupe", + "payer": 5, + "payed_for": [3], + "bill_type": "Expense", + "amount": "5000.0", + }, + ) + # Ensure it has not been created + piratebill = models.Bill.query.filter(models.Bill.what == "soupe").one_or_none() + assert piratebill is None, "piratebill 1 should not exist" + + # Then with invalid payer ID + self.client.post( + "/tartiflette/add", + data={ + "date": "2017-02-01", + "what": "pain", + "payer": 3, + "payed_for": [5], + "bill_type": "Expense", + "amount": "5000.0", + }, + ) + # Ensure it has not been created + piratebill = models.Bill.query.filter(models.Bill.what == "pain").one_or_none() + assert piratebill is None, "piratebill 2 should not exist" + + # Make sure we can actually create valid bills + self.client.post( + "/tartiflette/add", + data={ + "date": "2017-03-01", + "what": "baguette", + "payer": 5, + "payed_for": [5], + "bill_type": "Expense", + "amount": "5.0", + }, + ) + # Ensure it has been created + okbill = models.Bill.query.filter(models.Bill.what == "baguette").one_or_none() + assert okbill is not None, "Bill baguette should exist" + assert okbill.what == "baguette" + + # Now try to access and modify existing bills modified_bill = { "date": "2018-12-31", "what": "roblochon", @@ -1556,6 +1630,24 @@ class TestBudget(IhatemoneyTestCase): member = models.Person.query.filter(models.Person.id == 1).one_or_none() assert member is None + # test new settle endpoint to add bills with wrong payer / payed_for + self.client.post("/exit") + self.client.post( + "/authenticate", data={"id": "tartiflette", "password": "tartiflette"} + ) + self.client.post( + "/tartiflette/settle", + data={ + "sender_id": 4, + "receiver_id": 5, + "amount": "42.0", + }, + ) + piratebill = models.Bill.query.filter( + models.Bill.bill_type == models.BillType.REIMBURSEMENT + ).one_or_none() + assert piratebill is None, "piratebill 3 should not exist" + @pytest.mark.skip(reason="Currency conversion is broken") def test_currency_switch(self): # A project should be editable diff --git a/ihatemoney/utils.py b/ihatemoney/utils.py index ef4818ad..b8bb9a71 100644 --- a/ihatemoney/utils.py +++ b/ihatemoney/utils.py @@ -454,7 +454,9 @@ def format_form_errors(form, prefix): ) else: error_list = "