monopyly 1.5.1__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.1 → monopyly-1.6.0}/.gitignore +4 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/PKG-INFO +12 -13
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/CHANGELOG.md +27 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/README.md +3 -3
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/__init__.py +22 -27
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/_version.py +2 -2
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/auth/blueprint.py +2 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/auth/routes.py +2 -3
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/banking/accounts.py +7 -7
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/banking/actions.py +20 -17
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/banking/banks.py +1 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/banking/blueprint.py +2 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/banking/filters.py +6 -6
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/banking/forms.py +3 -5
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/banking/routes.py +72 -10
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/banking/transactions.py +15 -7
- monopyly-1.6.0/monopyly/common/forms/__init__.py +15 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/common/forms/_forms.py +1 -2
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/common/forms/fields.py +0 -2
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/common/forms/utils.py +1 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/common/transactions.py +89 -14
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/core/actions.py +2 -8
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/core/blueprint.py +2 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/core/filters.py +0 -2
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/core/routes.py +1 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/accounts.py +1 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/actions.py +4 -5
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/blueprint.py +2 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/cards.py +7 -3
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/forms.py +3 -5
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/routes.py +65 -87
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/statements.py +1 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/transactions/__init__.py +2 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/transactions/_transactions.py +18 -8
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/transactions/activity/__init__.py +6 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/transactions/activity/parser.py +0 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/transactions/activity/reconciliation.py +25 -4
- monopyly-1.6.0/monopyly/database/__init__.py +43 -0
- monopyly-1.6.0/monopyly/database/models.py +405 -0
- {monopyly-1.5.1 → 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.1 → 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.1 → monopyly-1.6.0}/monopyly/static/js/add-transfer.js +8 -9
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/bind-tag-actions.js +6 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/create-balance-chart.js +2 -2
- {monopyly-1.5.1 → 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.1 → monopyly-1.6.0}/monopyly/static/js/modules/expand-transaction.js +7 -6
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/update-display-ajax.js +20 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/update-transactions-display.js +8 -2
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/account_page.html +15 -16
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/account_summaries.html +2 -2
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/account_summary.html +1 -1
- {monopyly-1.5.1 → 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.1 → 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.1/monopyly/templates/credit → monopyly-1.6.0/monopyly/templates/common}/tags_page.html +7 -3
- {monopyly-1.5.1 → 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.1 → monopyly-1.6.0}/monopyly/templates/core/profile.html +1 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statement_page.html +2 -2
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html +1 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html +3 -3
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statement_summary.html +2 -2
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/transaction_submission_page.html +3 -3
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/transactions_page.html +19 -3
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/transactions_table/condensed_row_content.html +2 -3
- {monopyly-1.5.1 → 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.1 → monopyly-1.6.0}/monopyly/templates/credit/transactions_table/transactions.html +0 -1
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/layout.html +9 -4
- {monopyly-1.5.1 → monopyly-1.6.0}/pyproject.toml +32 -19
- monopyly-1.5.1/monopyly/cli/apps.py +0 -108
- monopyly-1.5.1/monopyly/cli/launch.py +0 -135
- monopyly-1.5.1/monopyly/common/forms/__init__.py +0 -7
- monopyly-1.5.1/monopyly/config/__init__.py +0 -1
- monopyly-1.5.1/monopyly/config/default_settings.py +0 -56
- monopyly-1.5.1/monopyly/config/settings.py +0 -59
- monopyly-1.5.1/monopyly/database/__init__.py +0 -101
- monopyly-1.5.1/monopyly/database/models.py +0 -483
- monopyly-1.5.1/monopyly/static/img/about/bank-account-details.png +0 -0
- monopyly-1.5.1/monopyly/static/img/about/bank-account-summaries.png +0 -0
- monopyly-1.5.1/monopyly/static/img/about/bank-accounts.png +0 -0
- monopyly-1.5.1/monopyly/static/img/about/credit-account-details.png +0 -0
- monopyly-1.5.1/monopyly/static/img/about/credit-transactions.png +0 -0
- monopyly-1.5.1/monopyly/static/img/about/homepage-user.png +0 -0
- monopyly-1.5.1/monopyly/static/img/about/homepage.png +0 -0
- monopyly-1.5.1/monopyly/static/jquery-3.7.0.min.js +0 -2
- monopyly-1.5.1/monopyly/templates/common/transactions_table/transactions.html +0 -18
- monopyly-1.5.1/monopyly/templates/core/index.html +0 -169
- monopyly-1.5.1/monopyly/templates/credit/tag_tree/subtag_tree.html +0 -22
- monopyly-1.5.1/monopyly/templates/credit/tag_tree/tag_tree.html +0 -13
- {monopyly-1.5.1 → monopyly-1.6.0}/COPYING +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/LICENSE +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/README.md +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/auth/actions.py +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/auth/tools.py +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/common/forms/validators.py +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/common/utils.py +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/core/context_processors.py +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/core/errors.py +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/core/internal_transactions.py +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/credit/transactions/activity/data.py +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/database/schema.sql +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/database/views.sql +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/browserconfig.xml +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-114.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-120.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-144.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-150.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-152.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-16.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-160.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-180.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-192.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-310.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-32.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-57.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-60.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-64.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-70.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-72.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-76.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon-96.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon.ico +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/favicon/favicon.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/about/statement-details.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/cards/chase-card.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/cards/discover-card.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/cards/new-card.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/cards/template-card.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/arrow-down.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/arrow-left.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/arrow-up.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/checkmark.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/delete-orange.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/delete-thick.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/delete.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/edit.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/link.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/minus-thick.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/minus.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/plus-thick.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/plus.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/refresh.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/save.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/sort-asc.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/sort-desc.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/statement-pair.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/statement-thick.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/statement.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/icons/x-thick.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/img/statement.png +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/add-subtransaction.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/autocomplete-transaction.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/define-filter.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/display-new-bank-inputs.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/expand-bank-account.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/expand-bank.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/expand-transaction.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/flip-card.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/hide-homepage-block.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/highlight-discrepant-transactions.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/infer-card.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/infer-statement.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/make-payment.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/ajax.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/autocomplete-input.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/expand-box-row.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/form-suggestions.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/manage-overlays.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/manage-subforms.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/modules/update-database-widget.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/show-credit-activity-loader.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/show-linked-transaction.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/toggle-navigation.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/update-account-statement-parameters.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/update-bank-name.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/update-card-status.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/update-statement-parameters.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/update-statements-display.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/use-suggested-amount.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/static/js/use-suggested-merchant.js +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/auth/change_password.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/auth/login.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/auth/register.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/account_form/account_form.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/account_form/account_form_page_new.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/account_summaries_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transaction_form.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/form_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/transaction_form/subform.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/credits.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/400.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/401.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/403.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/404.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/405.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/408.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/418.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/425.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/500.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/errors/error.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/core/story.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/account_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/card_form/card_form.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/card_form/card_form_page_new.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/card_graphic/card_back.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/card_submission_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/cards.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/cards_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/discrepant_records.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/summary.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statement_reconciliation/unrecorded_activities.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statements.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/statements_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.5.1 → monopyly-1.6.0}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.5.1 → 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,17 +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:
|
|
27
|
-
Requires-Dist: flask-wtf==1.2.
|
|
28
|
-
Requires-Dist: flask==3.
|
|
29
|
-
Requires-Dist: fuisce==1.0.2
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: dry-foundation==1.4.1
|
|
27
|
+
Requires-Dist: flask-wtf==1.2.2
|
|
28
|
+
Requires-Dist: flask==3.1.2
|
|
30
29
|
Requires-Dist: gunicorn==23.0.0
|
|
31
|
-
Requires-Dist: markdown==3.
|
|
32
|
-
Requires-Dist: nltk==3.9.
|
|
30
|
+
Requires-Dist: markdown==3.9
|
|
31
|
+
Requires-Dist: nltk==3.9.2
|
|
33
32
|
Requires-Dist: python-dateutil==2.9.0
|
|
34
|
-
Requires-Dist: rich==
|
|
35
|
-
Requires-Dist: sqlalchemy==2.0.
|
|
33
|
+
Requires-Dist: rich==14.1.0
|
|
34
|
+
Requires-Dist: sqlalchemy==2.0.43
|
|
36
35
|
Description-Content-Type: text/markdown
|
|
37
36
|
|
|
38
37
|
<div id="header">
|
|
@@ -64,10 +63,10 @@ The package requires a recent version of Python (3.10+).
|
|
|
64
63
|
|
|
65
64
|
## Getting started
|
|
66
65
|
|
|
67
|
-
Once the package is properly installed,
|
|
66
|
+
Once the package is properly installed, launch the app in local mode from the command line (the default options should be sensible, but you may customize the host and port if necessary):
|
|
68
67
|
|
|
69
68
|
```
|
|
70
|
-
$ monopyly local --browser [--host HOST] [--port PORT]
|
|
69
|
+
$ monopyly launch local --browser [--host HOST] [--port PORT]
|
|
71
70
|
```
|
|
72
71
|
|
|
73
72
|
Local mode indicates that the app is just going to be run using a locally hosted server, accessible to just your machine.
|
|
@@ -160,7 +159,7 @@ Card balances are also visible by visiting the pages for individual statements.
|
|
|
160
159
|
A full history of statements for each card is available off the homepage.
|
|
161
160
|
Each statement's page gives the statement's balance, transactions, and due date.
|
|
162
161
|
|
|
163
|
-
<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" />
|
|
164
163
|
|
|
165
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.
|
|
166
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.)
|
|
@@ -207,7 +207,34 @@
|
|
|
207
207
|
- Return to statement details page after deleting a transaction (rather than returning to the general transactions page)
|
|
208
208
|
- Allow users to change their password
|
|
209
209
|
- Warn users before form submission if the configuration currently disallows registration
|
|
210
|
+
- Fix issues with the application launcher not launching the browser; couple application launch process more tightly with click
|
|
211
|
+
- Use type annotations for SQLAlchemy ORM declarative mappings
|
|
210
212
|
- Increase the flexibility of the credit activity parser
|
|
211
213
|
|
|
212
214
|
|
|
215
|
+
### 1.5.2
|
|
216
|
+
|
|
217
|
+
- Use smoothing on charts for up to 100 transactions
|
|
218
|
+
- Improve tokenization normalization for credit reconciliation
|
|
219
|
+
- Remove statement requiring activity files be located in the `Downloads` directory
|
|
220
|
+
- Do not clear the reconciliation info when adding subtransaction fields via POST request
|
|
221
|
+
- Incorporate support for enhanced database handler selection subsets
|
|
222
|
+
- Bump dependencies (including support for recent SQLAlchemy versions)
|
|
223
|
+
|
|
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
|
+
|
|
213
240
|
<a name="bottom" id="bottom"></a>
|
|
@@ -27,10 +27,10 @@ The package requires a recent version of Python (3.10+).
|
|
|
27
27
|
|
|
28
28
|
## Getting started
|
|
29
29
|
|
|
30
|
-
Once the package is properly installed,
|
|
30
|
+
Once the package is properly installed, launch the app in local mode from the command line (the default options should be sensible, but you may customize the host and port if necessary):
|
|
31
31
|
|
|
32
32
|
```
|
|
33
|
-
$ monopyly local --browser [--host HOST] [--port PORT]
|
|
33
|
+
$ monopyly launch local --browser [--host HOST] [--port PORT]
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Local mode indicates that the app is just going to be run using a locally hosted server, accessible to just your machine.
|
|
@@ -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.)
|
|
@@ -2,39 +2,29 @@
|
|
|
2
2
|
Run the Monopyly app.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from dry_foundation import DryFlask, Factory, interact
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from monopyly.database import SQLAlchemy, register_db_cli_commands
|
|
7
|
+
from .core.errors import render_error_template
|
|
8
|
+
from .database import SQLAlchemy
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# Prepare the app configuration
|
|
17
|
-
if test_config:
|
|
18
|
-
config = test_config
|
|
19
|
-
else:
|
|
20
|
-
# Load the development/production config when not testing
|
|
21
|
-
if app.debug or debug:
|
|
22
|
-
config = DevelopmentConfig.configure_for_instance(app.instance_path)
|
|
23
|
-
else:
|
|
24
|
-
config = ProductionConfig.configure_for_instance(app.instance_path)
|
|
25
|
-
app.config.from_object(config)
|
|
26
|
-
|
|
27
|
-
# Initialize the app, including CLI commands and blueprints
|
|
28
|
-
init_app(app)
|
|
29
|
-
return app
|
|
30
|
-
|
|
11
|
+
@Factory(db_interface=SQLAlchemy, echo_engine=False)
|
|
12
|
+
def create_app(config=None):
|
|
13
|
+
"""
|
|
14
|
+
Create the Flask application.
|
|
31
15
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
16
|
+
Create the Flask app, including configurations as specified. This
|
|
17
|
+
will configure the app using the configuration objects made
|
|
18
|
+
available by the Monopyly application and initialize the app
|
|
19
|
+
by registering app blueprints, routes, and commands.
|
|
20
|
+
"""
|
|
21
|
+
# Create and configure the app
|
|
22
|
+
app = DryFlask(__name__, app_name="Monopyly")
|
|
23
|
+
app.configure(config)
|
|
24
|
+
# Register blueprints and error handlers specific to this app
|
|
35
25
|
register_blueprints(app)
|
|
36
26
|
register_errorhandlers(app)
|
|
37
|
-
|
|
27
|
+
return app
|
|
38
28
|
|
|
39
29
|
|
|
40
30
|
def register_blueprints(app):
|
|
@@ -76,3 +66,8 @@ def register_errorhandlers(app):
|
|
|
76
66
|
]
|
|
77
67
|
for code in handled_error_codes:
|
|
78
68
|
app.register_error_handler(code, render_error_template)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def main():
|
|
72
|
+
"""The entry point to the Monopyly application."""
|
|
73
|
+
interact(__name__)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Routes for site authentication.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from dry_foundation.database import db_transaction
|
|
5
6
|
from flask import (
|
|
6
7
|
current_app,
|
|
7
8
|
flash,
|
|
@@ -12,8 +13,6 @@ from flask import (
|
|
|
12
13
|
session,
|
|
13
14
|
url_for,
|
|
14
15
|
)
|
|
15
|
-
from fuisce.database import db_transaction
|
|
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
|
|
@@ -3,8 +3,8 @@ Tools for interacting with bank accounts in the database.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import sqlalchemy.sql.functions as sql_func
|
|
6
|
-
from
|
|
7
|
-
from
|
|
6
|
+
from dry_foundation.database.handler import DatabaseViewHandler
|
|
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(
|
|
@@ -229,10 +229,10 @@ class BankAccountHandler(
|
|
|
229
229
|
return account
|
|
230
230
|
|
|
231
231
|
@classmethod
|
|
232
|
-
def _filter_entries(cls, query, criteria):
|
|
232
|
+
def _filter_entries(cls, query, criteria, offset, limit):
|
|
233
233
|
# Add a join to enable filtering by bank account type
|
|
234
234
|
query = query.join(BankAccountTypeView)
|
|
235
|
-
return super()._filter_entries(query, criteria)
|
|
235
|
+
return super()._filter_entries(query, criteria, offset, limit)
|
|
236
236
|
|
|
237
237
|
@classmethod
|
|
238
238
|
def delete_entry(cls, entry_id):
|
|
@@ -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 = {}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Routes for banking financials.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
5
|
+
from dry_foundation.database import db_transaction
|
|
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():
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
Tools for interacting with the bank transactions in the database.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from dry_foundation.database.handler import DatabaseViewHandler
|
|
6
6
|
|
|
7
7
|
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,
|
|
@@ -36,7 +35,9 @@ class BankTransactionHandler(
|
|
|
36
35
|
|
|
37
36
|
@classmethod
|
|
38
37
|
@DatabaseViewHandler.view_query
|
|
39
|
-
def get_transactions(
|
|
38
|
+
def get_transactions(
|
|
39
|
+
cls, account_ids=None, active=None, sort_order="DESC", offset=None, limit=None
|
|
40
|
+
):
|
|
40
41
|
"""
|
|
41
42
|
Get bank transactions from the database.
|
|
42
43
|
|
|
@@ -61,6 +62,13 @@ class BankTransactionHandler(
|
|
|
61
62
|
An indicator of whether the transactions should be ordered
|
|
62
63
|
in ascending (oldest at top) or descending (newest at top)
|
|
63
64
|
order. The default is descending order.
|
|
65
|
+
offset : int, optional
|
|
66
|
+
The number of transactions by which to offset the results
|
|
67
|
+
returned by this query. The default is `None`, in which case
|
|
68
|
+
no offset will be added.
|
|
69
|
+
limit : int, optional
|
|
70
|
+
A limit on the number of transactions retrieved from the
|
|
71
|
+
database.
|
|
64
72
|
|
|
65
73
|
Returns
|
|
66
74
|
-------
|
|
@@ -71,7 +79,7 @@ class BankTransactionHandler(
|
|
|
71
79
|
criteria.add_match_filter(cls.model, "account_id", account_ids)
|
|
72
80
|
criteria.add_match_filter(BankAccountView, "active", active)
|
|
73
81
|
transactions = super()._get_transactions(
|
|
74
|
-
criteria=criteria, sort_order=sort_order
|
|
82
|
+
criteria=criteria, sort_order=sort_order, offset=offset, limit=limit
|
|
75
83
|
)
|
|
76
84
|
return transactions
|
|
77
85
|
|
|
@@ -150,7 +158,7 @@ class BankTagHandler(TransactionTagHandler, model=TransactionTagHandler.model):
|
|
|
150
158
|
return tags
|
|
151
159
|
|
|
152
160
|
@classmethod
|
|
153
|
-
def _filter_entries(cls, query, criteria):
|
|
161
|
+
def _filter_entries(cls, query, criteria, offset, limit):
|
|
154
162
|
# Add a join to enable filtering by transaction ID or subtransaction ID
|
|
155
163
|
join_transaction = BankTransactionView in criteria.discriminators
|
|
156
164
|
join_subtransaction = (
|
|
@@ -160,7 +168,7 @@ class BankTagHandler(TransactionTagHandler, model=TransactionTagHandler.model):
|
|
|
160
168
|
query = query.join(bank_tag_link_table).join(BankSubtransaction)
|
|
161
169
|
if join_transaction:
|
|
162
170
|
query = query.join(BankTransactionView)
|
|
163
|
-
return super()._filter_entries(query, criteria)
|
|
171
|
+
return super()._filter_entries(query, criteria, offset, limit)
|
|
164
172
|
|
|
165
173
|
|
|
166
174
|
@execute_on_form_validation
|
|
@@ -205,7 +213,7 @@ def save_transaction(form, transaction_id=None):
|
|
|
205
213
|
transfer = record_new_transfer(transfer_data)
|
|
206
214
|
transaction_data.update(
|
|
207
215
|
internal_transaction_id=transfer.internal_transaction_id,
|
|
208
|
-
merchant=transfer.
|
|
216
|
+
merchant=transfer.account_view.bank.bank_name,
|
|
209
217
|
)
|
|
210
218
|
transaction = BankTransactionHandler.add_entry(**transaction_data)
|
|
211
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."
|