diff --git a/docker-compose.yml b/docker-compose.yml index df893beb..cea47350 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,7 +33,7 @@ services: - SECRET_KEY=tralala - SESSION_COOKIE_SECURE=True - SHOW_ADMIN_EMAIL=True - - SQLALCHEMY_DATABASE_URI=sqlite:////database/ihatemoney.db + - SQLALCHEMY_DATABASE_URI=sqlite:///database/ihatemoney.db - SQLALCHEMY_TRACK_MODIFICATIONS=False - APPLICATION_ROOT=/ - ENABLE_CAPTCHA=False diff --git a/hatch_build.py b/hatch_build.py index d536daed..bedd89b3 100644 --- a/hatch_build.py +++ b/hatch_build.py @@ -1,5 +1,4 @@ import sys - from hatchling.builders.hooks.plugin.interface import BuildHookInterface diff --git a/ihatemoney.db b/ihatemoney.db new file mode 100644 index 00000000..b82834f2 Binary files /dev/null and b/ihatemoney.db differ diff --git a/ihatemoney/conf-templates/ihatemoney.cfg.j2 b/ihatemoney/conf-templates/ihatemoney.cfg.j2 index ba4793da..72e91973 100644 --- a/ihatemoney/conf-templates/ihatemoney.cfg.j2 +++ b/ihatemoney/conf-templates/ihatemoney.cfg.j2 @@ -8,7 +8,7 @@ DEBUG = False # The database URI, reprensenting the type of database and how to connect to it. # Enter an absolute path here. -SQLALCHEMY_DATABASE_URI = 'sqlite:////var/lib/ihatemoney/ihatemoney.sqlite' +SQLALCHEMY_DATABASE_URI = 'sqlite:///var/lib/ihatemoney/ihatemoney.sqlite' SQLACHEMY_ECHO = DEBUG # Will likely become the default value in flask-sqlalchemy >=3 ; could be removed diff --git a/ihatemoney/default_settings.py b/ihatemoney/default_settings.py index 48112afb..b477462e 100644 --- a/ihatemoney/default_settings.py +++ b/ihatemoney/default_settings.py @@ -1,6 +1,6 @@ # Verbose and documented settings are in conf-templates/ihatemoney.cfg.j2 DEBUG = SQLACHEMY_ECHO = False -SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/ihatemoney.db" +SQLALCHEMY_DATABASE_URI = "sqlite:///C:/Users/sylvie c/Documents/GitHub/ihatemoney/ihatemoney.db" SQLALCHEMY_TRACK_MODIFICATIONS = False SECRET_KEY = "tralala" MAIL_DEFAULT_SENDER = "Budget manager " diff --git a/ihatemoney/models.py b/ihatemoney/models.py index c591b85b..4228edea 100644 --- a/ihatemoney/models.py +++ b/ihatemoney/models.py @@ -133,7 +133,7 @@ class Project(db.Model): should_receive[bill.payer.id] += bill.converted_amount for ower in bill.owers: should_pay[ower.id] += ( - ower.weight * bill.converted_amount / total_weight + ower.weight * bill.converted_amount / total_weight ) if bill.bill_type == BillType.REIMBURSEMENT: @@ -264,6 +264,51 @@ class Project(db.Model): .order_by(Bill.id.desc()) ) + @staticmethod + def filter_by_date(query, start_date=None, end_date=None): + if start_date and end_date: + return query.filter(Bill.date.between(start_date, end_date)) + else: + return query + + @staticmethod + def filter_by_paid_by(query, paid_by=None): + if paid_by: + return query.filter(Bill.payer.has(paid_by)) + else: + return query + + @staticmethod + def filter_by_for_what(query, for_what=None): + if for_what: + return query.filter(Bill.what.has(for_what)) + else: + return query + + @staticmethod + def filter_by_for_whom(query, for_whom=None): + if for_whom: + return query.filter(Bill.payed_for.has(for_whom)) + else: + return query + + @staticmethod + def filter_by_how_much(query, how_much=None): + if how_much is not None: + return query.filter(Bill.amount.has(how_much)) + else: + return query + + @staticmethod + def apply_filters(self, query, **kwargs): + filtered_query = query + filtered_query = self.filter_by_date(filtered_query, kwargs.get('start_date'), kwargs.get('end_date')) + filtered_query = self.filter_by_paid_by(filtered_query, kwargs.get('paid_by')) + filtered_query = self.filter_by_for_what(filtered_query, kwargs.get('for_what')) + filtered_query = self.filter_by_for_whom(filtered_query, kwargs.get('for_whom')) + filtered_query = self.filter_by_how_much(filtered_query, kwargs.get('how_much')) + return filtered_query + def get_bill_weights(self): """ Return all bills for this project, along with the sum of weight for each bill. @@ -285,6 +330,28 @@ class Project(db.Model): """Ordered version of get_bill_weights""" return self.order_bills(self.get_bill_weights()) + def get_filtered_bill_weights_ordered(self, start_date=None, end_date=None, paid_by=None, for_what=None, + for_whom=None, + how_much=None): + query = self.get_bill_weights_ordered() + + if start_date and end_date: + query = self.filter_by_date(query, start_date, end_date) + + if paid_by: + query = self.filter_by_paid_by(query, paid_by) + + if for_what: + query = self.filter_by_for_what(query, for_what) + + if for_whom: + query = self.filter_by_for_whom(query, for_whom) + + if how_much is not None: + query = self.filter_by_how_much(query, how_much) + + return query + def get_member_bills(self, member_id): """Return the list of bills related to a specific member""" return ( @@ -697,16 +764,16 @@ class Bill(db.Model): currency_helper = CurrencyConverter() def __init__( - self, - amount: float, - date: datetime.datetime = None, - external_link: str = "", - original_currency: str = "", - owers: list = [], - payer_id: int = None, - project_default_currency: str = "", - what: str = "", - bill_type: str = "Expense", + self, + amount: float, + date: datetime.datetime = None, + external_link: str = "", + original_currency: str = "", + owers: list = [], + payer_id: int = None, + project_default_currency: str = "", + what: str = "", + bill_type: str = "Expense", ): super().__init__() self.amount = amount diff --git a/ihatemoney/templates/list_bills.html b/ihatemoney/templates/list_bills.html index 79e25262..f151bc86 100644 --- a/ihatemoney/templates/list_bills.html +++ b/ihatemoney/templates/list_bills.html @@ -105,6 +105,43 @@
  • {{ _("Older bills") }} »
  • {% endif %} +
    + {{ csrf_form.csrf_token }} + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + + +
    + + {{ static_include("images/plus.svg") | safe }} diff --git a/ihatemoney/web.py b/ihatemoney/web.py index 0d0bdd20..5005e43c 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -642,7 +642,7 @@ def invite(): return render_template("send_invites.html", form=form, qrcode=qrcode_svg) -@main.route("//") +@main.route("//", methods=["GET", "POST"]) def list_bills(): bill_form = get_billform_for(g.project) # Used for CSRF validation @@ -666,19 +666,53 @@ def list_bills(): # Each item will be a (weight_sum, Bill) tuple. # TODO: improve this awkward result using column_property: # https://docs.sqlalchemy.org/en/14/orm/mapped_sql_expr.html. - weighted_bills = g.project.get_bill_weights_ordered().paginate( - per_page=100, error_out=True - ) + if request.method == "GET": + weighted_bills = g.project.get_bill_weights_ordered().paginate( + per_page=100, error_out=True + ) + return render_template( + "list_bills.html", + bills=weighted_bills, + member_form=MemberForm(g.project), + bill_form=bill_form, + csrf_form=csrf_form, + add_bill=request.values.get("add_bill", False), + current_view="list_bills", + ) + + if request.method == "POST": + start_date = request.form.get('start_date', None) + end_date = request.form.get('end_date', None) + paid_by = request.form.get('paid_by', None) + for_what = request.form.get('for_what', None) + for_whom = request.form.get('for_whom', None) + how_much = request.form.get('how_much', None) + + weighted_bills = g.project.get_filtered_bill_weights_ordered( + start_date=start_date, + end_date=end_date, + paid_by=paid_by, + for_what=for_what, + for_whom=for_whom, + how_much=how_much + ).paginate(per_page=100, error_out=True) + + return render_template( + "list_bills.html", + bills=weighted_bills, + member_form=MemberForm(g.project), + bill_form=bill_form, + csrf_form=csrf_form, + add_bill=request.values.get("add_bill", False), + current_view="list_bills", + start_date=start_date, + end_date=end_date, + paid_by=paid_by, + for_what=for_what, + for_whom=for_whom, + how_much=how_much + ) - return render_template( - "list_bills.html", - bills=weighted_bills, - member_form=MemberForm(g.project), - bill_form=bill_form, - csrf_form=csrf_form, - add_bill=request.values.get("add_bill", False), - current_view="list_bills", - ) @main.route("//members/add", methods=["GET", "POST"])