monopyly 1.4.4__py3-none-any.whl → 1.4.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. monopyly/__init__.py +2 -1
  2. monopyly/_version.py +2 -2
  3. monopyly/auth/blueprint.py +1 -0
  4. monopyly/auth/routes.py +1 -0
  5. monopyly/auth/tools.py +1 -0
  6. monopyly/banking/accounts.py +2 -1
  7. monopyly/banking/actions.py +1 -0
  8. monopyly/banking/banks.py +1 -0
  9. monopyly/banking/blueprint.py +1 -0
  10. monopyly/banking/filters.py +1 -0
  11. monopyly/banking/forms.py +2 -1
  12. monopyly/banking/routes.py +1 -0
  13. monopyly/banking/transactions.py +1 -0
  14. monopyly/cli/apps.py +1 -0
  15. monopyly/common/forms/_forms.py +11 -10
  16. monopyly/common/forms/fields.py +10 -0
  17. monopyly/common/forms/utils.py +2 -1
  18. monopyly/common/forms/validators.py +1 -0
  19. monopyly/common/transactions.py +2 -1
  20. monopyly/common/utils.py +1 -0
  21. monopyly/config/default_settings.py +1 -0
  22. monopyly/config/settings.py +1 -0
  23. monopyly/core/actions.py +1 -0
  24. monopyly/core/blueprint.py +1 -0
  25. monopyly/core/context_processors.py +1 -0
  26. monopyly/core/filters.py +3 -2
  27. monopyly/core/internal_transactions.py +1 -0
  28. monopyly/core/routes.py +1 -0
  29. monopyly/credit/accounts.py +1 -0
  30. monopyly/credit/actions.py +30 -2
  31. monopyly/credit/blueprint.py +1 -0
  32. monopyly/credit/cards.py +1 -0
  33. monopyly/credit/forms.py +14 -6
  34. monopyly/credit/routes.py +3 -5
  35. monopyly/credit/statements.py +22 -1
  36. monopyly/credit/transactions/__init__.py +1 -0
  37. monopyly/credit/transactions/_transactions.py +1 -0
  38. monopyly/database/__init__.py +2 -1
  39. monopyly/database/models.py +14 -0
  40. monopyly/static/css/style.css +4 -0
  41. monopyly/templates/auth/login.html +1 -1
  42. monopyly/templates/auth/register.html +1 -1
  43. monopyly/templates/banking/account_summaries.html +1 -1
  44. monopyly/templates/banking/account_summary.html +1 -1
  45. monopyly/templates/banking/transaction_form/transaction_form.html +1 -1
  46. monopyly/templates/banking/transactions_table/expanded_row_content.html +1 -1
  47. monopyly/templates/credit/card_graphic/card_back.html +1 -1
  48. monopyly/templates/credit/statement_summary.html +3 -3
  49. monopyly/templates/index.html +1 -1
  50. {monopyly-1.4.4.dist-info → monopyly-1.4.6.dist-info}/METADATA +7 -7
  51. {monopyly-1.4.4.dist-info → monopyly-1.4.6.dist-info}/RECORD +55 -55
  52. {monopyly-1.4.4.dist-info → monopyly-1.4.6.dist-info}/WHEEL +0 -0
  53. {monopyly-1.4.4.dist-info → monopyly-1.4.6.dist-info}/entry_points.txt +0 -0
  54. {monopyly-1.4.4.dist-info → monopyly-1.4.6.dist-info}/licenses/COPYING +0 -0
  55. {monopyly-1.4.4.dist-info → monopyly-1.4.6.dist-info}/licenses/LICENSE +0 -0
monopyly/__init__.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Run a development server for the Monopyly app.
3
3
  """
4
+
4
5
  from flask import Flask
5
6
 
6
7
  from monopyly.config import DevelopmentConfig, ProductionConfig
@@ -38,7 +39,7 @@ def register_blueprints(app):
38
39
  """
39
40
  Register blueprints with the app.
40
41
 
41
- Note
42
+ Notes
42
43
  -----
43
44
  Blueprints are imported in this function to avoid loading modules
44
45
  that require database models before those models have been set up
monopyly/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '1.4.4'
4
- __version_tuple__ = version_tuple = (1, 4, 4)
3
+ __version__ = version = '1.4.6'
4
+ __version_tuple__ = version_tuple = (1, 4, 6)
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Flask blueprint for site authentication.
3
3
  """
4
+
4
5
  from flask import Blueprint
5
6
 
6
7
  # Define the blueprint
monopyly/auth/routes.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Routes for site authentication.
3
3
  """
4
+
4
5
  from flask import (
5
6
  current_app,
6
7
  flash,
monopyly/auth/tools.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for dealing with the authorization blueprint.
3
3
  """
4
+
4
5
  import functools
5
6
 
6
7
  from flask import current_app, g, redirect, session, url_for
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for interacting with bank accounts in the database.
3
3
  """
4
+
4
5
  import sqlalchemy.sql.functions as sql_func
5
6
  from authanor.database.handler import DatabaseViewHandler
6
7
  from werkzeug.exceptions import abort
@@ -57,7 +58,7 @@ class BankAccountTypeHandler(
57
58
  query = query.join(BankAccount).join(Bank).distinct()
58
59
  # Get only types for the specified bank
59
60
  query = query.where(Bank.id == bank_id)
60
- account_types = cls._db.session.execute(query).scalars()
61
+ account_types = cls._db.session.scalars(query)
61
62
  return account_types
62
63
 
63
64
  @classmethod
@@ -1,4 +1,5 @@
1
1
  """Module describing logical banking actions (to be used in routes)."""
2
+
2
3
  from ..common.utils import convert_date_to_midnight_timestamp
3
4
  from .accounts import BankAccountHandler, BankAccountTypeHandler
4
5
 
monopyly/banking/banks.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for interacting with banks in the database.
3
3
  """
4
+
4
5
  from authanor.database.handler import DatabaseHandler
5
6
 
6
7
  from ..database.models import Bank
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Flask blueprint for bank financials.
3
3
  """
4
+
4
5
  from flask import Blueprint
5
6
 
6
7
  # Define the blueprint
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Filters defined for the banking blueprint.
3
3
  """
4
+
4
5
  from .blueprint import bp
5
6
 
6
7
 
monopyly/banking/forms.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Generate banking forms for the user to complete.
3
3
  """
4
+
4
5
  from wtforms.fields import BooleanField, FieldList, FormField, StringField, SubmitField
5
6
  from wtforms.validators import DataRequired, Optional
6
7
 
@@ -162,7 +163,7 @@ class BankTransactionForm(TransactionForm):
162
163
  last_four_digits = LastFourDigitsField(
163
164
  "Last Four Digits", validators=[DataRequired()]
164
165
  )
165
- type_name = StringField("AccountType", validators=[DataRequired()])
166
+ type_name = StringField("Account Type", validators=[DataRequired()])
166
167
 
167
168
  def get_account(self):
168
169
  """Get the bank account described by the form data."""
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Routes for banking financials.
3
3
  """
4
+
4
5
  from flask import jsonify, redirect, render_template, request, url_for
5
6
  from fuisce.database import db_transaction
6
7
 
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for interacting with the bank transactions in the database.
3
3
  """
4
+
4
5
  from authanor.database.handler import DatabaseViewHandler
5
6
 
6
7
  from ..common.forms.utils import execute_on_form_validation
monopyly/cli/apps.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Application objects for running the app via the CLI."""
2
+
2
3
  import multiprocessing
3
4
  import subprocess
4
5
 
@@ -1,6 +1,7 @@
1
1
  """
2
2
  General form constructions.
3
3
  """
4
+
4
5
  from abc import ABC, abstractmethod
5
6
 
6
7
  from flask_wtf import FlaskForm
@@ -33,16 +34,6 @@ class EntryForm(FlaskForm, metaclass=AbstractEntryFormMixinMeta):
33
34
  """
34
35
  Generate a duplicate prepopulated form.
35
36
 
36
- Note
37
- ----
38
- WTForms requires that a form be instantiated in order to be
39
- able to properly introspect fields. Because of this, this method
40
- will only return a duplicate form matching the type of the
41
- form instance used to call it. Using the form's process method
42
- will not properly handle enumeration of field lists, so it
43
- can not be used as a replacement for populating an existing
44
- form.
45
-
46
37
  Parameters
47
38
  ----------
48
39
  entry : database.models.Model
@@ -54,6 +45,16 @@ class EntryForm(FlaskForm, metaclass=AbstractEntryFormMixinMeta):
54
45
  form : EntryForm
55
46
  A duplicate form, prepopulated with the collected database
56
47
  information.
48
+
49
+ Notes
50
+ -----
51
+ WTForms requires that a form be instantiated in order to be
52
+ able to properly introspect fields. Because of this, this method
53
+ will only return a duplicate form matching the type of the
54
+ form instance used to call it. Using the form's process method
55
+ will not properly handle enumeration of field lists, so it
56
+ can not be used as a replacement for populating an existing
57
+ form.
57
58
  """
58
59
  data = self.gather_entry_data(entry)
59
60
  return self.__class__(data=data)
@@ -1,6 +1,7 @@
1
1
  """
2
2
  General form constructions.
3
3
  """
4
+
4
5
  from abc import ABC, abstractmethod
5
6
 
6
7
  from flask_wtf import FlaskForm
@@ -25,6 +26,15 @@ class DateField(wtforms_fields.DateField):
25
26
  super().__init__(*args, filters=filters, **kwargs)
26
27
 
27
28
 
29
+ class OptionalDateField(DateField):
30
+ """A date field where the field value is optional."""
31
+
32
+ def process_formdata(self, valuelist):
33
+ """Process data from the form, ignoring an ommitted value."""
34
+ if valuelist != [""]:
35
+ super().process_formdata(valuelist)
36
+
37
+
28
38
  class CurrencyField(wtforms_fields.DecimalField):
29
39
  """A decimal field with currency-specific customizations."""
30
40
 
@@ -1,6 +1,7 @@
1
1
  """
2
2
  General utility objects for handling forms.
3
3
  """
4
+
4
5
  from functools import wraps
5
6
 
6
7
  from flask import current_app, flash
@@ -73,7 +74,7 @@ class Autocompleter:
73
74
  """Get autocomplete suggestions for a field."""
74
75
  # Get information from the database to use for autocompletion
75
76
  query = model.select_for_user(getattr(model, field))
76
- values = current_app.db.session.execute(query).scalars()
77
+ values = current_app.db.session.scalars(query)
77
78
  suggestions = sort_by_frequency([value for value in values])
78
79
  return suggestions
79
80
 
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Commonly shared form validators.
3
3
  """
4
+
4
5
  from wtforms.validators import ValidationError
5
6
 
6
7
 
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for building a common transaction interface.
3
3
  """
4
+
4
5
  from abc import abstractmethod
5
6
 
6
7
  from authanor.database.handler import DatabaseHandler, DatabaseViewHandler
@@ -248,7 +249,7 @@ class TransactionTagHandler(DatabaseHandler, model=TransactionTag):
248
249
  # Filter the query to get only subtags of the given tag
249
250
  parent_id = tag.id if tag else None
250
251
  query = query.where(cls.model.parent_id == parent_id)
251
- subtags = cls._db.session.execute(query).scalars()
252
+ subtags = cls._db.session.scalars(query)
252
253
  return subtags
253
254
 
254
255
  @classmethod
monopyly/common/utils.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  General utility objects.
3
3
  """
4
+
4
5
  import datetime
5
6
  from collections import Counter
6
7
  from datetime import timezone
@@ -1,4 +1,5 @@
1
1
  """Default configuration settings."""
2
+
2
3
  import json
3
4
  from pathlib import Path
4
5
 
@@ -1,4 +1,5 @@
1
1
  """A module containing objects with various configuration settings."""
2
+
2
3
  import warnings
3
4
  from pathlib import Path
4
5
 
monopyly/core/actions.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Module describing logical core actions (to be used in routes)."""
2
+
2
3
  from datetime import datetime
3
4
 
4
5
  import markdown
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Flask blueprint for core functionality.
3
3
  """
4
+
4
5
  from flask import Blueprint
5
6
 
6
7
  # Define the blueprint
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Filters defined for the application.
3
3
  """
4
+
4
5
  from datetime import date
5
6
  from importlib import import_module
6
7
 
monopyly/core/filters.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Filters defined for the application.
3
3
  """
4
+
4
5
  from .blueprint import bp
5
6
 
6
7
 
@@ -29,8 +30,8 @@ def make_ordinal(integer):
29
30
  - 21 => 21st
30
31
  - 101 => 101st
31
32
 
32
- Note
33
- ----
33
+ Notes
34
+ -----
34
35
  This function is an adaptation of the one proposed by Stack Overflow user
35
36
  Florian Brucker (https://stackoverflow.com/a/50992575/8754471).
36
37
 
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for interacting with internal transactions in the database.
3
3
  """
4
+
4
5
  from flask import current_app
5
6
  from sqlalchemy import insert
6
7
 
monopyly/core/routes.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Routes for core functionality.
3
3
  """
4
+
4
5
  from pathlib import Path
5
6
 
6
7
  from flask import g, render_template, render_template_string, session
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for interacting with credit accounts in the database.
3
3
  """
4
+
4
5
  from authanor.database.handler import DatabaseHandler
5
6
 
6
7
  from ..database.models import CreditAccount
@@ -1,4 +1,5 @@
1
1
  """Module describing logical credit actions (to be used in routes)."""
2
+
2
3
  from ..banking.transactions import record_new_transfer
3
4
  from ..common.forms.utils import execute_on_form_validation
4
5
  from .cards import CreditCardHandler
@@ -12,7 +13,7 @@ def get_card_statement_grouping(cards):
12
13
 
13
14
  Parameters
14
15
  ----------
15
- cards : list of sqlite3.Row
16
+ cards : list of database.models.CreditCard
16
17
  The database card entries for which to get statements.
17
18
 
18
19
  Returns
@@ -30,6 +31,33 @@ def get_card_statement_grouping(cards):
30
31
  return card_statements
31
32
 
32
33
 
34
+ def get_statement_and_transactions(statement_id, transaction_sort_order="DESC"):
35
+ """
36
+ Given a statement ID, get the corresponding statement and transactions.
37
+
38
+ Parameters
39
+ ----------
40
+ statement_id : int
41
+ The ID of the statement to acquire.
42
+ transaction_sort_order : str
43
+ The order to sort transactions returned for the statement. The
44
+ default is 'DESC' for transactions sorted in descending order.
45
+
46
+ Returns
47
+ -------
48
+ statement : database.models.CreditStatementView
49
+ The statement with the given ID.
50
+ transactions : list of database.models.CreditTransactionView
51
+ All transactions on the statement with the given ID.
52
+ """
53
+ statement = CreditStatementHandler.get_entry(statement_id)
54
+ transactions = CreditTransactionHandler.get_transactions(
55
+ statement_ids=(statement_id,),
56
+ sort_order=transaction_sort_order,
57
+ )
58
+ return statement, transactions.all()
59
+
60
+
33
61
  def get_potential_preceding_card(card):
34
62
  """
35
63
  Get the card that this new card may be intended to replace (if any).
@@ -41,7 +69,7 @@ def get_potential_preceding_card(card):
41
69
 
42
70
  Parameters
43
71
  ----------
44
- card : sqlite3.Row
72
+ card : database.models.CreditCard
45
73
  The new card being added to the database.
46
74
 
47
75
  Returns
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Flask blueprint for credit card financials.
3
3
  """
4
+
4
5
  from flask import Blueprint
5
6
 
6
7
  # Define the blueprint
monopyly/credit/cards.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for interacting with credit cards in the database.
3
3
  """
4
+
4
5
  from authanor.database.handler import DatabaseHandler
5
6
 
6
7
  from ..common.forms.utils import execute_on_form_validation
monopyly/credit/forms.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Generate credit card forms for the user to complete.
3
3
  """
4
+
4
5
  from werkzeug.exceptions import abort
5
6
  from wtforms.fields import (
6
7
  BooleanField,
@@ -18,8 +19,8 @@ from ..banking.forms import BankSelectField, BankSubform
18
19
  from ..common.forms import AcquisitionSubform, EntryForm, EntrySubform, TransactionForm
19
20
  from ..common.forms.fields import (
20
21
  CustomChoiceSelectField,
21
- DateField,
22
22
  LastFourDigitsField,
23
+ OptionalDateField,
23
24
  StringField,
24
25
  )
25
26
  from ..common.forms.utils import Autocompleter
@@ -69,13 +70,20 @@ class CreditCardForm(EntryForm):
69
70
  statement_issue_day = IntegerField("Statement Issue Day", [Optional()])
70
71
  statement_due_day = IntegerField("Statement Due Day", [Optional()])
71
72
 
72
- def validate(self, *args, **kwargs):
73
- """Validate the subform."""
74
- # Unset subform `CustomChoiceSelectField` values will be invalid
73
+ def validate(self, extra_validators=None):
74
+ """
75
+ Validate the subform.
76
+
77
+ Notes
78
+ -----
79
+ Unset values on the ``CustomChoiceSelectField`` will take a
80
+ default value that will be considered to be invalid. They
81
+ must be replaced (after processing) with the inferred value.
82
+ """
75
83
  if self.bank_info.bank_id.data == -1:
76
84
  account = self.get_account()
77
85
  self.bank_info.bank_id.data = account.bank.id
78
- return super().validate(*args, **kwargs)
86
+ return super().validate(extra_validators=extra_validators)
79
87
 
80
88
  def get_account(self):
81
89
  return self._produce_entry_from_field("account_id")
@@ -185,7 +193,7 @@ class CreditTransactionForm(TransactionForm):
185
193
  # Fields to identify the card/bank information for the statement
186
194
  card_info = FormField(CardSubform)
187
195
  # Fields pertaining to the statement
188
- issue_date = DateField("Statement Date")
196
+ issue_date = OptionalDateField("Statement Date")
189
197
 
190
198
  def get_statement(self, transaction_date):
191
199
  """Get the credit card statement described by the form data."""
monopyly/credit/routes.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Routes for credit card financials.
3
3
  """
4
+
4
5
  from itertools import islice
5
6
 
6
7
  from flask import flash, g, jsonify, redirect, render_template, request, url_for
@@ -21,6 +22,7 @@ from .accounts import CreditAccountHandler
21
22
  from .actions import (
22
23
  get_card_statement_grouping,
23
24
  get_potential_preceding_card,
25
+ get_statement_and_transactions,
24
26
  make_payment,
25
27
  transfer_credit_card_statement,
26
28
  )
@@ -176,11 +178,7 @@ def update_statements_display():
176
178
  @bp.route("/statement/<int:statement_id>")
177
179
  @login_required
178
180
  def load_statement_details(statement_id):
179
- statement = CreditStatementHandler.get_entry(statement_id)
180
- transactions = CreditTransactionHandler.get_transactions(
181
- statement_ids=(statement_id,),
182
- sort_order="DESC",
183
- )
181
+ statement, transactions = get_statement_and_transactions(statement_id)
184
182
  # Get bank accounts for potential payments
185
183
  bank_accounts = BankAccountHandler.get_accounts()
186
184
  return render_template(
@@ -1,7 +1,9 @@
1
1
  """
2
2
  Tools for interacting with the credit statements in the database.
3
3
  """
4
+
4
5
  from authanor.database.handler import DatabaseViewHandler
6
+ from dateutil.relativedelta import relativedelta
5
7
 
6
8
  from ..common.utils import get_next_occurrence_of_day
7
9
  from ..database.models import (
@@ -135,7 +137,7 @@ class CreditStatementHandler(
135
137
 
136
138
  Returns
137
139
  -------
138
- statement : database.models.CreditStatementView
140
+ statement : database.models.CreditStatement
139
141
  The inferred statement entry for the transaction.
140
142
  """
141
143
  issue_day = card.account.statement_issue_day
@@ -145,6 +147,25 @@ class CreditStatementHandler(
145
147
  statement = cls.add_statement(card, issue_date)
146
148
  return statement
147
149
 
150
+ @classmethod
151
+ @DatabaseViewHandler.view_query
152
+ def get_prior_statement(cls, statement):
153
+ """
154
+ Given a statement, get the immediately preceding statement.
155
+
156
+ Parameters
157
+ ----------
158
+ statement : database.models.CreditStatement
159
+ The statement for which to find the preceding statement.
160
+
161
+ Returns
162
+ -------
163
+ statement : database.models.CreditStatementView
164
+ The statement immediately preceding the given statement.
165
+ """
166
+ issue_date = statement.issue_date + relativedelta(months=-1)
167
+ return cls.find_statement(statement.card.id, issue_date=issue_date)
168
+
148
169
  @classmethod
149
170
  def add_statement(cls, card, issue_date, due_date=None):
150
171
  """Add a statement to the database."""
@@ -1,4 +1,5 @@
1
1
  """
2
2
  Tools for interacting with the credit transactions in the database.
3
3
  """
4
+
4
5
  from ._transactions import CreditTagHandler, CreditTransactionHandler, save_transaction
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Tools for interacting with the credit transactions in the database.
3
3
  """
4
+
4
5
  from authanor.database.handler import DatabaseHandler, DatabaseViewHandler
5
6
 
6
7
  from ...common.forms.utils import execute_on_form_validation
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Expose commonly used database functionality to the rest of the package.
3
3
  """
4
+
4
5
  import sqlite3
5
6
  from pathlib import Path
6
7
 
@@ -49,7 +50,7 @@ class SQLAlchemy(_SQLAlchemy):
49
50
  super().initialize(app)
50
51
 
51
52
 
52
- SQLAlchemy.create_default_interface()
53
+ SQLAlchemy.create_default_interface(echo_engine=False)
53
54
 
54
55
 
55
56
  @click.command("init-db")
@@ -1,5 +1,6 @@
1
1
  from authanor.database.models import AuthorizedAccessMixin, Model
2
2
  from sqlalchemy import Column, Date, Float, ForeignKey, Integer, String, Table
3
+ from sqlalchemy.ext.hybrid import hybrid_property
3
4
  from sqlalchemy.orm import mapped_column, relationship
4
5
 
5
6
 
@@ -72,6 +73,8 @@ class TransactionTag(AuthorizedAccessMixin, Model):
72
73
  parent_id = mapped_column(Integer, ForeignKey("transaction_tags.id"))
73
74
  tag_name = mapped_column(String, nullable=False)
74
75
  # Relationships
76
+ parent = relationship("TransactionTag", back_populates="children", remote_side=[id])
77
+ children = relationship("TransactionTag", back_populates="parent")
75
78
  bank_subtransactions = relationship(
76
79
  "BankSubtransaction",
77
80
  secondary=bank_tag_link_table,
@@ -83,6 +86,13 @@ class TransactionTag(AuthorizedAccessMixin, Model):
83
86
  back_populates="tags",
84
87
  )
85
88
 
89
+ @property
90
+ def depth(self):
91
+ tag, depth = self, 0
92
+ while (tag := tag.parent) is not None:
93
+ depth += 1
94
+ return depth
95
+
86
96
 
87
97
  class Bank(AuthorizedAccessMixin, Model):
88
98
  __tablename__ = "banks"
@@ -256,6 +266,7 @@ class BankTransactionView(AuthorizedAccessMixin, Model):
256
266
  subtransactions = relationship(
257
267
  "BankSubtransaction",
258
268
  back_populates="transaction",
269
+ lazy="selectin",
259
270
  )
260
271
 
261
272
 
@@ -281,6 +292,7 @@ class BankSubtransaction(AuthorizedAccessMixin, Model):
281
292
  "TransactionTag",
282
293
  secondary=bank_tag_link_table,
283
294
  back_populates="bank_subtransactions",
295
+ lazy="selectin",
284
296
  )
285
297
 
286
298
 
@@ -429,6 +441,7 @@ class CreditTransactionView(AuthorizedAccessMixin, Model):
429
441
  subtransactions = relationship(
430
442
  "CreditSubtransaction",
431
443
  back_populates="transaction",
444
+ lazy="selectin",
432
445
  )
433
446
 
434
447
 
@@ -460,4 +473,5 @@ class CreditSubtransaction(AuthorizedAccessMixin, Model):
460
473
  "TransactionTag",
461
474
  secondary=credit_tag_link_table,
462
475
  back_populates="credit_subtransactions",
476
+ lazy="selectin",
463
477
  )
@@ -1664,6 +1664,10 @@ form .autocomplete-box .item.active {
1664
1664
  text-align: center;
1665
1665
  }
1666
1666
 
1667
+ .details .summary-box .balance .dollar-sign {
1668
+ margin-right: 5px;
1669
+ }
1670
+
1667
1671
  .details .summary-box .projected-balance .amount {
1668
1672
  color: var(--moneytree);
1669
1673
  font-weight: 500;
@@ -10,6 +10,6 @@
10
10
  <input name="username" id="username" required>
11
11
  <label for="password">Password</label>
12
12
  <input type="password" name="password" id="password" required>
13
- <input class="button" type="submit" value="Log In">
13
+ <input class="button" type="submit" value="Log In" />
14
14
  </form>
15
15
  {% endblock %}
@@ -10,6 +10,6 @@
10
10
  <input name="username" id="username" required>
11
11
  <label for="password">Password</label>
12
12
  <input type="password" name="password" id="password" required>
13
- <input class="button" type="submit" value="Register">
13
+ <input class="button" type="submit" value="Register" />
14
14
  </form>
15
15
  {% endblock %}
@@ -3,7 +3,7 @@
3
3
  {% for account_type in type_accounts %}
4
4
  <div class="account-type-block grouping">
5
5
 
6
- {% for account in type_accounts[account_type] %}
6
+ {% for account in type_accounts[account_type]|sort(attribute='last_four_digits') %}
7
7
 
8
8
  <a class="account-block button-block" href="{{ url_for('banking.load_account_details', account_id=account.id) }}">
9
9
  <div class="title">
@@ -9,7 +9,7 @@
9
9
  </div>
10
10
 
11
11
  <div class="balance">
12
- <div>$</div>
12
+ <div class="dollar-sign">$</div>
13
13
  <div>{{ account.balance|currency }}</div>
14
14
  </div>
15
15
 
@@ -1,4 +1,4 @@
1
- <form id="bank-transaction" class="transaction" action="{{ submission_url }}" method="POST" autocomplete="off">
1
+ <form id="bank-transaction" class="transaction" action="{{ submission_url }}" method="post" autocomplete="off">
2
2
 
3
3
  {{ form.hidden_tag() }}
4
4
 
@@ -19,7 +19,7 @@
19
19
 
20
20
  <div class="description-header">
21
21
  <div class="brief">
22
- {% if transaction | is_single_bank_transfer %}
22
+ {% if transaction|is_single_bank_transfer %}
23
23
  Transfer
24
24
  {% else %}
25
25
  {% if transaction.merchant %}
@@ -10,7 +10,7 @@
10
10
  <div class="options">
11
11
 
12
12
  <label class="toggle-switch-gadget" for="card-{{ card.id }}-status">
13
- <input id="card-{{ card.id }}-status" data-card-id="{{ card.id }}" type="checkbox" {{ 'checked' if card.active else '' }}/>
13
+ <input id="card-{{ card.id }}-status" data-card-id="{{ card.id }}" type="checkbox"{{ ' checked' if card.active else '' }} />
14
14
 
15
15
  <div class="option">
16
16
  <div class="text">
@@ -9,7 +9,7 @@
9
9
  </div>
10
10
 
11
11
  <div class="balance">
12
- <div>$</div>
12
+ <div class="dollar-sign">$</div>
13
13
  <div>{{ statement.balance|currency }}</div>
14
14
  </div>
15
15
 
@@ -47,8 +47,8 @@
47
47
 
48
48
  <div class="form-line">
49
49
  <div class="dollar-sign">$</div>
50
- <input id="pay-amount" type="text" name="pay_amount" value="{{ statement.balance|currency }}" maxlength="8"/>
51
- <input id="pay-date" type="text" name="pay_date" value="{{ date_today }}" maxlength="10"/>
50
+ <input id="pay-amount" type="text" name="pay_amount" value="{{ statement.balance|currency }}" maxlength="8" />
51
+ <input id="pay-date" type="text" name="pay_date" value="{{ date_today }}" maxlength="10" />
52
52
  </div>
53
53
 
54
54
  <div class="form-line">
@@ -50,7 +50,7 @@
50
50
  See account summaries
51
51
  </a>
52
52
  <ul>
53
- {% for account in bank_accounts[bank] %}
53
+ {% for account in bank_accounts[bank]|sort(attribute='account_type.type_common_name,last_four_digits') %}
54
54
  <li>
55
55
  <a href="{{ url_for('banking.load_account_details', account_id=account.id) }}">
56
56
  {{ account.account_type.type_common_name }} {{ account.last_four_digits }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: monopyly
3
- Version: 1.4.4
3
+ Version: 1.4.6
4
4
  Summary: A homemade personal finance manager.
5
5
  Project-URL: Download, https://pypi.org/project/monopyly
6
6
  Project-URL: Homepage, http://monopyly.com
@@ -25,13 +25,13 @@ Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
25
25
  Requires-Python: <3.11,>=3.10
26
26
  Requires-Dist: authanor==1.1.0
27
27
  Requires-Dist: flask-wtf==1.2.1
28
- Requires-Dist: flask==3.0.0
28
+ Requires-Dist: flask==3.0.3
29
29
  Requires-Dist: fuisce==1.0.2
30
- Requires-Dist: gunicorn==21.2.0
31
- Requires-Dist: markdown==3.5.1
32
- Requires-Dist: python-dateutil==2.8.2
33
- Requires-Dist: rich==13.7.0
34
- Requires-Dist: sqlalchemy==2.0.23
30
+ Requires-Dist: gunicorn==22.0.0
31
+ Requires-Dist: markdown==3.6
32
+ Requires-Dist: python-dateutil==2.9.0
33
+ Requires-Dist: rich==13.7.1
34
+ Requires-Dist: sqlalchemy==2.0.29
35
35
  Description-Content-Type: text/markdown
36
36
 
37
37
  <div id="header">
@@ -1,52 +1,52 @@
1
1
  monopyly/README.md,sha256=T-Avdpr3whgP6g0P_w-u02CSwzBH2yRnleRrM1sKrj4,8281
2
- monopyly/__init__.py,sha256=IJkmwjfkx-AqV_R2P6a6V_Hf7VR7HQAcIKB4atpQ9R8,1761
3
- monopyly/_version.py,sha256=Gdr1mJsyNZMcHyr55nNXWXcVuRw1WkUj7UMD7OEywt4,160
2
+ monopyly/__init__.py,sha256=YxMJAI5ig4-jlFyBGy4gOC_H5cp8a4L2sGmoX5bRYjU,1763
3
+ monopyly/_version.py,sha256=rcHU37_iyVNYVuSTTj5Shc-mUDPpQw2cyeznRUHdnOc,160
4
4
  monopyly/auth/actions.py,sha256=T99eAP8dxFZMzLu2LxZFIXGYIroP8I7KJ-WJ8GJ1gws,260
5
- monopyly/auth/blueprint.py,sha256=r2BfSFz-n29KDvyMwbh2puvenoIddalES5joIxjYnfM,243
6
- monopyly/auth/routes.py,sha256=psWxQg_fGyMbBPDoS0JIcLae43DL584gcEBynbumgaw,2901
7
- monopyly/auth/tools.py,sha256=QrzYa5OuTxpBug2hcV5GxqrtRHp3Y5IWy90m-tdDyYA,800
8
- monopyly/banking/accounts.py,sha256=9tjmdY0Eyi8Q6aMhUo6kPn_D6HGA_zIBHSBaVCTikEs,9837
9
- monopyly/banking/actions.py,sha256=bxuNV4-a3yrw0PVkq6BZKYi9M0rmWGnH7OXaHUtI5ZI,1564
10
- monopyly/banking/banks.py,sha256=OKehUdEssgf29sDFoajKanz6KLfnucjO1mx3rWUG_HA,1744
11
- monopyly/banking/blueprint.py,sha256=d1K4bz13MjBdKCR_Poyj4oWeTcBnCKIWR9GlAPyrFEs,254
12
- monopyly/banking/filters.py,sha256=2xwK1PFieX4dJu1-GaHw_tFC1USm7eIN2Nnn7HiUHmw,684
13
- monopyly/banking/forms.py,sha256=yMJE_uQNtSaWGNV-5I3WP_LWmvpyCWJ3XyJBt21ZknY,10504
14
- monopyly/banking/routes.py,sha256=-YPvcjdBW7svCLw-jYClhTHZYiPn5k1bokDqlFna19I,8148
15
- monopyly/banking/transactions.py,sha256=WAQjEZwaPLnCja5r3E95c_2KkvbQc_s7qmtKvTlID3k,8453
16
- monopyly/cli/apps.py,sha256=Y2IwoXzRDwGDXPO770aSYPmBwJQCxRcg8MH6SJfr5_c,2688
5
+ monopyly/auth/blueprint.py,sha256=sFbmTvSBhjv1pn2gJLH5J6zanPLuemmWbgEj5ZKCiTY,244
6
+ monopyly/auth/routes.py,sha256=mODJHrdFhffusfbYwQ_wpIY7eAE7Gh7irosIef4Mluc,2902
7
+ monopyly/auth/tools.py,sha256=hifCHBN06Ht-FnMhJPzzj-xjMN8uwbeJ-azRVjnT99c,801
8
+ monopyly/banking/accounts.py,sha256=BYGqDISd5FszktDa1NW494ipcNgqI1Y03Gn07kBeiWE,9828
9
+ monopyly/banking/actions.py,sha256=am8AlVWJGhrTvWNiAQPba-2guxdESmhzl6mIJhjAmlg,1565
10
+ monopyly/banking/banks.py,sha256=X1seKJrec-o8z-bM0wVTR42vMx1q5A9Lf2jNUtINlrQ,1745
11
+ monopyly/banking/blueprint.py,sha256=PDsqe4DmiT7cDYW9IxxTfizu9Lqol1Lhp7ajMK6fUuI,255
12
+ monopyly/banking/filters.py,sha256=wCsAUh592B5BmGFC9NqmrJZ0NdQZi21hvy0QsKqhgLg,685
13
+ monopyly/banking/forms.py,sha256=p-YGZgdKugk3VKILLiQNEH5KQmXop5G8P1Bs4sUqbK0,10506
14
+ monopyly/banking/routes.py,sha256=BAV6PBQf20dSTxqY88Zh0FGAzpj7TxgZaHPxGCJneJ4,8149
15
+ monopyly/banking/transactions.py,sha256=2_wFXl4ZrHzLoFGz72ksYA2_8UvB0N1wtBFd8kTwTyQ,8454
16
+ monopyly/cli/apps.py,sha256=ufyKali-sLsUONuERoMTvSjbFzageShtG62JO0p9fIo,2689
17
17
  monopyly/cli/run.py,sha256=8NHv5TW5Ze6pyhxZCOjmba-EU4Nu6sObYfNJbDPtrds,3838
18
- monopyly/common/transactions.py,sha256=c7OUUI4VDh3byORPoIC8GFScNuajBofBHshmfXRi1fI,12194
19
- monopyly/common/utils.py,sha256=ujfGMXTtH3sVk10bRuN5e1dAu0eToRkU_e8TR821d7g,5866
18
+ monopyly/common/transactions.py,sha256=2-EkctGC9k5W5kodXdYJIBXu5l5JNI9dxfgbp2Nxccg,12185
19
+ monopyly/common/utils.py,sha256=BjXhfNXGWkAPt-ebldvmZ2z00P8MhKJzjxrJJ6mzRQY,5867
20
20
  monopyly/common/forms/__init__.py,sha256=6iFTlcoaQmCbrjS1KfFhWXQUfyqnA-vKPMVxLyJMlXY,120
21
- monopyly/common/forms/_forms.py,sha256=AxQjxO6_QdBWuc2PVjep_9oYqoAjvjPunyGPJl0Ahj0,7811
22
- monopyly/common/forms/fields.py,sha256=tJ4Xqrm7y6dYNLzpsq85lFxN20V6cDAfUF6DstzUVi8,3793
23
- monopyly/common/forms/utils.py,sha256=wHkLP7u5m2uuglunXR22TablKB7Uoj-UWs2rYkJzhqM,5685
24
- monopyly/common/forms/validators.py,sha256=dM1WHEJTWpSH2u4oxU3PyIO7lY4fyN6PtRsUS8Ie0cI,1204
21
+ monopyly/common/forms/_forms.py,sha256=TjWEe62LQ3j-OQqzu0whz8CyhQVixhiHCqnMR_n7zmU,7814
22
+ monopyly/common/forms/fields.py,sha256=XgvkfszpUAZyIs-osHGFADmzuo0Ni_e78NXtG-grBdI,4082
23
+ monopyly/common/forms/utils.py,sha256=awZOtWaohzSW_SZrXhUpJBY1AXPnKBFTG2cqim22Db0,5676
24
+ monopyly/common/forms/validators.py,sha256=C5_NN8ktvBw6MrSXwv_x9_SQohCNmnp4brNQFELHBlI,1205
25
25
  monopyly/config/__init__.py,sha256=pW2lHNhsdM2gRjLAWumnLUHZMAOEdprX4aXaONwo6AY,73
26
- monopyly/config/default_settings.py,sha256=ZMKM20TPRymOXA21o3VcTNc0yQyPG5iGtwRe7_tMtQU,1828
27
- monopyly/config/settings.py,sha256=xOgnXypQPa0TRyAR1wFItBYM7sR-EJzPPmov6X-tiwY,1983
28
- monopyly/core/actions.py,sha256=cUofmjHKWvdSgEQutzihFK378C5MpXl8fttcOWtMVeY,1219
29
- monopyly/core/blueprint.py,sha256=UevEYMNdzknzKBr31Zy5ZrGRgKI1lr4tNcFt9MV6tqA,251
30
- monopyly/core/context_processors.py,sha256=5zFKgd5QIDTZqqQmjHpCGhwTf1j5WNMCqGegfYRQaak,820
31
- monopyly/core/filters.py,sha256=nf-zyWjYHELmyI5RAH5CjHbg35zeVY33OyWZYUcZApo,1250
32
- monopyly/core/internal_transactions.py,sha256=Hf3vUhpvMgB2wgDbrjALQ9b7x-pS9PX0lUQ0S8fjA_c,458
33
- monopyly/core/routes.py,sha256=u8ZgVt6vE5kfQadp19PBbO6VP2OvU9ccAGkbqrWPCGA,2426
34
- monopyly/credit/accounts.py,sha256=4coTjk4YF8sXPsHNzqftMny5fzi6HfzqB0qkowCi0Js,1897
35
- monopyly/credit/actions.py,sha256=5gtN-CXU7SmCrY6bnZGloKmSQyIlIFArHCs4wlHgpgs,5333
36
- monopyly/credit/blueprint.py,sha256=deN8s8mEKF1QvCBm7eayo-jZ4DFwWFu99uIEu6kcqVk,250
37
- monopyly/credit/cards.py,sha256=OkyeG6HdfZnXI-oJ6k-ncecqmB9rtXvQ0TWt2REG_L0,5613
38
- monopyly/credit/forms.py,sha256=K5uKgjdcZSRAAyQGiqWAxjH5JvBiXYs54JXfUawltvM,12008
39
- monopyly/credit/routes.py,sha256=Mjfr0no0Pua4-22ys8210DfApdaiMge5QzpoJBMdT9U,17879
40
- monopyly/credit/statements.py,sha256=cbRSElIPl7Si23hgbiV0naBSVKf4N-eHv12XtFuS_ZY,6519
41
- monopyly/credit/transactions/__init__.py,sha256=x2ueMsckfU0IosWIpwbexQqgUwR5ka406gDIrLEKkY4,164
42
- monopyly/credit/transactions/_transactions.py,sha256=7Z6vXuzGwPtU-ryaIkOLxlfCUSpET5SZt8gAeu4J5Ao,8770
43
- monopyly/database/__init__.py,sha256=ZL8SnNdABhLYSm18k5gGddZDmhAuBsX9h-zCAcl5BEg,3384
44
- monopyly/database/models.py,sha256=L0MwE_4BbUusC1nwgDbKO7AEyKHdkrZxGm54SoLiANs,14629
26
+ monopyly/config/default_settings.py,sha256=jtWz4ElasnAbfBCiRfbsYyy--L6N6jTOGRh3PNWnVj4,1829
27
+ monopyly/config/settings.py,sha256=YEctSb8mRDvuMA1tTs1N7862Z2bMY7DPf0xo4Ist2pg,1984
28
+ monopyly/core/actions.py,sha256=M886J8DrxMfpessVPwigbkkUfUo7au1mj1La-BfFTkw,1220
29
+ monopyly/core/blueprint.py,sha256=Kbl4M2Sdr4Sf2l9RPxCVFP6R_cBzYzdbIEG5ozUKGIc,252
30
+ monopyly/core/context_processors.py,sha256=NZktwVLUUvE-Zv25ZqTAjIWGWx74Bda4LeZPcDTflsE,821
31
+ monopyly/core/filters.py,sha256=9EQCHqLD3LEuyVkAE6_XCVjU1BezgAUsSWAS4PAzIb4,1253
32
+ monopyly/core/internal_transactions.py,sha256=PImeViMU9rdDDPLXld1xC6bdAoulzRDr0zci4pUQY8I,459
33
+ monopyly/core/routes.py,sha256=BSeqMFu5XSm2MuMBDHwPAaOiLeMjO3KsFYm5RySzS8c,2427
34
+ monopyly/credit/accounts.py,sha256=otQmuTDJZYX868EZl80n89XlzfiwlnBkfSXZa65rSqI,1898
35
+ monopyly/credit/actions.py,sha256=E80ocYxWKsNO1ynU3KQjVZkTwAGo4Fv7mYPmA68T9ng,6310
36
+ monopyly/credit/blueprint.py,sha256=XbrMhtyCp2732uWPB2kjn_W8P8pH8RVTwo9P8Pt--Is,251
37
+ monopyly/credit/cards.py,sha256=9Ug51aY2-G8SzooLC-FemRNeTBLd9JOpXHFW74EcdLU,5614
38
+ monopyly/credit/forms.py,sha256=-Oj7NxmDKNY39McbtqSC-v_Tppec0BfDZmeUnbnugCo,12250
39
+ monopyly/credit/routes.py,sha256=XIVuf6WEbwlDkNPi1RSyCxSgS3JbobikINBQpYb1r44,17794
40
+ monopyly/credit/statements.py,sha256=lRcAgcr9kPnh5GCE6McWfOdY8uCgmg2j2bikSioR3HI,7219
41
+ monopyly/credit/transactions/__init__.py,sha256=1Exn6T--HtwNCHzS_hDJ0ba7qCB7w_8C-yY3KSxvFKg,165
42
+ monopyly/credit/transactions/_transactions.py,sha256=LqbZV1Q2bb_B6tATmBihCPm8YhcbABRUCCYhNRg9qtk,8771
43
+ monopyly/database/__init__.py,sha256=tlZsXmzH8hB5m_RASbKTjPRqLel-xbymqrzo0TPoFCk,3402
44
+ monopyly/database/models.py,sha256=TqIwIe4tVv3TS0azz8Bm9nL1qT2RAyq292myPW0XH1A,15095
45
45
  monopyly/database/preloads.sql,sha256=KBS_WYRofFdOVN-IgHzZhfyJrY6ZOwHeExN-fQr3htU,363
46
46
  monopyly/database/schema.sql,sha256=cdl9b5CnYrnQ-lck147GtwArx_JJbX1vF9YYLivTeyw,4660
47
47
  monopyly/database/views.sql,sha256=UTO2QRkVTfQ12bMVkR-O6Qv80DvYo8hngPHn2A1_1F8,3853
48
48
  monopyly/static/jquery-3.7.0.min.js,sha256=2Pmvv0kuTBOenSvLm6bvfBSSHrUJ-3A7x6P5Ebd07_g,87462
49
- monopyly/static/css/style.css,sha256=TKU9Uzj7iEBNcTARinZ1fsTI4Y-qePHQAZdBKspSjkk,48860
49
+ monopyly/static/css/style.css,sha256=cBa9cFhr--fBgx7TaeegXLleUAGO2ui9TLSXmo5Sc7k,48930
50
50
  monopyly/static/favicon/browserconfig.xml,sha256=Zt_AVOxiritWWXoUwPsHpx4vu4kM_butdFVzoYCYbM8,315
51
51
  monopyly/static/favicon/favicon-114.png,sha256=kjElVFiix-kFCMdADkLpJsi8kL3GDsFm85oJhyCH-C0,20601
52
52
  monopyly/static/favicon/favicon-120.png,sha256=g4uzHMdW0MlJhcgWfrOsj2MB2D-HdLa4t60_hvyICkM,22077
@@ -137,27 +137,27 @@ monopyly/static/js/modules/manage-subforms.js,sha256=-yKA7l8ZI0294auTI307LrKkw_B
137
137
  monopyly/static/js/modules/update-database-widget.js,sha256=S67hmqaGwzbPy94IjYrag0ZPOur4r5y_tb3_5t7xuYI,1581
138
138
  monopyly/static/js/modules/update-display-ajax.js,sha256=MJBiRMmeIHRG7lLbqIcXkUecKNNMFFVJXwhs_N8LKl0,878
139
139
  monopyly/templates/credits.html,sha256=U2lOe7XrW1Rt9DCvo7pRxHDzRzBnYxHjX3cS5z36NI8,506
140
- monopyly/templates/index.html,sha256=czjm5IfP20tib0rlxde2TTKdr5u5zfjDLTrQAbbxJ88,4309
140
+ monopyly/templates/index.html,sha256=caoQuizzJibU90nivf1-Esnptg5wzFWyhVJoAkEPKdE,4374
141
141
  monopyly/templates/layout.html,sha256=ETQ7fNC4H0JjZZTnamZoJKwXbsXEHW72ZhahOER3NkQ,4741
142
142
  monopyly/templates/profile.html,sha256=qFU1lu5lbzC6p36OCcZGgRHcygmSqPpXDTLxaRDnLXU,2291
143
143
  monopyly/templates/story.html,sha256=fUvIE8t5oG_Mw443Oeu4vWKospFQv8dSfBOCisn5L0g,3207
144
- monopyly/templates/auth/login.html,sha256=CBEIxnDVrZ8dH3fgsYi5BVyesEUAvUGYQW7hW_gI8aE,441
145
- monopyly/templates/auth/register.html,sha256=eFSDl2vUiFil9VAB7uMuxNT872MSQugvVXCfXtVT5ac,445
144
+ monopyly/templates/auth/login.html,sha256=aZwaHBiKtQa_ZkVBqLUQIbwQdEAJNwcyYNjFQcRp-k0,443
145
+ monopyly/templates/auth/register.html,sha256=G6VxfYPIbXQco6Z_1PVQQ40vNWgG3PScmN20npx88yQ,447
146
146
  monopyly/templates/banking/account_page.html,sha256=pP7j6pRngqO8D_kQKbRINTQ61L5VJs66dsNPscO0czs,1946
147
- monopyly/templates/banking/account_summaries.html,sha256=ZO8vURwp-5PfVgwEN9wMVYfLnvv_hPnSJDvYcaPZ1uk,612
147
+ monopyly/templates/banking/account_summaries.html,sha256=h_grH5xF94Um8F2a8Ag1I9_bwstr7e6Lpx4Ak5ISeiQ,647
148
148
  monopyly/templates/banking/account_summaries_page.html,sha256=LuAAjd8U0LDxgJoBKAtVUw-b6KGni0Osre8O3gN17fY,1086
149
- monopyly/templates/banking/account_summary.html,sha256=45vsPptiN_ZLVFG54hib9alKJHArUWtIvgqS0foi2JE,655
149
+ monopyly/templates/banking/account_summary.html,sha256=6Ol5SdO5BC7Ptr2-M9WZPvWOeHRESrVjbieKTmA-3SE,675
150
150
  monopyly/templates/banking/accounts_page.html,sha256=JYI0cfaK6lxF-aDsYQLQkfEkx9xWdgAfW1xYbZRQmGs,2149
151
151
  monopyly/templates/banking/account_form/account_form.html,sha256=v8AH6DTBW-4XVffcmr-sRRBl1k0e-30Q7Vy5sXHkrPM,1226
152
152
  monopyly/templates/banking/account_form/account_form_page_new.html,sha256=aCahQlbJfIM_vzvHJb81dG_ORpXFmOw5QzYFNep7LxY,533
153
153
  monopyly/templates/banking/transaction_form/bank_info_form.html,sha256=RkEjEgbD5zw4QlvpJ6koA8TxE0ZRTGmfmCy8SC2RVd0,670
154
- monopyly/templates/banking/transaction_form/transaction_form.html,sha256=U2JtO7Ji7kgK9zeNfUm48nVSowjv0CxHjHTK45ZXiRw,1399
154
+ monopyly/templates/banking/transaction_form/transaction_form.html,sha256=M8Jqdl50GtVLuKxV1kLNuS3eMiaDo7rXQvwq3DpPv9Q,1399
155
155
  monopyly/templates/banking/transaction_form/transaction_form_page.html,sha256=q7pPXmqhpBXZ8jJKWENisqYXPpAZxVL1bYT0lIUUZA4,526
156
156
  monopyly/templates/banking/transaction_form/transaction_form_page_new.html,sha256=-PFAF3IcJXKRg2erlGFuvwD1XMx6JvndNWjdPJbmRtA,313
157
157
  monopyly/templates/banking/transaction_form/transaction_form_page_update.html,sha256=4PljOpO7wTNNEq-mhFzA-uHvUxzerfRMbDddWvSMzlo,350
158
158
  monopyly/templates/banking/transaction_form/transfer_form.html,sha256=ar3ttK7kgEWQpvnD23-ud8OcLPgtvGDrKRIp4UWGoQw,168
159
159
  monopyly/templates/banking/transactions_table/condensed_row_content.html,sha256=5eHwrdoGxNouRWSPe-NxTtvcpiZafw1WPSR_d_O3YTc,416
160
- monopyly/templates/banking/transactions_table/expanded_row_content.html,sha256=33FtqbSZH7nx0cOCf8-PnLZ1ppxHPtQoJ-SuU5Qe3qw,1280
160
+ monopyly/templates/banking/transactions_table/expanded_row_content.html,sha256=NtOiMMm-6n8dBTa7JAJh-JqMV7aRooK44Aj6lIx1avQ,1278
161
161
  monopyly/templates/banking/transactions_table/transaction_field_titles.html,sha256=WvjcRAByg1xSHqfNEkcJYB1eFnNfm4-qfHZben_YApI,600
162
162
  monopyly/templates/banking/transactions_table/transactions.html,sha256=emId66z4LXyOF1xdiJIAwrGqE4WSY-iv03yZF4kye1k,486
163
163
  monopyly/templates/common/form_page.html,sha256=Sx2Y6dhNsjkDQBaepTBPBvVeIe7N730llhgbcAQOrCk,107
@@ -176,7 +176,7 @@ monopyly/templates/credit/card_submission_page.html,sha256=7OU6PB01jACSKSzXGySWL
176
176
  monopyly/templates/credit/cards.html,sha256=lvuKip2nHDmPi2Wj8zmO9ZXKXY22Dlv9fDrtHuYCSqA,598
177
177
  monopyly/templates/credit/cards_page.html,sha256=HTrTQwF-Y5Z9oWqt72i6HbAaRhQibTEt3TJKavm6_2M,250
178
178
  monopyly/templates/credit/statement_page.html,sha256=vYuahNkkF4M2pWAJaaraIZq70EmMgpvAJcUMF1Pzhko,2009
179
- monopyly/templates/credit/statement_summary.html,sha256=AbRNlb6bhALpD7Cu7HgopeDjQ2VVHY3Lk2DcWL7cW1o,2601
179
+ monopyly/templates/credit/statement_summary.html,sha256=V8J6QbO2K6VW8HiePWQbfo19vC4jPdUv9dRTBjiJ9nk,2623
180
180
  monopyly/templates/credit/statements.html,sha256=2A65crmxXMwW7FQdJEYuBv32QJ8T9lF1jayKzmJX2zA,1731
181
181
  monopyly/templates/credit/statements_page.html,sha256=s_CSiRO8iTcCoEPVRF2TEOG2XNOeuGF1BSiJGhrH5O0,1458
182
182
  monopyly/templates/credit/tags_page.html,sha256=mKm-QxuicO5MO12JOQy7tuc8Bd9fnnKiTSHDmMP5mTI,662
@@ -185,7 +185,7 @@ monopyly/templates/credit/transactions_page.html,sha256=4Tx2yUGPUIRfz0M3vAQqjcfH
185
185
  monopyly/templates/credit/card_form/card_form.html,sha256=xF7x1RBQyAfKYuW_cRxwhKhug4tum-3s0YHyP0ovhFs,1666
186
186
  monopyly/templates/credit/card_form/card_form_page_new.html,sha256=9ayX0dUEAbSSPVL3vIKIBJYkN8odL_KHOfq3fl6C8yw,525
187
187
  monopyly/templates/credit/card_form/transfer_statement_inquiry.html,sha256=LuxietZsTLF3AMqeeHqcPoAYvJX_TsJ8MlDldpZwpoQ,1228
188
- monopyly/templates/credit/card_graphic/card_back.html,sha256=AbpxlQVdz-8ryHSMhY2Uj153YFMbQPTO4XsmLvxUt2A,888
188
+ monopyly/templates/credit/card_graphic/card_back.html,sha256=gfr3975mziF0PCd_f2Jy83Y4ktFeFnZ1icVVpiVwoQA,889
189
189
  monopyly/templates/credit/card_graphic/card_front.html,sha256=I53u0WvrQj-m2D-fkcga0GRketfpMuMXNM6Rs0e5CNA,679
190
190
  monopyly/templates/credit/tag_tree/subtag_tree.html,sha256=wMt4BbLIxkioQiWmdIcEVD1xz21uuXtSB08VxJUPB2I,580
191
191
  monopyly/templates/credit/tag_tree/tag_tree.html,sha256=cAsYpZWfguZWivALn4hE8fRVD6YsSvmIzRg2g5RSJTg,345
@@ -197,9 +197,9 @@ monopyly/templates/credit/transactions_table/condensed_row_content.html,sha256=T
197
197
  monopyly/templates/credit/transactions_table/expanded_row_content.html,sha256=oL1BqlEC3H6s3fSI9Vb65DLL-P9OPliPqqiGL4av4Xs,1971
198
198
  monopyly/templates/credit/transactions_table/transaction_field_titles.html,sha256=km-3YEDJaygwcKV-rwYnrE7xF8u4Z7jCBsk0Ia-LO-M,758
199
199
  monopyly/templates/credit/transactions_table/transactions.html,sha256=tcppv0qNV-Qq6U5jfxoNyGKPXmyV9h9nR6rw4jXfBsY,481
200
- monopyly-1.4.4.dist-info/METADATA,sha256=E3Z546zAtfic-yr6-q9tWxbJZRTBV2cdBGn4IuDGS6Y,10208
201
- monopyly-1.4.4.dist-info/WHEEL,sha256=Fd6mP6ydyRguakwUJ05oBE7fh2IPxgtDN9IwHJ9OqJQ,87
202
- monopyly-1.4.4.dist-info/entry_points.txt,sha256=ANAwWNF6IG83opRITM2P9i_im_b6qLdG1n7dAcS2ZNU,51
203
- monopyly-1.4.4.dist-info/licenses/COPYING,sha256=5X8cMguM-HmKfS_4Om-eBqM6A1hfbgZf6pfx2G24QFI,35150
204
- monopyly-1.4.4.dist-info/licenses/LICENSE,sha256=94rIicMccmTPhqXiRLV9JsU8P2ocMuEUUtUpp6LPKiE,253
205
- monopyly-1.4.4.dist-info/RECORD,,
200
+ monopyly-1.4.6.dist-info/METADATA,sha256=0GNEa8OFRutu8kofK7ykznrD9cEBpfxmUOdkyjyRjD4,10206
201
+ monopyly-1.4.6.dist-info/WHEEL,sha256=Fd6mP6ydyRguakwUJ05oBE7fh2IPxgtDN9IwHJ9OqJQ,87
202
+ monopyly-1.4.6.dist-info/entry_points.txt,sha256=ANAwWNF6IG83opRITM2P9i_im_b6qLdG1n7dAcS2ZNU,51
203
+ monopyly-1.4.6.dist-info/licenses/COPYING,sha256=5X8cMguM-HmKfS_4Om-eBqM6A1hfbgZf6pfx2G24QFI,35150
204
+ monopyly-1.4.6.dist-info/licenses/LICENSE,sha256=94rIicMccmTPhqXiRLV9JsU8P2ocMuEUUtUpp6LPKiE,253
205
+ monopyly-1.4.6.dist-info/RECORD,,