monopyly 1.5.0__tar.gz → 1.5.2__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.0 → monopyly-1.5.2}/.gitignore +4 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/PKG-INFO +12 -13
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/CHANGELOG.md +23 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/README.md +2 -2
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/__init__.py +22 -27
- monopyly-1.5.2/monopyly/_version.py +16 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/auth/actions.py +12 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/auth/routes.py +31 -22
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/auth/tools.py +1 -2
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/banking/accounts.py +3 -3
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/banking/banks.py +1 -1
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/banking/routes.py +1 -1
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/banking/transactions.py +14 -5
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/common/forms/utils.py +1 -2
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/common/transactions.py +17 -7
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/core/actions.py +0 -7
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/core/routes.py +0 -6
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/accounts.py +1 -1
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/cards.py +7 -3
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/forms.py +1 -1
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/routes.py +46 -25
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/statements.py +1 -1
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/transactions/_transactions.py +18 -5
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/transactions/activity/parser.py +14 -6
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/transactions/activity/reconciliation.py +20 -1
- monopyly-1.5.2/monopyly/database/__init__.py +46 -0
- monopyly-1.5.2/monopyly/database/models.py +391 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/css/style.css +191 -11
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/create-balance-chart.js +1 -1
- monopyly-1.5.2/monopyly/templates/auth/change_password.html +21 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/auth/login.html +3 -1
- monopyly-1.5.2/monopyly/templates/auth/register.html +25 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/profile.html +2 -2
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html +1 -1
- monopyly-1.5.2/monopyly/templates/credit/transaction_submission_page.html +89 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transactions_table/condensed_row_content.html +0 -1
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/layout.html +2 -2
- {monopyly-1.5.0 → monopyly-1.5.2}/pyproject.toml +10 -10
- monopyly-1.5.0/monopyly/_version.py +0 -4
- monopyly-1.5.0/monopyly/cli/apps.py +0 -108
- monopyly-1.5.0/monopyly/cli/launch.py +0 -135
- monopyly-1.5.0/monopyly/config/__init__.py +0 -1
- monopyly-1.5.0/monopyly/config/default_settings.py +0 -56
- monopyly-1.5.0/monopyly/config/settings.py +0 -59
- monopyly-1.5.0/monopyly/database/__init__.py +0 -101
- monopyly-1.5.0/monopyly/database/models.py +0 -483
- monopyly-1.5.0/monopyly/templates/auth/register.html +0 -15
- monopyly-1.5.0/monopyly/templates/credit/transaction_submission_page.html +0 -96
- {monopyly-1.5.0 → monopyly-1.5.2}/COPYING +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/LICENSE +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/README.md +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/auth/blueprint.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/banking/actions.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/banking/blueprint.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/banking/filters.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/banking/forms.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/common/forms/__init__.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/common/forms/_forms.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/common/forms/fields.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/common/forms/validators.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/common/utils.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/core/blueprint.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/core/context_processors.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/core/errors.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/core/filters.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/core/internal_transactions.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/actions.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/blueprint.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/transactions/__init__.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/transactions/activity/__init__.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/credit/transactions/activity/data.py +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/database/preloads.sql +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/database/schema.sql +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/database/views.sql +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/browserconfig.xml +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-114.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-120.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-144.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-150.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-152.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-16.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-160.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-180.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-192.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-310.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-32.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-57.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-60.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-64.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-70.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-72.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-76.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon-96.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon.ico +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/favicon/favicon.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/about/bank-account-details.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/about/bank-account-summaries.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/about/bank-accounts.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/about/credit-account-details.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/about/credit-transactions.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/about/homepage-user.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/about/homepage.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/about/statement-details.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/cards/chase-card.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/cards/discover-card.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/cards/new-card.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/cards/template-card.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/arrow-down.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/arrow-left.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/arrow-up.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/checkmark.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/delete-orange.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/delete-thick.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/delete.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/edit.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/link.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/minus-thick.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/minus.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/plus-thick.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/plus.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/refresh.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/save.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/sort-asc.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/sort-desc.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/statement-pair.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/statement-thick.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/statement.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/icons/x-thick.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/img/statement.png +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/jquery-3.7.0.min.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/add-subtransaction.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/add-transfer.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/autocomplete-transaction.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/bind-tag-actions.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/create-category-chart.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/define-filter.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/display-new-bank-inputs.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/expand-bank-account.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/expand-bank.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/expand-transaction.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/flip-card.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/hide-homepage-block.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/highlight-discrepant-transactions.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/infer-card.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/infer-statement.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/make-payment.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/ajax.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/autocomplete-input.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/expand-box-row.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/expand-transaction.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/form-suggestions.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/manage-overlays.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/manage-subforms.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/update-database-widget.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/modules/update-display-ajax.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/show-credit-activity-loader.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/show-linked-transaction.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/toggle-navigation.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/update-account-statement-parameters.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/update-bank-name.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/update-card-status.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/update-statement-parameters.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/update-statements-display.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/update-transactions-display.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/use-suggested-amount.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/static/js/use-suggested-merchant.js +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/account_form/account_form.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/account_form/account_form_page_new.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/account_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/account_summaries.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/account_summaries_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/account_summary.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/accounts_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transaction_form.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/banking/transactions_table/transactions.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/form_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transaction_form/subform.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/linked_bank_transaction.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/transactions.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/credits.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/400.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/401.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/403.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/404.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/405.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/408.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/418.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/425.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/500.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/errors/error.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/index.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/core/story.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/account_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/card_form/card_form.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/card_form/card_form_page_new.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/card_graphic/card_back.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/card_submission_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/cards.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/cards_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statement_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/discrepant_records.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/summary.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/unrecorded_activities.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statement_summary.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statements.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/statements_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/tag_tree/subtag_tree.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/tag_tree/tag_tree.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/tags_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transactions_page.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transactions_table/expanded_row_content.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transactions_table/transaction_field_titles.html +0 -0
- {monopyly-1.5.0 → monopyly-1.5.2}/monopyly/templates/credit/transactions_table/transactions.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: monopyly
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.2
|
|
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
|
|
@@ -23,16 +23,15 @@ Classifier: Topic :: Office/Business :: Financial
|
|
|
23
23
|
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
24
24
|
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
25
25
|
Requires-Python: <3.11,>=3.10
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist: flask-wtf==1.2.
|
|
28
|
-
Requires-Dist: flask==3.
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist: nltk==3.8.1
|
|
26
|
+
Requires-Dist: dry-foundation==1.3.0
|
|
27
|
+
Requires-Dist: flask-wtf==1.2.2
|
|
28
|
+
Requires-Dist: flask==3.1.2
|
|
29
|
+
Requires-Dist: gunicorn==23.0.0
|
|
30
|
+
Requires-Dist: markdown==3.9
|
|
31
|
+
Requires-Dist: nltk==3.9.1
|
|
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.
|
|
@@ -199,4 +199,27 @@
|
|
|
199
199
|
- Create a tool for reconciling credit card transactions with information collected from external resources (e.g., CSVs downloaded from a user's online credit card account)
|
|
200
200
|
|
|
201
201
|
|
|
202
|
+
### 1.5.1
|
|
203
|
+
|
|
204
|
+
- Bump dependencies (including patching security vulnerability in NLTK)
|
|
205
|
+
- Style credit transaction submissions as receipts
|
|
206
|
+
- Style flash messages according to content
|
|
207
|
+
- Return to statement details page after deleting a transaction (rather than returning to the general transactions page)
|
|
208
|
+
- Allow users to change their password
|
|
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
|
|
212
|
+
- Increase the flexibility of the credit activity parser
|
|
213
|
+
|
|
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
|
+
|
|
202
225
|
<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.
|
|
@@ -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)
|
|
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__)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '1.5.2'
|
|
16
|
+
__version_tuple__ = version_tuple = (1, 5, 2)
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"""Module describing logical authorization actions (to be used in routes)."""
|
|
2
2
|
|
|
3
|
+
from flask import current_app
|
|
4
|
+
from sqlalchemy import select
|
|
5
|
+
|
|
6
|
+
from ..database.models import User
|
|
7
|
+
|
|
3
8
|
|
|
4
9
|
def get_username_and_password(form):
|
|
5
10
|
"""
|
|
@@ -11,3 +16,10 @@ def get_username_and_password(form):
|
|
|
11
16
|
username = form["username"].lower()
|
|
12
17
|
password = form["password"]
|
|
13
18
|
return username, password
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def identify_user(username):
|
|
22
|
+
"""Identify the user in the database based on the username."""
|
|
23
|
+
user_query = select(User).where(User.username == username)
|
|
24
|
+
user = current_app.db.session.scalar(user_query)
|
|
25
|
+
return user
|
|
@@ -2,21 +2,22 @@
|
|
|
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,
|
|
9
|
+
g,
|
|
8
10
|
redirect,
|
|
9
11
|
render_template,
|
|
10
12
|
request,
|
|
11
13
|
session,
|
|
12
14
|
url_for,
|
|
13
15
|
)
|
|
14
|
-
from fuisce.database import db_transaction
|
|
15
16
|
from sqlalchemy import select
|
|
16
17
|
from werkzeug.security import check_password_hash, generate_password_hash
|
|
17
18
|
|
|
18
19
|
from ..database.models import User
|
|
19
|
-
from .actions import get_username_and_password
|
|
20
|
+
from .actions import get_username_and_password, identify_user
|
|
20
21
|
from .blueprint import bp
|
|
21
22
|
|
|
22
23
|
|
|
@@ -33,24 +34,17 @@ def register():
|
|
|
33
34
|
error = "Username is required."
|
|
34
35
|
elif not password:
|
|
35
36
|
error = "Password is required."
|
|
37
|
+
elif user := identify_user(username):
|
|
38
|
+
error = f"User {username} is already registered."
|
|
36
39
|
else:
|
|
37
|
-
#
|
|
38
|
-
user_query = select(User).where(User.username == username)
|
|
39
|
-
user = current_app.db.session.scalar(user_query)
|
|
40
|
-
if user:
|
|
41
|
-
error = f"User {username} is already registered."
|
|
42
|
-
else:
|
|
43
|
-
error = None
|
|
44
|
-
# Add the username and hashed password to the database
|
|
45
|
-
if not error:
|
|
40
|
+
# Create a new user
|
|
46
41
|
new_user = User(
|
|
47
42
|
username=username,
|
|
48
43
|
password=generate_password_hash(password),
|
|
49
44
|
)
|
|
50
45
|
current_app.db.session.add(new_user)
|
|
51
46
|
return redirect(url_for("auth.login"))
|
|
52
|
-
|
|
53
|
-
flash(error)
|
|
47
|
+
flash(error)
|
|
54
48
|
# Display the registration page
|
|
55
49
|
return render_template("auth/register.html")
|
|
56
50
|
|
|
@@ -60,23 +54,17 @@ def login():
|
|
|
60
54
|
if request.method == "POST":
|
|
61
55
|
# Get username and passwords from the form
|
|
62
56
|
username, password = get_username_and_password(request.form)
|
|
63
|
-
# Get user information from the database
|
|
64
|
-
user_query = select(User).where(User.username == username)
|
|
65
|
-
user = current_app.db.session.scalar(user_query)
|
|
66
57
|
# Check for errors in the accessed information
|
|
67
|
-
if user is None:
|
|
58
|
+
if (user := identify_user(username)) is None:
|
|
68
59
|
error = "That user is not yet registered."
|
|
69
60
|
elif not check_password_hash(user.password, password):
|
|
70
61
|
error = "Incorrect username and password combination."
|
|
71
62
|
else:
|
|
72
|
-
|
|
73
|
-
# Set the user ID securely for a new session
|
|
74
|
-
if not error:
|
|
63
|
+
# Set the user ID securely for a new session
|
|
75
64
|
session.clear()
|
|
76
65
|
session["user_id"] = user.id
|
|
77
66
|
return redirect(url_for("core.index"))
|
|
78
|
-
|
|
79
|
-
flash(error)
|
|
67
|
+
flash(error)
|
|
80
68
|
# Display the login page
|
|
81
69
|
return render_template("auth/login.html")
|
|
82
70
|
|
|
@@ -86,3 +74,24 @@ def logout():
|
|
|
86
74
|
# End the session and clear the user ID
|
|
87
75
|
session.clear()
|
|
88
76
|
return redirect(url_for("core.index"))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@bp.route("/change_password", methods=("GET", "POST"))
|
|
80
|
+
@db_transaction
|
|
81
|
+
def change_password():
|
|
82
|
+
if request.method == "POST":
|
|
83
|
+
current_password = request.form["current-password"]
|
|
84
|
+
new_password = request.form["new-password"]
|
|
85
|
+
if check_password_hash(g.user.password, current_password):
|
|
86
|
+
# Merge the user item (dissociated from the current session) for updating
|
|
87
|
+
g.user = current_app.db.session.merge(g.user)
|
|
88
|
+
g.user.password = generate_password_hash(new_password)
|
|
89
|
+
flash("Password updated successfully.", category="success")
|
|
90
|
+
return redirect(url_for("core.load_profile"))
|
|
91
|
+
else:
|
|
92
|
+
flash(
|
|
93
|
+
"The provided value for the current password does not match the "
|
|
94
|
+
"value of the current password set on this account.",
|
|
95
|
+
category="error",
|
|
96
|
+
)
|
|
97
|
+
return render_template("auth/change_password.html")
|
|
@@ -14,8 +14,7 @@ from .blueprint import bp
|
|
|
14
14
|
@bp.before_app_request
|
|
15
15
|
def load_logged_in_user():
|
|
16
16
|
# Match the user's information with the session
|
|
17
|
-
user_id
|
|
18
|
-
if user_id is None:
|
|
17
|
+
if (user_id := session.get("user_id")) is None:
|
|
19
18
|
g.user = None
|
|
20
19
|
else:
|
|
21
20
|
query = select(User).where(User.id == user_id)
|
|
@@ -3,7 +3,7 @@ Tools for interacting with bank accounts in the database.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import sqlalchemy.sql.functions as sql_func
|
|
6
|
-
from
|
|
6
|
+
from dry_foundation.database.handler import DatabaseViewHandler
|
|
7
7
|
from werkzeug.exceptions import abort
|
|
8
8
|
|
|
9
9
|
from ..common.forms.utils import execute_on_form_validation
|
|
@@ -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):
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Routes for banking financials.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from dry_foundation.database import db_transaction
|
|
5
6
|
from flask import jsonify, redirect, render_template, request, url_for
|
|
6
|
-
from fuisce.database import db_transaction
|
|
7
7
|
|
|
8
8
|
from ..auth.tools import login_required
|
|
9
9
|
from ..common.forms.utils import extend_field_list_for_ajax
|
|
@@ -2,7 +2,7 @@
|
|
|
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
|
|
@@ -36,7 +36,9 @@ class BankTransactionHandler(
|
|
|
36
36
|
|
|
37
37
|
@classmethod
|
|
38
38
|
@DatabaseViewHandler.view_query
|
|
39
|
-
def get_transactions(
|
|
39
|
+
def get_transactions(
|
|
40
|
+
cls, account_ids=None, active=None, sort_order="DESC", offset=None, limit=None
|
|
41
|
+
):
|
|
40
42
|
"""
|
|
41
43
|
Get bank transactions from the database.
|
|
42
44
|
|
|
@@ -61,6 +63,13 @@ class BankTransactionHandler(
|
|
|
61
63
|
An indicator of whether the transactions should be ordered
|
|
62
64
|
in ascending (oldest at top) or descending (newest at top)
|
|
63
65
|
order. The default is descending order.
|
|
66
|
+
offset : int, optional
|
|
67
|
+
The number of transactions by which to offset the results
|
|
68
|
+
returned by this query. The default is `None`, in which case
|
|
69
|
+
no offset will be added.
|
|
70
|
+
limit : int, optional
|
|
71
|
+
A limit on the number of transactions retrieved from the
|
|
72
|
+
database.
|
|
64
73
|
|
|
65
74
|
Returns
|
|
66
75
|
-------
|
|
@@ -71,7 +80,7 @@ class BankTransactionHandler(
|
|
|
71
80
|
criteria.add_match_filter(cls.model, "account_id", account_ids)
|
|
72
81
|
criteria.add_match_filter(BankAccountView, "active", active)
|
|
73
82
|
transactions = super()._get_transactions(
|
|
74
|
-
criteria=criteria, sort_order=sort_order
|
|
83
|
+
criteria=criteria, sort_order=sort_order, offset=None, limit=None
|
|
75
84
|
)
|
|
76
85
|
return transactions
|
|
77
86
|
|
|
@@ -150,7 +159,7 @@ class BankTagHandler(TransactionTagHandler, model=TransactionTagHandler.model):
|
|
|
150
159
|
return tags
|
|
151
160
|
|
|
152
161
|
@classmethod
|
|
153
|
-
def _filter_entries(cls, query, criteria):
|
|
162
|
+
def _filter_entries(cls, query, criteria, offset, limit):
|
|
154
163
|
# Add a join to enable filtering by transaction ID or subtransaction ID
|
|
155
164
|
join_transaction = BankTransactionView in criteria.discriminators
|
|
156
165
|
join_subtransaction = (
|
|
@@ -160,7 +169,7 @@ class BankTagHandler(TransactionTagHandler, model=TransactionTagHandler.model):
|
|
|
160
169
|
query = query.join(bank_tag_link_table).join(BankSubtransaction)
|
|
161
170
|
if join_transaction:
|
|
162
171
|
query = query.join(BankTransactionView)
|
|
163
|
-
return super()._filter_entries(query, criteria)
|
|
172
|
+
return super()._filter_entries(query, criteria, offset, limit)
|
|
164
173
|
|
|
165
174
|
|
|
166
175
|
@execute_on_form_validation
|
|
@@ -145,7 +145,6 @@ def execute_on_form_validation(func):
|
|
|
145
145
|
else:
|
|
146
146
|
# Show an error to the user and print the errors for the admin
|
|
147
147
|
flash(form_err_msg)
|
|
148
|
-
|
|
149
|
-
raise ValidationError("The form did not validate properly.")
|
|
148
|
+
raise ValidationError(f"The form did not validate properly: {form.errors}")
|
|
150
149
|
|
|
151
150
|
return wrapper
|
|
@@ -4,7 +4,7 @@ Tools for building a common transaction interface.
|
|
|
4
4
|
|
|
5
5
|
from abc import abstractmethod
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from dry_foundation.database.handler import DatabaseHandler, DatabaseViewHandler
|
|
8
8
|
from flask import current_app
|
|
9
9
|
|
|
10
10
|
from ..database.models import (
|
|
@@ -31,17 +31,27 @@ class TransactionHandler(DatabaseViewHandler):
|
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
@classmethod
|
|
34
|
-
def _customize_entries_query(
|
|
34
|
+
def _customize_entries_query(
|
|
35
|
+
cls, query, criteria, column_orders, offset=None, limit=None
|
|
36
|
+
):
|
|
35
37
|
# Group transactions and order by transaction date
|
|
36
38
|
query = query.group_by(cls.model.id)
|
|
37
|
-
return super()._customize_entries_query(
|
|
39
|
+
return super()._customize_entries_query(
|
|
40
|
+
query, criteria, column_orders, offset=offset, limit=limit
|
|
41
|
+
)
|
|
38
42
|
|
|
39
43
|
@classmethod
|
|
40
|
-
def _get_transactions(
|
|
44
|
+
def _get_transactions(
|
|
45
|
+
cls, criteria=None, sort_order="DESC", offset=None, limit=None
|
|
46
|
+
):
|
|
41
47
|
# Specify transaction order
|
|
42
48
|
column_orders = {cls.model.transaction_date: sort_order}
|
|
43
49
|
entries = cls.get_entries(
|
|
44
|
-
entry_ids=None,
|
|
50
|
+
entry_ids=None,
|
|
51
|
+
criteria=criteria,
|
|
52
|
+
column_orders=column_orders,
|
|
53
|
+
offset=offset,
|
|
54
|
+
limit=limit,
|
|
45
55
|
)
|
|
46
56
|
return entries
|
|
47
57
|
|
|
@@ -232,10 +242,10 @@ class TransactionTagHandler(DatabaseHandler, model=TransactionTag):
|
|
|
232
242
|
return tags
|
|
233
243
|
|
|
234
244
|
@classmethod
|
|
235
|
-
def _filter_entries(cls, query, criteria):
|
|
245
|
+
def _filter_entries(cls, query, criteria, offset, limit):
|
|
236
246
|
# Only get distinct tag entries
|
|
237
247
|
query = query.distinct()
|
|
238
|
-
return super()._filter_entries(query, criteria)
|
|
248
|
+
return super()._filter_entries(query, criteria, offset, limit)
|
|
239
249
|
|
|
240
250
|
@classmethod
|
|
241
251
|
def get_subtags(cls, tag):
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
"""Module describing logical core actions (to be used in routes)."""
|
|
2
2
|
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
|
|
5
3
|
import markdown
|
|
6
4
|
|
|
7
5
|
|
|
8
|
-
def get_timestamp():
|
|
9
|
-
"""Get a timestamp for backup filenames."""
|
|
10
|
-
return datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
11
|
-
|
|
12
|
-
|
|
13
6
|
class MarkdownConverter:
|
|
14
7
|
|
|
15
8
|
replacements = {
|
|
@@ -5,7 +5,6 @@ Routes for core functionality.
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from flask import g, render_template, render_template_string, session
|
|
8
|
-
from werkzeug.exceptions import abort
|
|
9
8
|
|
|
10
9
|
from ..auth.tools import login_required
|
|
11
10
|
from ..banking.accounts import BankAccountHandler
|
|
@@ -85,8 +84,3 @@ def load_profile():
|
|
|
85
84
|
banks = BankHandler.get_banks()
|
|
86
85
|
# Return banks as a list to allow multiple reuse
|
|
87
86
|
return render_template("core/profile.html", banks=list(banks))
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
@bp.route("/change_password")
|
|
91
|
-
def change_password():
|
|
92
|
-
abort(418)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Tools for interacting with credit cards in the database.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from dry_foundation.database.handler import DatabaseHandler
|
|
6
6
|
|
|
7
7
|
from ..common.forms.utils import execute_on_form_validation
|
|
8
8
|
from ..database.models import Bank, CreditAccount, CreditCard
|
|
@@ -99,8 +99,12 @@ class CreditCardHandler(DatabaseHandler, model=CreditCard):
|
|
|
99
99
|
return card
|
|
100
100
|
|
|
101
101
|
@classmethod
|
|
102
|
-
def _customize_entries_query(
|
|
103
|
-
|
|
102
|
+
def _customize_entries_query(
|
|
103
|
+
cls, query, filters, sort_order, offset=None, limit=None
|
|
104
|
+
):
|
|
105
|
+
query = super()._customize_entries_query(
|
|
106
|
+
query, filters, sort_order, offset=offset, limit=limit
|
|
107
|
+
)
|
|
104
108
|
# Order cards by active status (active cards first)
|
|
105
109
|
query = query.order_by(cls.model.active.desc())
|
|
106
110
|
return query
|
|
@@ -301,7 +301,7 @@ class CreditTransactionForm(TransactionForm):
|
|
|
301
301
|
|
|
302
302
|
def _extract_merchant_suggestion(self, data):
|
|
303
303
|
# Use the merchant transaction data as a suggestion source
|
|
304
|
-
if merchant :=
|
|
304
|
+
if merchant := data.get("merchant"):
|
|
305
305
|
merchant_tokens = ActivityMatchmaker.tokenize(merchant)
|
|
306
306
|
# Suggest a known merchant with the closest distance to the activity merchant
|
|
307
307
|
score_records = []
|