Do not allow negative weights on users (Fixes #362) (#366)

This commit is contained in:
Alexis Metaireau 2019-01-03 13:29:56 +01:00 committed by GitHub
parent 04adfe4155
commit d55b996170
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 70 additions and 5 deletions

View file

@ -8,10 +8,16 @@ This document describes changes between each past release.
Added
=====
- Add CORS headers in the API (#407)
- Document database migrations (#390)
- Allow basic math operations in amount field (#413)
Fixed
=====
- Do not allow negative weights on users (#366)
3.0 (2018-11-25)
----------------

View file

@ -2,7 +2,7 @@ from flask_wtf.form import FlaskForm
from wtforms.fields.core import SelectField, SelectMultipleField
from wtforms.fields.html5 import DateField, DecimalField
from wtforms.fields.simple import PasswordField, SubmitField, TextAreaField, StringField
from wtforms.validators import Email, Required, ValidationError, EqualTo
from wtforms.validators import Email, Required, ValidationError, EqualTo, NumberRange
from flask_babel import lazy_gettext as _
from flask import request
from werkzeug.security import generate_password_hash
@ -174,9 +174,11 @@ class BillForm(FlaskForm):
class MemberForm(FlaskForm):
name = StringField(_("Name"), validators=[Required()])
weight = CommaDecimalField(_("Weight"), default=1)
weight_validators = [NumberRange(min=0.1, message=_("Weights should be positive"))]
weight = CommaDecimalField(_("Weight"), default=1,
validators=weight_validators)
submit = SubmitField(_("Add"))
def __init__(self, project, edit=False, *args, **kwargs):

View file

@ -79,6 +79,9 @@ msgstr ""
msgid "Weight"
msgstr ""
msgid "Weights should be positive"
msgstr ""
msgid "Add"
msgstr ""

View file

@ -0,0 +1,38 @@
"""Migrate negative weights
Revision ID: a67119aa3ee5
Revises: afbf27e6ef20
Create Date: 2018-12-25 18:34:20.220844
"""
# revision identifiers, used by Alembic.
revision = 'a67119aa3ee5'
down_revision = 'afbf27e6ef20'
from alembic import op
import sqlalchemy as sa
# Snapshot of the person table
person_helper = sa.Table(
'person', sa.MetaData(),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('project_id', sa.String(length=64), nullable=True),
sa.Column('name', sa.UnicodeText(), nullable=True),
sa.Column('activated', sa.Boolean(), nullable=True),
sa.Column('weight', sa.Float(), nullable=True),
sa.ForeignKeyConstraint(['project_id'], ['project.id'], ),
sa.PrimaryKeyConstraint('id')
)
def upgrade():
op.execute(
person_helper.update()
.where(person_helper.c.weight <= 0)
.values(weight=1)
)
def downgrade():
# Downgrade path is not possible, because information has been lost.
pass

View file

@ -376,8 +376,9 @@ class Bill(db.Model):
def pay_each(self):
"""Compute what each share has to pay"""
if self.owers:
# FIXME: SQL might dot that more efficiently
return self.amount / sum(i.weight for i in self.owers)
# FIXME: SQL might do that more efficiently
weights = sum(i.weight for i in self.owers)
return self.amount / weights
else:
return 0

View file

@ -621,6 +621,18 @@ class BudgetTestCase(IhatemoneyTestCase):
resp = self.client.get("/raclette/")
self.assertNotIn('extra-info', resp.data.decode('utf-8'))
def test_negative_weight(self):
self.post_project("raclette")
# Add one user and edit it to have a negative share
self.client.post("/raclette/members/add", data={'name': 'alexis'})
resp = self.client.post("/raclette/members/1/edit", data={'name': 'alexis', 'weight': -1})
# An error should be generated, and its weight should still be 1.
self.assertIn('<p class="alert alert-danger">', resp.data.decode('utf-8'))
self.assertEqual(len(models.Project.query.get('raclette').members), 1)
self.assertEqual(models.Project.query.get('raclette').members[0].weight, 1)
def test_rounding(self):
self.post_project("raclette")

View file

@ -105,6 +105,9 @@ msgstr "Nom"
msgid "Weight"
msgstr "Parts"
msgid "Weights should be positive"
msgstr "Les parts doivent être positives"
msgid "Add"
msgstr "Ajouter"