monopyly 1.5.2__tar.gz → 1.6.1__tar.gz
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.
- {monopyly-1.5.2 → monopyly-1.6.1}/PKG-INFO +8 -8
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/CHANGELOG.md +22 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/README.md +1 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/__init__.py +1 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/_version.py +2 -2
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/auth/blueprint.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/auth/routes.py +1 -2
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/accounts.py +4 -4
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/actions.py +20 -17
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/blueprint.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/filters.py +6 -6
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/forms.py +7 -5
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/routes.py +71 -9
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/transactions.py +2 -3
- monopyly-1.6.1/monopyly/common/forms/__init__.py +15 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/forms/_forms.py +6 -16
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/forms/fields.py +0 -11
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/forms/utils.py +4 -4
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/transactions.py +72 -7
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/actions.py +2 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/blueprint.py +2 -0
- monopyly-1.6.1/monopyly/core/context_processors.py +23 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/filters.py +0 -2
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/routes.py +1 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/actions.py +4 -5
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/blueprint.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/cards.py +1 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/forms.py +9 -7
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/routes.py +37 -62
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/__init__.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/_transactions.py +1 -4
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/activity/__init__.py +6 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/activity/parser.py +0 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/activity/reconciliation.py +5 -3
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/__init__.py +0 -3
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/models.py +63 -49
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/preloads.sql +6 -1
- monopyly-1.6.1/monopyly/scripts/screenshot_application.py +100 -0
- monopyly-1.6.1/monopyly/static/chartist-1.5.0.min.js +8 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/css/style.css +39 -14
- monopyly-1.6.1/monopyly/static/img/about/bank-account-details.png +0 -0
- monopyly-1.6.1/monopyly/static/img/about/bank-account-summaries.png +0 -0
- monopyly-1.6.1/monopyly/static/img/about/bank-accounts.png +0 -0
- monopyly-1.6.1/monopyly/static/img/about/credit-account-details.png +0 -0
- monopyly-1.6.1/monopyly/static/img/about/credit-statement-details.png +0 -0
- monopyly-1.6.1/monopyly/static/img/about/credit-transactions.png +0 -0
- monopyly-1.6.1/monopyly/static/img/about/homepage-user.png +0 -0
- monopyly-1.6.1/monopyly/static/img/about/homepage.png +0 -0
- monopyly-1.6.1/monopyly/static/jquery-3.7.1.min.js +2 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/add-transfer.js +8 -9
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/bind-tag-actions.js +6 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/create-balance-chart.js +1 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/create-category-chart.js +1 -1
- monopyly-1.6.1/monopyly/static/js/load-more-transactions.js +27 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/expand-transaction.js +7 -6
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/update-display-ajax.js +20 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-transactions-display.js +8 -2
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_form/account_form_page_new.html +10 -8
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_page.html +32 -33
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_summaries.html +2 -2
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_summary.html +1 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/accounts_page.html +10 -8
- monopyly-1.6.1/monopyly/templates/banking/transactions_table/table.html +3 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transactions_table/transactions.html +0 -1
- monopyly-1.6.1/monopyly/templates/common/tag_tree.html +25 -0
- {monopyly-1.5.2/monopyly/templates/credit → monopyly-1.6.1/monopyly/templates/common}/tags_page.html +17 -13
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/linked_bank_transaction.html +2 -2
- monopyly-1.6.1/monopyly/templates/common/transactions_table/table.html +6 -0
- monopyly-1.6.1/monopyly/templates/common/transactions_table/transactions.html +12 -0
- monopyly-1.6.1/monopyly/templates/core/index.html +184 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/profile.html +19 -20
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/account_page.html +17 -17
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_form/card_form_page_new.html +8 -5
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_page.html +45 -46
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html +22 -22
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_summary.html +2 -2
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statements_page.html +13 -13
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_submission_page.html +3 -3
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_page.html +38 -22
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_table/condensed_row_content.html +2 -2
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_table/expanded_row_content.html +5 -5
- monopyly-1.6.1/monopyly/templates/credit/transactions_table/table.html +3 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_table/transactions.html +0 -1
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/layout.html +16 -9
- {monopyly-1.5.2 → monopyly-1.6.1}/pyproject.toml +29 -16
- monopyly-1.5.2/monopyly/common/forms/__init__.py +0 -7
- monopyly-1.5.2/monopyly/core/context_processors.py +0 -41
- monopyly-1.5.2/monopyly/static/img/about/bank-account-details.png +0 -0
- monopyly-1.5.2/monopyly/static/img/about/bank-account-summaries.png +0 -0
- monopyly-1.5.2/monopyly/static/img/about/bank-accounts.png +0 -0
- monopyly-1.5.2/monopyly/static/img/about/credit-account-details.png +0 -0
- monopyly-1.5.2/monopyly/static/img/about/credit-transactions.png +0 -0
- monopyly-1.5.2/monopyly/static/img/about/homepage-user.png +0 -0
- monopyly-1.5.2/monopyly/static/img/about/homepage.png +0 -0
- monopyly-1.5.2/monopyly/static/jquery-3.7.0.min.js +0 -2
- monopyly-1.5.2/monopyly/templates/common/transactions_table/transactions.html +0 -18
- monopyly-1.5.2/monopyly/templates/core/index.html +0 -169
- monopyly-1.5.2/monopyly/templates/credit/tag_tree/subtag_tree.html +0 -22
- monopyly-1.5.2/monopyly/templates/credit/tag_tree/tag_tree.html +0 -13
- {monopyly-1.5.2 → monopyly-1.6.1}/.gitignore +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/COPYING +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/LICENSE +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/README.md +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/auth/actions.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/auth/tools.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/banks.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/forms/validators.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/utils.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/errors.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/internal_transactions.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/accounts.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/statements.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/activity/data.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/schema.sql +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/views.sql +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/browserconfig.xml +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-114.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-120.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-144.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-150.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-152.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-16.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-160.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-180.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-192.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-310.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-32.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-57.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-60.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-64.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-70.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-72.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-76.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-96.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon.ico +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/about/statement-details.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/cards/chase-card.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/cards/discover-card.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/cards/new-card.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/cards/template-card.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/arrow-down.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/arrow-left.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/arrow-up.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/checkmark.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/delete-orange.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/delete-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/delete.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/edit.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/link.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/minus-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/minus.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/plus-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/plus.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/refresh.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/save.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/sort-asc.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/sort-desc.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/statement-pair.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/statement-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/statement.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/x-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/statement.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/add-subtransaction.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/autocomplete-transaction.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/define-filter.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/display-new-bank-inputs.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/expand-bank-account.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/expand-bank.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/expand-transaction.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/flip-card.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/hide-homepage-block.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/highlight-discrepant-transactions.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/infer-card.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/infer-statement.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/make-payment.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/ajax.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/autocomplete-input.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/expand-box-row.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/form-suggestions.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/manage-overlays.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/manage-subforms.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/update-database-widget.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/show-credit-activity-loader.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/show-linked-transaction.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/toggle-navigation.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-account-statement-parameters.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-bank-name.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-card-status.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-statement-parameters.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-statements-display.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/use-suggested-amount.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/use-suggested-merchant.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/auth/change_password.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/auth/login.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/auth/register.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_form/account_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_summaries_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transaction_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/form_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transaction_form/subform.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/credits.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/400.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/401.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/403.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/404.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/405.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/408.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/418.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/425.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/500.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/error.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/story.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_form/card_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_graphic/card_back.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_submission_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/cards.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/cards_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/discrepant_records.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/summary.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/unrecorded_activities.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statements.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +9 -9
- {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_table/transaction_field_titles.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: monopyly
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.1
|
|
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
|
|
@@ -22,16 +22,16 @@ Classifier: Programming Language :: Python
|
|
|
22
22
|
Classifier: Topic :: Office/Business :: Financial
|
|
23
23
|
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
24
24
|
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
25
|
-
Requires-Python:
|
|
26
|
-
Requires-Dist: dry-foundation==1.
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: dry-foundation==1.6.0
|
|
27
27
|
Requires-Dist: flask-wtf==1.2.2
|
|
28
28
|
Requires-Dist: flask==3.1.2
|
|
29
29
|
Requires-Dist: gunicorn==23.0.0
|
|
30
|
-
Requires-Dist: markdown==3.
|
|
31
|
-
Requires-Dist: nltk==3.9.
|
|
30
|
+
Requires-Dist: markdown==3.10
|
|
31
|
+
Requires-Dist: nltk==3.9.2
|
|
32
32
|
Requires-Dist: python-dateutil==2.9.0
|
|
33
|
-
Requires-Dist: rich==14.
|
|
34
|
-
Requires-Dist: sqlalchemy==2.0.
|
|
33
|
+
Requires-Dist: rich==14.2.0
|
|
34
|
+
Requires-Dist: sqlalchemy==2.0.45
|
|
35
35
|
Description-Content-Type: text/markdown
|
|
36
36
|
|
|
37
37
|
<div id="header">
|
|
@@ -159,7 +159,7 @@ Card balances are also visible by visiting the pages for individual statements.
|
|
|
159
159
|
A full history of statements for each card is available off the homepage.
|
|
160
160
|
Each statement's page gives the statement's balance, transactions, and due date.
|
|
161
161
|
|
|
162
|
-
<img class="screenshot" src="https://raw.githubusercontent.com/mitchnegus/monopyly/main/monopyly/static/img/about/statement-details.png" alt="statement details" width="800px" />
|
|
162
|
+
<img class="screenshot" src="https://raw.githubusercontent.com/mitchnegus/monopyly/main/monopyly/static/img/about/credit-statement-details.png" alt="statement details" width="800px" />
|
|
163
163
|
|
|
164
164
|
Payments can be made directly from a statement's page and can be linked to a bank account in the _Monopyly_ system for simplified tracking.
|
|
165
165
|
(Note that even linked transactions must be edited independently, as there are times when a user may wish to have separate values for linked transactions. For example, a credit card payment may be processed on a given date while it is only registered as a bank account transaction several days later.)
|
|
@@ -222,4 +222,26 @@
|
|
|
222
222
|
- Bump dependencies (including support for recent SQLAlchemy versions)
|
|
223
223
|
|
|
224
224
|
|
|
225
|
+
### 1.6.0
|
|
226
|
+
|
|
227
|
+
- Enable additional transactions to be loaded on bank/credit transactions tables
|
|
228
|
+
- Create a script to take application screenshots (e.g., for the 'About' page)
|
|
229
|
+
- Remove Python version requirement (allow Python versions after 3.10)
|
|
230
|
+
- Use Jinja recursive loops for transaction tag tree structures in templates
|
|
231
|
+
- Refactor to include ruff-based linting checks
|
|
232
|
+
- Set 'Credit payments' to be a default tag for all users
|
|
233
|
+
- Protect globally defined tags from user deletion
|
|
234
|
+
- Hide transaction tags when the Escape key is pressed
|
|
235
|
+
- Ensure that the 'Record Transfer' functionality only ever adds one input box
|
|
236
|
+
- Update dependencies (including using ruff in place of _Black_ and _isort_)
|
|
237
|
+
- Update JavaScript libraries (jQuery, Chartist)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
### 1.6.1
|
|
241
|
+
|
|
242
|
+
- Allow pie chart labels to overflow the SVG container
|
|
243
|
+
- Define JavaScript elements in header (deferring execution when logical)
|
|
244
|
+
- Bump dependencies
|
|
245
|
+
|
|
246
|
+
|
|
225
247
|
<a name="bottom" id="bottom"></a>
|
|
@@ -123,7 +123,7 @@ Card balances are also visible by visiting the pages for individual statements.
|
|
|
123
123
|
A full history of statements for each card is available off the homepage.
|
|
124
124
|
Each statement's page gives the statement's balance, transactions, and due date.
|
|
125
125
|
|
|
126
|
-
<img class="screenshot" src="monopyly/static/img/about/statement-details.png" alt="statement details" width="800px" />
|
|
126
|
+
<img class="screenshot" src="monopyly/static/img/about/credit-statement-details.png" alt="statement details" width="800px" />
|
|
127
127
|
|
|
128
128
|
Payments can be made directly from a statement's page and can be linked to a bank account in the _Monopyly_ system for simplified tracking.
|
|
129
129
|
(Note that even linked transactions must be edited independently, as there are times when a user may wish to have separate values for linked transactions. For example, a credit card payment may be processed on a given date while it is only registered as a bank account transaction several days later.)
|
|
@@ -13,7 +13,6 @@ from flask import (
|
|
|
13
13
|
session,
|
|
14
14
|
url_for,
|
|
15
15
|
)
|
|
16
|
-
from sqlalchemy import select
|
|
17
16
|
from werkzeug.security import check_password_hash, generate_password_hash
|
|
18
17
|
|
|
19
18
|
from ..database.models import User
|
|
@@ -34,7 +33,7 @@ def register():
|
|
|
34
33
|
error = "Username is required."
|
|
35
34
|
elif not password:
|
|
36
35
|
error = "Password is required."
|
|
37
|
-
elif
|
|
36
|
+
elif identify_user(username):
|
|
38
37
|
error = f"User {username} is already registered."
|
|
39
38
|
else:
|
|
40
39
|
# Create a new user
|
|
@@ -4,7 +4,7 @@ Tools for interacting with bank accounts in the database.
|
|
|
4
4
|
|
|
5
5
|
import sqlalchemy.sql.functions as sql_func
|
|
6
6
|
from dry_foundation.database.handler import DatabaseViewHandler
|
|
7
|
-
from
|
|
7
|
+
from flask import abort
|
|
8
8
|
|
|
9
9
|
from ..common.forms.utils import execute_on_form_validation
|
|
10
10
|
from ..database.models import (
|
|
@@ -114,15 +114,15 @@ class BankAccountTypeHandler(
|
|
|
114
114
|
super().delete_entry(entry_id)
|
|
115
115
|
|
|
116
116
|
@classmethod
|
|
117
|
-
def
|
|
118
|
-
account_type = super().
|
|
119
|
-
# Limit manipulation to only the user (excluding common entries)
|
|
117
|
+
def _retrieve_authorized_manipulable_entry(cls, entry_id):
|
|
118
|
+
account_type = super()._retrieve_authorized_manipulable_entry(entry_id)
|
|
120
119
|
if account_type.user_id != cls.user_id:
|
|
121
120
|
abort_msg = (
|
|
122
121
|
"The current user is not authorized to manipulate "
|
|
123
122
|
"this account type entry."
|
|
124
123
|
)
|
|
125
124
|
abort(403, abort_msg)
|
|
125
|
+
return account_type
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
class BankAccountHandler(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Module describing logical banking actions (to be used in routes)."""
|
|
2
2
|
|
|
3
|
-
from collections import
|
|
3
|
+
from collections import UserDict, namedtuple
|
|
4
4
|
|
|
5
5
|
from ..common.utils import convert_date_to_midnight_timestamp
|
|
6
6
|
from .accounts import BankAccountHandler, BankAccountTypeHandler
|
|
@@ -30,25 +30,26 @@ def get_balance_chart_data(transactions):
|
|
|
30
30
|
|
|
31
31
|
Returns
|
|
32
32
|
-------
|
|
33
|
-
chart_data :
|
|
34
|
-
A
|
|
33
|
+
chart_data : dict
|
|
34
|
+
A dictionary containing a Chartist compatible data structure,
|
|
35
|
+
including (x, y) pairs that each represent the Unix
|
|
35
36
|
timestamp (in milliseconds) and the bank account balance.
|
|
36
37
|
"""
|
|
37
|
-
return
|
|
38
|
+
return _BalanceChartData(transactions).data
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
class _BalanceChartData(
|
|
41
|
+
class _BalanceChartData(UserDict):
|
|
41
42
|
"""
|
|
42
|
-
A
|
|
43
|
+
A mapping of balances to be passed to a `chartist.js` chart constructor.
|
|
43
44
|
|
|
44
|
-
A special
|
|
45
|
-
use in a balance chart created by the `chartist.js`
|
|
46
|
-
converts each transaction into an (x, y) pair
|
|
47
|
-
timestamp (in milleseconds) and a corresponding
|
|
48
|
-
balance. For transactions occurring on the same day
|
|
49
|
-
granularity recorded by the Monopyly app), a slight
|
|
50
|
-
added to each timestamp to guarantee a smooth
|
|
51
|
-
rendered chart.
|
|
45
|
+
A special dictionary-like object containing transaction data
|
|
46
|
+
formatted for use in a balance chart created by the `chartist.js`
|
|
47
|
+
library. This converts each transaction into an (x, y) pair
|
|
48
|
+
consisting of a Unix timestamp (in milleseconds) and a corresponding
|
|
49
|
+
bank account balance. For transactions occurring on the same day
|
|
50
|
+
(the finest granularity recorded by the Monopyly app), a slight
|
|
51
|
+
offset is added to each timestamp to guarantee a smooth
|
|
52
|
+
representation in the rendered chart.
|
|
52
53
|
|
|
53
54
|
Parameters
|
|
54
55
|
----------
|
|
@@ -61,9 +62,9 @@ class _BalanceChartData(UserList):
|
|
|
61
62
|
point = namedtuple("DataPoint", ["timestamp", "balance"])
|
|
62
63
|
|
|
63
64
|
def __init__(self, transactions):
|
|
64
|
-
super().__init__()
|
|
65
65
|
transaction_groups = self._group_transactions_by_date(transactions)
|
|
66
|
-
self._prepare_chart_data(transaction_groups)
|
|
66
|
+
chart_data = self._prepare_chart_data(transaction_groups)
|
|
67
|
+
super().__init__({"series": [{"name": "balances", "data": chart_data}]})
|
|
67
68
|
|
|
68
69
|
@staticmethod
|
|
69
70
|
def _group_transactions_by_date(transactions):
|
|
@@ -75,6 +76,7 @@ class _BalanceChartData(UserList):
|
|
|
75
76
|
|
|
76
77
|
def _prepare_chart_data(self, transaction_groups):
|
|
77
78
|
# Assign chart data to the list as tuples, adding offsets for duplicated dates
|
|
79
|
+
chart_data = []
|
|
78
80
|
for transaction_date, transaction_group in transaction_groups.items():
|
|
79
81
|
base_timestamp = convert_date_to_midnight_timestamp(
|
|
80
82
|
transaction_date, milliseconds=True
|
|
@@ -82,4 +84,5 @@ class _BalanceChartData(UserList):
|
|
|
82
84
|
offset = self._DAILY_MILLISECONDS / len(transaction_group)
|
|
83
85
|
for i, transaction in enumerate(transaction_group):
|
|
84
86
|
adjusted_timestamp = base_timestamp + (i * offset)
|
|
85
|
-
|
|
87
|
+
chart_data.append({"x": adjusted_timestamp, "y": transaction.balance})
|
|
88
|
+
return chart_data
|
|
@@ -6,14 +6,14 @@ from .blueprint import bp
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
@bp.app_template_filter("is_single_bank_transfer")
|
|
9
|
-
def check_transfer_is_within_bank(
|
|
10
|
-
"""Check if the transfer is linked
|
|
11
|
-
if
|
|
12
|
-
linked_bank_transactions =
|
|
9
|
+
def check_transfer_is_within_bank(transaction_view):
|
|
10
|
+
"""Check if the transfer is linked to another transaction at the same bank."""
|
|
11
|
+
if internal_transaction := transaction_view.internal_transaction:
|
|
12
|
+
linked_bank_transactions = internal_transaction.bank_transaction_views
|
|
13
13
|
if len(linked_bank_transactions) > 1:
|
|
14
|
-
common_bank_id = linked_bank_transactions[0].
|
|
14
|
+
common_bank_id = linked_bank_transactions[0].account_view.bank_id
|
|
15
15
|
return all(
|
|
16
|
-
transaction.
|
|
16
|
+
transaction.account_view.bank_id == common_bank_id
|
|
17
17
|
for transaction in linked_bank_transactions
|
|
18
18
|
)
|
|
19
19
|
return False
|
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
Generate banking forms for the user to complete.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from wtforms.fields import BooleanField, FieldList, FormField,
|
|
5
|
+
from wtforms.fields import BooleanField, FieldList, FormField, SubmitField
|
|
6
6
|
from wtforms.validators import DataRequired, Optional
|
|
7
7
|
|
|
8
8
|
from ..common.forms import AcquisitionSubform, EntryForm, EntrySubform, TransactionForm
|
|
9
9
|
from ..common.forms.fields import (
|
|
10
10
|
CustomChoiceSelectField,
|
|
11
|
-
DateField,
|
|
12
11
|
LastFourDigitsField,
|
|
13
12
|
StringField,
|
|
14
13
|
)
|
|
15
14
|
from ..common.forms.utils import Autocompleter
|
|
16
|
-
from ..common.utils import parse_date
|
|
17
15
|
from ..database.models import (
|
|
18
16
|
Bank,
|
|
19
17
|
BankAccountTypeView,
|
|
@@ -117,6 +115,10 @@ class BankAccountForm(EntryForm):
|
|
|
117
115
|
}
|
|
118
116
|
return account_type_data
|
|
119
117
|
|
|
118
|
+
def gather_entry_data(self, entry):
|
|
119
|
+
"""Gather data for the form from the given database entry."""
|
|
120
|
+
raise NotImplementedError
|
|
121
|
+
|
|
120
122
|
# Fields to identify the bank/account type information for the account
|
|
121
123
|
bank_info = FormField(BankSubform)
|
|
122
124
|
account_type_info = FormField(AccountTypeSubform)
|
|
@@ -179,7 +181,7 @@ class BankTransactionForm(TransactionForm):
|
|
|
179
181
|
data = {
|
|
180
182
|
"bank_name": entry.bank.bank_name,
|
|
181
183
|
"last_four_digits": entry.last_four_digits,
|
|
182
|
-
"type_name": entry.
|
|
184
|
+
"type_name": entry.account_type_view.type_name,
|
|
183
185
|
}
|
|
184
186
|
elif isinstance(entry, Bank):
|
|
185
187
|
data = {"bank_name": entry.bank_name}
|
|
@@ -278,7 +280,7 @@ class BankTransactionForm(TransactionForm):
|
|
|
278
280
|
"""Gather data for the form from the given database entry."""
|
|
279
281
|
if isinstance(entry, BankTransactionView):
|
|
280
282
|
data = self._gather_transaction_data(entry)
|
|
281
|
-
account_info = entry.
|
|
283
|
+
account_info = entry.account_view
|
|
282
284
|
# Do not prepopulate any transfer information
|
|
283
285
|
elif isinstance(entry, (BankAccountView, Bank)):
|
|
284
286
|
data = {}
|
|
@@ -3,7 +3,7 @@ Routes for banking financials.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from dry_foundation.database import db_transaction
|
|
6
|
-
from flask import jsonify, redirect, render_template, request, url_for
|
|
6
|
+
from flask import g, jsonify, redirect, render_template, request, url_for
|
|
7
7
|
|
|
8
8
|
from ..auth.tools import login_required
|
|
9
9
|
from ..common.forms.utils import extend_field_list_for_ajax
|
|
@@ -13,7 +13,10 @@ from .actions import get_balance_chart_data, get_bank_account_type_grouping
|
|
|
13
13
|
from .banks import BankHandler
|
|
14
14
|
from .blueprint import bp
|
|
15
15
|
from .forms import BankAccountForm, BankTransactionForm
|
|
16
|
-
from .transactions import BankTransactionHandler, save_transaction
|
|
16
|
+
from .transactions import BankTagHandler, BankTransactionHandler, save_transaction
|
|
17
|
+
|
|
18
|
+
# Set a limit on the number of transactions loaded at one time for certain routes
|
|
19
|
+
TRANSACTION_LIMIT = 100
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
@bp.route("/accounts")
|
|
@@ -34,7 +37,7 @@ def add_account(bank_id):
|
|
|
34
37
|
form = BankAccountForm()
|
|
35
38
|
# Check if an account was submitted and add it to the database
|
|
36
39
|
if request.method == "POST":
|
|
37
|
-
|
|
40
|
+
save_account(form)
|
|
38
41
|
return redirect(url_for("banking.load_accounts"))
|
|
39
42
|
else:
|
|
40
43
|
if bank_id:
|
|
@@ -69,21 +72,39 @@ def load_account_summaries(bank_id):
|
|
|
69
72
|
@login_required
|
|
70
73
|
def load_account_details(account_id):
|
|
71
74
|
account = BankAccountHandler.get_entry(account_id)
|
|
72
|
-
transactions =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
76
|
-
)
|
|
75
|
+
transactions = BankTransactionHandler.get_transactions(
|
|
76
|
+
account_ids=(account_id,), sort_order="DESC"
|
|
77
|
+
).all()
|
|
77
78
|
# Only display the first 100 transactions
|
|
78
79
|
return render_template(
|
|
79
80
|
"banking/account_page.html",
|
|
80
81
|
account=account,
|
|
81
|
-
|
|
82
|
+
transactions=transactions[:100],
|
|
83
|
+
total_transactions=len(transactions),
|
|
82
84
|
# Reverse the chart transactions to be chronologically ascending
|
|
83
85
|
chart_data=get_balance_chart_data(reversed(transactions)),
|
|
84
86
|
)
|
|
85
87
|
|
|
86
88
|
|
|
89
|
+
@bp.route("/_extra_transactions", methods=("POST",))
|
|
90
|
+
@login_required
|
|
91
|
+
def load_more_transactions():
|
|
92
|
+
# Get info about the transactions being displayed from the AJAX request
|
|
93
|
+
post_args = request.get_json()
|
|
94
|
+
account_id = post_args["account_id"]
|
|
95
|
+
block_index = post_args["block_count"] - 1
|
|
96
|
+
# Get a subset of the remaining transactions to load
|
|
97
|
+
more_transactions = BankTransactionHandler.get_transactions(
|
|
98
|
+
account_ids=(account_id,),
|
|
99
|
+
offset=block_index * TRANSACTION_LIMIT,
|
|
100
|
+
limit=TRANSACTION_LIMIT,
|
|
101
|
+
)
|
|
102
|
+
return render_template(
|
|
103
|
+
"banking/transactions_table/transactions.html",
|
|
104
|
+
transactions=more_transactions,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
87
108
|
@bp.route("/_expand_transaction", methods=("POST",))
|
|
88
109
|
@login_required
|
|
89
110
|
def expand_transaction():
|
|
@@ -216,6 +237,47 @@ def delete_transaction(transaction_id):
|
|
|
216
237
|
return redirect(url_for("banking.load_account_details", account_id=account_id))
|
|
217
238
|
|
|
218
239
|
|
|
240
|
+
@bp.route("/tags")
|
|
241
|
+
@login_required
|
|
242
|
+
def load_tags():
|
|
243
|
+
# Get the tag hierarchy from the database
|
|
244
|
+
hierarchy = BankTagHandler.get_hierarchy()
|
|
245
|
+
return render_template("common/tags_page.html", tags_hierarchy=hierarchy)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@bp.route("/_add_tag", methods=("POST",))
|
|
249
|
+
@login_required
|
|
250
|
+
@db_transaction
|
|
251
|
+
def add_tag():
|
|
252
|
+
# Get the new tag (and potentially parent category) from the AJAX request
|
|
253
|
+
post_args = request.get_json()
|
|
254
|
+
tag_name = post_args["tag_name"]
|
|
255
|
+
parent_name = post_args.get("parent")
|
|
256
|
+
# Check that the tag name does not already exist
|
|
257
|
+
if BankTagHandler.get_tags(tag_names=(tag_name,)):
|
|
258
|
+
raise ValueError("The given tag name already exists. Tag names must be unique.")
|
|
259
|
+
parent_id = BankTagHandler.find_tag(parent_name).id if parent_name else None
|
|
260
|
+
tag = BankTagHandler.add_entry(
|
|
261
|
+
parent_id=parent_id,
|
|
262
|
+
user_id=g.user.id,
|
|
263
|
+
tag_name=tag_name,
|
|
264
|
+
)
|
|
265
|
+
return render_template("common/tag_tree.html", tags_hierarchy={tag: []})
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@bp.route("/_delete_tag", methods=("POST",))
|
|
269
|
+
@login_required
|
|
270
|
+
@db_transaction
|
|
271
|
+
def delete_tag():
|
|
272
|
+
# Get the tag to be deleted from the AJAX request
|
|
273
|
+
post_args = request.get_json()
|
|
274
|
+
tag_name = post_args["tag_name"]
|
|
275
|
+
tag = BankTagHandler.find_tag(tag_name)
|
|
276
|
+
# Remove the tag from the database
|
|
277
|
+
BankTagHandler.delete_entry(tag.id)
|
|
278
|
+
return ""
|
|
279
|
+
|
|
280
|
+
|
|
219
281
|
@bp.route("/_suggest_transaction_autocomplete", methods=("POST",))
|
|
220
282
|
@login_required
|
|
221
283
|
def suggest_transaction_autocomplete():
|
|
@@ -8,7 +8,6 @@ from ..common.forms.utils import execute_on_form_validation
|
|
|
8
8
|
from ..common.transactions import TransactionHandler, TransactionTagHandler
|
|
9
9
|
from ..core.internal_transactions import add_internal_transaction
|
|
10
10
|
from ..database.models import (
|
|
11
|
-
Bank,
|
|
12
11
|
BankAccountView,
|
|
13
12
|
BankSubtransaction,
|
|
14
13
|
BankTransaction,
|
|
@@ -80,7 +79,7 @@ class BankTransactionHandler(
|
|
|
80
79
|
criteria.add_match_filter(cls.model, "account_id", account_ids)
|
|
81
80
|
criteria.add_match_filter(BankAccountView, "active", active)
|
|
82
81
|
transactions = super()._get_transactions(
|
|
83
|
-
criteria=criteria, sort_order=sort_order, offset=
|
|
82
|
+
criteria=criteria, sort_order=sort_order, offset=offset, limit=limit
|
|
84
83
|
)
|
|
85
84
|
return transactions
|
|
86
85
|
|
|
@@ -214,7 +213,7 @@ def save_transaction(form, transaction_id=None):
|
|
|
214
213
|
transfer = record_new_transfer(transfer_data)
|
|
215
214
|
transaction_data.update(
|
|
216
215
|
internal_transaction_id=transfer.internal_transaction_id,
|
|
217
|
-
merchant=transfer.
|
|
216
|
+
merchant=transfer.account_view.bank.bank_name,
|
|
218
217
|
)
|
|
219
218
|
transaction = BankTransactionHandler.add_entry(**transaction_data)
|
|
220
219
|
return transaction
|
|
@@ -2,26 +2,20 @@
|
|
|
2
2
|
General form constructions.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from abc import
|
|
5
|
+
from abc import abstractmethod
|
|
6
6
|
from datetime import date
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from wtforms.fields import
|
|
8
|
+
from dry_foundation.forms import AbstractFlaskForm, FlaskSubform
|
|
9
|
+
from wtforms.fields import StringField, SubmitField
|
|
10
10
|
from wtforms.validators import DataRequired
|
|
11
11
|
|
|
12
12
|
from .fields import CurrencyField, DateField
|
|
13
|
-
from .validators import SelectionNotBlank
|
|
14
13
|
|
|
15
14
|
# Define a custom form error messaage
|
|
16
15
|
form_err_msg = "There was an improper value in your form. Please try again."
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
class
|
|
20
|
-
# Defined to allow the forms to also to be abstract base classes
|
|
21
|
-
pass
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class EntryForm(FlaskForm, metaclass=AbstractEntryFormMixinMeta):
|
|
18
|
+
class EntryForm(AbstractFlaskForm):
|
|
25
19
|
"""
|
|
26
20
|
A form designed to accept database entry information.
|
|
27
21
|
|
|
@@ -86,11 +80,8 @@ class EntryForm(FlaskForm, metaclass=AbstractEntryFormMixinMeta):
|
|
|
86
80
|
)
|
|
87
81
|
|
|
88
82
|
|
|
89
|
-
class EntrySubform(EntryForm):
|
|
90
|
-
"""
|
|
91
|
-
|
|
92
|
-
def __init__(self, *args, **kwargs):
|
|
93
|
-
super().__init__(meta={"csrf": False}, *args, **kwargs)
|
|
83
|
+
class EntrySubform(FlaskSubform, EntryForm):
|
|
84
|
+
"""A subform implementing ``EntryForm`` behavior."""
|
|
94
85
|
|
|
95
86
|
|
|
96
87
|
class AcquisitionSubform(EntrySubform):
|
|
@@ -150,7 +141,6 @@ class TransactionForm(EntryForm):
|
|
|
150
141
|
}
|
|
151
142
|
return data
|
|
152
143
|
|
|
153
|
-
@abstractmethod
|
|
154
144
|
def gather_entry_data(self, entry):
|
|
155
145
|
if self.subtransaction_model is None:
|
|
156
146
|
raise RuntimeError(
|
|
@@ -4,12 +4,10 @@ General form constructions.
|
|
|
4
4
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
|
|
7
|
-
from flask_wtf import FlaskForm
|
|
8
7
|
from wtforms import fields as wtforms_fields
|
|
9
8
|
from wtforms.validators import Length
|
|
10
9
|
from wtforms.widgets import NumberInput
|
|
11
10
|
|
|
12
|
-
from ...banking.banks import BankHandler
|
|
13
11
|
from ..utils import parse_date
|
|
14
12
|
from .validators import NumeralsOnly, SelectionNotBlank
|
|
15
13
|
|
|
@@ -26,15 +24,6 @@ class DateField(wtforms_fields.DateField):
|
|
|
26
24
|
super().__init__(*args, filters=filters, **kwargs)
|
|
27
25
|
|
|
28
26
|
|
|
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
|
-
|
|
38
27
|
class CurrencyField(wtforms_fields.DecimalField):
|
|
39
28
|
"""A decimal field with currency-specific customizations."""
|
|
40
29
|
|
|
@@ -75,7 +75,7 @@ class Autocompleter:
|
|
|
75
75
|
# Get information from the database to use for autocompletion
|
|
76
76
|
query = model.select_for_user(getattr(model, field))
|
|
77
77
|
values = current_app.db.session.scalars(query)
|
|
78
|
-
suggestions = sort_by_frequency(
|
|
78
|
+
suggestions = sort_by_frequency(list(values))
|
|
79
79
|
return suggestions
|
|
80
80
|
|
|
81
81
|
def _sort_suggestions_by_field(
|
|
@@ -113,9 +113,9 @@ def extend_field_list_for_ajax(form_class, field_list_name, field_list_count):
|
|
|
113
113
|
|
|
114
114
|
Parameters
|
|
115
115
|
----------
|
|
116
|
-
form_class :
|
|
117
|
-
The class (not class instance) containing the field list to
|
|
118
|
-
extended.
|
|
116
|
+
form_class : type
|
|
117
|
+
The form class (not class instance) containing the field list to
|
|
118
|
+
be extended.
|
|
119
119
|
field_list_name : str
|
|
120
120
|
The name of the field list to be extended.
|
|
121
121
|
field_list_count : int
|