monopyly 1.5.2__tar.gz → 1.6.0__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.0}/PKG-INFO +5 -5
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/CHANGELOG.md +15 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/README.md +1 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/__init__.py +1 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/_version.py +2 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/auth/blueprint.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/auth/routes.py +1 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/banking/accounts.py +4 -4
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/banking/actions.py +20 -17
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/banking/blueprint.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/banking/filters.py +6 -6
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/banking/forms.py +3 -5
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/banking/routes.py +71 -9
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/banking/transactions.py +2 -3
- monopyly-1.6.0/monopyly/common/forms/__init__.py +15 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/common/forms/_forms.py +1 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/common/forms/fields.py +0 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/common/forms/utils.py +1 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/common/transactions.py +72 -7
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/core/actions.py +2 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/core/blueprint.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/core/filters.py +0 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/core/routes.py +1 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/actions.py +4 -5
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/blueprint.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/forms.py +3 -5
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/routes.py +37 -62
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/transactions/__init__.py +2 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/transactions/_transactions.py +1 -4
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/transactions/activity/__init__.py +6 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/transactions/activity/parser.py +0 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/transactions/activity/reconciliation.py +5 -3
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/database/__init__.py +0 -3
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/database/models.py +63 -49
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/database/preloads.sql +6 -1
- monopyly-1.6.0/monopyly/scripts/screenshot_application.py +100 -0
- monopyly-1.6.0/monopyly/static/chartist-1.5.0.min.js +8 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/css/style.css +35 -14
- monopyly-1.6.0/monopyly/static/img/about/bank-account-details.png +0 -0
- monopyly-1.6.0/monopyly/static/img/about/bank-account-summaries.png +0 -0
- monopyly-1.6.0/monopyly/static/img/about/bank-accounts.png +0 -0
- monopyly-1.6.0/monopyly/static/img/about/credit-account-details.png +0 -0
- monopyly-1.6.0/monopyly/static/img/about/credit-statement-details.png +0 -0
- monopyly-1.6.0/monopyly/static/img/about/credit-transactions.png +0 -0
- monopyly-1.6.0/monopyly/static/img/about/homepage-user.png +0 -0
- monopyly-1.6.0/monopyly/static/img/about/homepage.png +0 -0
- monopyly-1.6.0/monopyly/static/jquery-3.7.1.min.js +2 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/add-transfer.js +8 -9
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/bind-tag-actions.js +6 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/create-balance-chart.js +1 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/create-category-chart.js +1 -1
- monopyly-1.6.0/monopyly/static/js/load-more-transactions.js +27 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/expand-transaction.js +7 -6
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/update-display-ajax.js +20 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/update-transactions-display.js +8 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/account_page.html +15 -16
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/account_summaries.html +2 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/account_summary.html +1 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/accounts_page.html +2 -2
- monopyly-1.6.0/monopyly/templates/banking/transactions_table/table.html +3 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transactions_table/transactions.html +0 -1
- monopyly-1.6.0/monopyly/templates/common/tag_tree.html +25 -0
- {monopyly-1.5.2/monopyly/templates/credit → monopyly-1.6.0/monopyly/templates/common}/tags_page.html +7 -3
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/linked_bank_transaction.html +2 -2
- monopyly-1.6.0/monopyly/templates/common/transactions_table/table.html +6 -0
- monopyly-1.6.0/monopyly/templates/common/transactions_table/transactions.html +12 -0
- monopyly-1.6.0/monopyly/templates/core/index.html +180 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/profile.html +1 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statement_page.html +2 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html +3 -3
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statement_summary.html +2 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transaction_submission_page.html +3 -3
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transactions_page.html +19 -3
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transactions_table/condensed_row_content.html +2 -2
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transactions_table/expanded_row_content.html +5 -5
- monopyly-1.6.0/monopyly/templates/credit/transactions_table/table.html +3 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transactions_table/transactions.html +0 -1
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/layout.html +9 -4
- {monopyly-1.5.2 → monopyly-1.6.0}/pyproject.toml +26 -13
- monopyly-1.5.2/monopyly/common/forms/__init__.py +0 -7
- 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.0}/.gitignore +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/COPYING +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/LICENSE +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/README.md +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/auth/actions.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/auth/tools.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/banking/banks.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/common/forms/validators.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/common/utils.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/core/context_processors.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/core/errors.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/core/internal_transactions.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/accounts.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/cards.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/statements.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/credit/transactions/activity/data.py +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/database/schema.sql +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/database/views.sql +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/browserconfig.xml +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-114.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-120.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-144.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-150.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-152.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-16.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-160.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-180.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-192.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-310.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-32.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-57.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-60.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-64.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-70.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-72.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-76.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon-96.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon.ico +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/favicon/favicon.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/about/statement-details.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/cards/chase-card.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/cards/discover-card.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/cards/new-card.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/cards/template-card.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/arrow-down.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/arrow-left.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/arrow-up.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/checkmark.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/delete-orange.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/delete-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/delete.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/edit.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/link.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/minus-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/minus.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/plus-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/plus.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/refresh.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/save.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/sort-asc.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/sort-desc.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/statement-pair.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/statement-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/statement.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/icons/x-thick.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/img/statement.png +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/add-subtransaction.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/autocomplete-transaction.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/define-filter.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/display-new-bank-inputs.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/expand-bank-account.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/expand-bank.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/expand-transaction.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/flip-card.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/hide-homepage-block.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/highlight-discrepant-transactions.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/infer-card.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/infer-statement.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/make-payment.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/ajax.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/autocomplete-input.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/expand-box-row.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/form-suggestions.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/manage-overlays.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/manage-subforms.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/modules/update-database-widget.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/show-credit-activity-loader.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/show-linked-transaction.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/toggle-navigation.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/update-account-statement-parameters.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/update-bank-name.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/update-card-status.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/update-statement-parameters.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/update-statements-display.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/use-suggested-amount.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/static/js/use-suggested-merchant.js +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/auth/change_password.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/auth/login.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/auth/register.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/account_form/account_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/account_form/account_form_page_new.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/account_summaries_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transaction_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/form_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transaction_form/subform.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/credits.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/400.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/401.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/403.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/404.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/405.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/408.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/418.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/425.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/500.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/errors/error.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/core/story.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/account_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/card_form/card_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/card_form/card_form_page_new.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/card_graphic/card_back.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/card_submission_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/cards.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/cards_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/discrepant_records.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/summary.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/unrecorded_activities.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statements.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/statements_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.5.2 → monopyly-1.6.0}/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.0
|
|
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,13 +22,13 @@ 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.4.1
|
|
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
30
|
Requires-Dist: markdown==3.9
|
|
31
|
-
Requires-Dist: nltk==3.9.
|
|
31
|
+
Requires-Dist: nltk==3.9.2
|
|
32
32
|
Requires-Dist: python-dateutil==2.9.0
|
|
33
33
|
Requires-Dist: rich==14.1.0
|
|
34
34
|
Requires-Dist: sqlalchemy==2.0.43
|
|
@@ -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,19 @@
|
|
|
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
|
+
|
|
225
240
|
<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,
|
|
@@ -179,7 +177,7 @@ class BankTransactionForm(TransactionForm):
|
|
|
179
177
|
data = {
|
|
180
178
|
"bank_name": entry.bank.bank_name,
|
|
181
179
|
"last_four_digits": entry.last_four_digits,
|
|
182
|
-
"type_name": entry.
|
|
180
|
+
"type_name": entry.account_type_view.type_name,
|
|
183
181
|
}
|
|
184
182
|
elif isinstance(entry, Bank):
|
|
185
183
|
data = {"bank_name": entry.bank_name}
|
|
@@ -278,7 +276,7 @@ class BankTransactionForm(TransactionForm):
|
|
|
278
276
|
"""Gather data for the form from the given database entry."""
|
|
279
277
|
if isinstance(entry, BankTransactionView):
|
|
280
278
|
data = self._gather_transaction_data(entry)
|
|
281
|
-
account_info = entry.
|
|
279
|
+
account_info = entry.account_view
|
|
282
280
|
# Do not prepopulate any transfer information
|
|
283
281
|
elif isinstance(entry, (BankAccountView, Bank)):
|
|
284
282
|
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
|
|
@@ -6,11 +6,10 @@ from abc import ABC, abstractmethod
|
|
|
6
6
|
from datetime import date
|
|
7
7
|
|
|
8
8
|
from flask_wtf import FlaskForm
|
|
9
|
-
from wtforms.fields import
|
|
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."
|
|
@@ -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
|
|
|
@@ -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(
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
Tools for building a common transaction interface.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from abc import abstractmethod
|
|
6
|
-
|
|
7
5
|
from dry_foundation.database.handler import DatabaseHandler, DatabaseViewHandler
|
|
8
|
-
from flask import current_app
|
|
6
|
+
from flask import abort, current_app
|
|
9
7
|
|
|
10
8
|
from ..database.models import (
|
|
11
9
|
BankAccountTypeView,
|
|
@@ -86,7 +84,26 @@ class TransactionHandler(DatabaseViewHandler):
|
|
|
86
84
|
|
|
87
85
|
@classmethod
|
|
88
86
|
def update_entry(cls, entry_id, **field_values):
|
|
89
|
-
"""
|
|
87
|
+
"""
|
|
88
|
+
Update a transaction in the database.
|
|
89
|
+
|
|
90
|
+
Accept a mapping relating given inputs to database fields. This
|
|
91
|
+
mapping is used to update an existing transaction in the
|
|
92
|
+
database. All fields are sanitized prior to updating, and any
|
|
93
|
+
subtransactions are identified for individual processing.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
entry_id : int
|
|
98
|
+
The ID of the transaction to be updated.
|
|
99
|
+
**field_values :
|
|
100
|
+
Values for the fields to update in the transaction.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
transaction : database.models.BankTransaction
|
|
105
|
+
The saved transaction.
|
|
106
|
+
"""
|
|
90
107
|
# Extend the default method to account for subtransactions
|
|
91
108
|
subtransactions_data = field_values.pop("subtransactions", None)
|
|
92
109
|
transaction = super().update_entry(entry_id, **field_values)
|
|
@@ -110,6 +127,30 @@ class TransactionHandler(DatabaseViewHandler):
|
|
|
110
127
|
# Flush to the database after all subtransactions have been added
|
|
111
128
|
cls._db.session.flush()
|
|
112
129
|
|
|
130
|
+
@classmethod
|
|
131
|
+
def delete_entry(cls, entry_id):
|
|
132
|
+
"""
|
|
133
|
+
Delete a transaction in the database given its ID.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
entry_id : int
|
|
138
|
+
The ID of the transaction to be deleted.
|
|
139
|
+
|
|
140
|
+
Notes
|
|
141
|
+
-----
|
|
142
|
+
This will also delete any internal transactions associated with
|
|
143
|
+
this transaction, since the internal transaction link no longer
|
|
144
|
+
exists.
|
|
145
|
+
"""
|
|
146
|
+
internal_transaction = cls.get_entry(entry_id).internal_transaction
|
|
147
|
+
super().delete_entry(entry_id)
|
|
148
|
+
if internal_transaction:
|
|
149
|
+
cls._db.session.refresh(internal_transaction)
|
|
150
|
+
if len(internal_transaction.transaction_views) <= 1:
|
|
151
|
+
cls._db.session.delete(internal_transaction)
|
|
152
|
+
cls._db.session.flush()
|
|
153
|
+
|
|
113
154
|
|
|
114
155
|
def get_linked_transaction(transaction):
|
|
115
156
|
"""
|
|
@@ -369,6 +410,25 @@ class TransactionTagHandler(DatabaseHandler, model=TransactionTag):
|
|
|
369
410
|
tag = cls._db.session.execute(query).scalar_one_or_none()
|
|
370
411
|
return tag
|
|
371
412
|
|
|
413
|
+
@classmethod
|
|
414
|
+
def delete_entry(cls, entry_id):
|
|
415
|
+
"""
|
|
416
|
+
Delete the tag in the database given its ID.
|
|
417
|
+
|
|
418
|
+
Parameters
|
|
419
|
+
----------
|
|
420
|
+
entry_id : int
|
|
421
|
+
The ID of the tag to be deleted.
|
|
422
|
+
"""
|
|
423
|
+
super().delete_entry(entry_id)
|
|
424
|
+
|
|
425
|
+
@classmethod
|
|
426
|
+
def _retrieve_authorized_manipulable_entry(cls, entry_id):
|
|
427
|
+
tag = super()._retrieve_authorized_manipulable_entry(entry_id)
|
|
428
|
+
if tag.user_id != cls.user_id:
|
|
429
|
+
abort(403, "The current user is not authorized to manipulate this tag.")
|
|
430
|
+
return tag
|
|
431
|
+
|
|
372
432
|
|
|
373
433
|
def categorize(transactions):
|
|
374
434
|
"""
|
|
@@ -412,6 +472,11 @@ class CategoryTree:
|
|
|
412
472
|
"""
|
|
413
473
|
Store a tree of categories.
|
|
414
474
|
|
|
475
|
+
The category tree is a tree of categorized subtransactions. Each
|
|
476
|
+
leaf of the tree represents a transaction tag and the
|
|
477
|
+
subtransactions that have been categorized according to that tag
|
|
478
|
+
(the category).
|
|
479
|
+
|
|
415
480
|
Parameters
|
|
416
481
|
----------
|
|
417
482
|
category : database.models.TransactionTag, str
|
|
@@ -480,9 +545,9 @@ class RootCategoryTree(CategoryTree):
|
|
|
480
545
|
Given a subtransaction, add that subtransaction to the category
|
|
481
546
|
tree according to its tags. If multiple tags exist at the same
|
|
482
547
|
level of the tree (i.e., a subtransaction with tags in diverging
|
|
483
|
-
branches), the
|
|
484
|
-
tag is listed only as a member of the root tree and not
|
|
485
|
-
member of any other subcategory tree.
|
|
548
|
+
branches), the subtransaction is determined to be "uncategorizable"
|
|
549
|
+
and the tag is listed only as a member of the root tree and not
|
|
550
|
+
as a member of any other subcategory tree.
|
|
486
551
|
|
|
487
552
|
Parameters
|
|
488
553
|
----------
|
|
@@ -4,6 +4,7 @@ import markdown
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class MarkdownConverter:
|
|
7
|
+
"""An object to convert Markdown to HTML."""
|
|
7
8
|
|
|
8
9
|
replacements = {
|
|
9
10
|
"src": [
|
|
@@ -71,7 +72,7 @@ def convert_readme_to_html_template(readme_path):
|
|
|
71
72
|
'<div class="resource-links">'
|
|
72
73
|
" <h2>Links</h2>"
|
|
73
74
|
' <p><a href="{{ url_for("core.story") }}">Story</a></p>'
|
|
74
|
-
' <p><a href="{{ url_for("core.
|
|
75
|
+
' <p><a href="{{ url_for("core.application_credits") }}">Credits</a></p>'
|
|
75
76
|
"</div>"
|
|
76
77
|
),
|
|
77
78
|
)
|