monopyly 1.5.2__tar.gz → 1.6.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. {monopyly-1.5.2 → monopyly-1.6.1}/PKG-INFO +8 -8
  2. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/CHANGELOG.md +22 -0
  3. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/README.md +1 -1
  4. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/__init__.py +1 -1
  5. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/_version.py +2 -2
  6. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/auth/blueprint.py +2 -0
  7. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/auth/routes.py +1 -2
  8. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/accounts.py +4 -4
  9. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/actions.py +20 -17
  10. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/blueprint.py +2 -0
  11. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/filters.py +6 -6
  12. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/forms.py +7 -5
  13. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/routes.py +71 -9
  14. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/transactions.py +2 -3
  15. monopyly-1.6.1/monopyly/common/forms/__init__.py +15 -0
  16. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/forms/_forms.py +6 -16
  17. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/forms/fields.py +0 -11
  18. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/forms/utils.py +4 -4
  19. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/transactions.py +72 -7
  20. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/actions.py +2 -1
  21. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/blueprint.py +2 -0
  22. monopyly-1.6.1/monopyly/core/context_processors.py +23 -0
  23. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/filters.py +0 -2
  24. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/routes.py +1 -1
  25. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/actions.py +4 -5
  26. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/blueprint.py +2 -0
  27. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/cards.py +1 -1
  28. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/forms.py +9 -7
  29. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/routes.py +37 -62
  30. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/__init__.py +2 -0
  31. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/_transactions.py +1 -4
  32. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/activity/__init__.py +6 -0
  33. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/activity/parser.py +0 -1
  34. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/activity/reconciliation.py +5 -3
  35. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/__init__.py +0 -3
  36. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/models.py +63 -49
  37. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/preloads.sql +6 -1
  38. monopyly-1.6.1/monopyly/scripts/screenshot_application.py +100 -0
  39. monopyly-1.6.1/monopyly/static/chartist-1.5.0.min.js +8 -0
  40. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/css/style.css +39 -14
  41. monopyly-1.6.1/monopyly/static/img/about/bank-account-details.png +0 -0
  42. monopyly-1.6.1/monopyly/static/img/about/bank-account-summaries.png +0 -0
  43. monopyly-1.6.1/monopyly/static/img/about/bank-accounts.png +0 -0
  44. monopyly-1.6.1/monopyly/static/img/about/credit-account-details.png +0 -0
  45. monopyly-1.6.1/monopyly/static/img/about/credit-statement-details.png +0 -0
  46. monopyly-1.6.1/monopyly/static/img/about/credit-transactions.png +0 -0
  47. monopyly-1.6.1/monopyly/static/img/about/homepage-user.png +0 -0
  48. monopyly-1.6.1/monopyly/static/img/about/homepage.png +0 -0
  49. monopyly-1.6.1/monopyly/static/jquery-3.7.1.min.js +2 -0
  50. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/add-transfer.js +8 -9
  51. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/bind-tag-actions.js +6 -0
  52. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/create-balance-chart.js +1 -1
  53. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/create-category-chart.js +1 -1
  54. monopyly-1.6.1/monopyly/static/js/load-more-transactions.js +27 -0
  55. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/expand-transaction.js +7 -6
  56. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/update-display-ajax.js +20 -1
  57. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-transactions-display.js +8 -2
  58. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_form/account_form_page_new.html +10 -8
  59. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_page.html +32 -33
  60. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_summaries.html +2 -2
  61. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_summary.html +1 -1
  62. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/accounts_page.html +10 -8
  63. monopyly-1.6.1/monopyly/templates/banking/transactions_table/table.html +3 -0
  64. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transactions_table/transactions.html +0 -1
  65. monopyly-1.6.1/monopyly/templates/common/tag_tree.html +25 -0
  66. {monopyly-1.5.2/monopyly/templates/credit → monopyly-1.6.1/monopyly/templates/common}/tags_page.html +17 -13
  67. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/linked_bank_transaction.html +2 -2
  68. monopyly-1.6.1/monopyly/templates/common/transactions_table/table.html +6 -0
  69. monopyly-1.6.1/monopyly/templates/common/transactions_table/transactions.html +12 -0
  70. monopyly-1.6.1/monopyly/templates/core/index.html +184 -0
  71. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/profile.html +19 -20
  72. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/account_page.html +17 -17
  73. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_form/card_form_page_new.html +8 -5
  74. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_page.html +45 -46
  75. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html +22 -22
  76. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_summary.html +2 -2
  77. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statements_page.html +13 -13
  78. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_submission_page.html +3 -3
  79. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_page.html +38 -22
  80. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_table/condensed_row_content.html +2 -2
  81. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_table/expanded_row_content.html +5 -5
  82. monopyly-1.6.1/monopyly/templates/credit/transactions_table/table.html +3 -0
  83. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_table/transactions.html +0 -1
  84. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/layout.html +16 -9
  85. {monopyly-1.5.2 → monopyly-1.6.1}/pyproject.toml +29 -16
  86. monopyly-1.5.2/monopyly/common/forms/__init__.py +0 -7
  87. monopyly-1.5.2/monopyly/core/context_processors.py +0 -41
  88. monopyly-1.5.2/monopyly/static/img/about/bank-account-details.png +0 -0
  89. monopyly-1.5.2/monopyly/static/img/about/bank-account-summaries.png +0 -0
  90. monopyly-1.5.2/monopyly/static/img/about/bank-accounts.png +0 -0
  91. monopyly-1.5.2/monopyly/static/img/about/credit-account-details.png +0 -0
  92. monopyly-1.5.2/monopyly/static/img/about/credit-transactions.png +0 -0
  93. monopyly-1.5.2/monopyly/static/img/about/homepage-user.png +0 -0
  94. monopyly-1.5.2/monopyly/static/img/about/homepage.png +0 -0
  95. monopyly-1.5.2/monopyly/static/jquery-3.7.0.min.js +0 -2
  96. monopyly-1.5.2/monopyly/templates/common/transactions_table/transactions.html +0 -18
  97. monopyly-1.5.2/monopyly/templates/core/index.html +0 -169
  98. monopyly-1.5.2/monopyly/templates/credit/tag_tree/subtag_tree.html +0 -22
  99. monopyly-1.5.2/monopyly/templates/credit/tag_tree/tag_tree.html +0 -13
  100. {monopyly-1.5.2 → monopyly-1.6.1}/.gitignore +0 -0
  101. {monopyly-1.5.2 → monopyly-1.6.1}/COPYING +0 -0
  102. {monopyly-1.5.2 → monopyly-1.6.1}/LICENSE +0 -0
  103. {monopyly-1.5.2 → monopyly-1.6.1}/README.md +0 -0
  104. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/auth/actions.py +0 -0
  105. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/auth/tools.py +0 -0
  106. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/banking/banks.py +0 -0
  107. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/forms/validators.py +0 -0
  108. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/common/utils.py +0 -0
  109. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/errors.py +0 -0
  110. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/core/internal_transactions.py +0 -0
  111. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/accounts.py +0 -0
  112. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/statements.py +0 -0
  113. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/credit/transactions/activity/data.py +0 -0
  114. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/schema.sql +0 -0
  115. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/database/views.sql +0 -0
  116. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/browserconfig.xml +0 -0
  117. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-114.png +0 -0
  118. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-120.png +0 -0
  119. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-144.png +0 -0
  120. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-150.png +0 -0
  121. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-152.png +0 -0
  122. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-16.png +0 -0
  123. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-160.png +0 -0
  124. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-180.png +0 -0
  125. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-192.png +0 -0
  126. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-310.png +0 -0
  127. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-32.png +0 -0
  128. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-57.png +0 -0
  129. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-60.png +0 -0
  130. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-64.png +0 -0
  131. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-70.png +0 -0
  132. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-72.png +0 -0
  133. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-76.png +0 -0
  134. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon-96.png +0 -0
  135. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon.ico +0 -0
  136. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/favicon/favicon.png +0 -0
  137. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/about/statement-details.png +0 -0
  138. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/cards/chase-card.png +0 -0
  139. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/cards/discover-card.png +0 -0
  140. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/cards/new-card.png +0 -0
  141. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/cards/template-card.png +0 -0
  142. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/arrow-down.png +0 -0
  143. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/arrow-left.png +0 -0
  144. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/arrow-up.png +0 -0
  145. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/checkmark.png +0 -0
  146. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
  147. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/delete-orange.png +0 -0
  148. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/delete-thick.png +0 -0
  149. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/delete.png +0 -0
  150. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/edit.png +0 -0
  151. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/link.png +0 -0
  152. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/minus-thick.png +0 -0
  153. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/minus.png +0 -0
  154. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/plus-thick.png +0 -0
  155. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/plus.png +0 -0
  156. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/refresh.png +0 -0
  157. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/save.png +0 -0
  158. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/sort-asc.png +0 -0
  159. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/sort-desc.png +0 -0
  160. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/statement-pair.png +0 -0
  161. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/statement-thick.png +0 -0
  162. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/statement.png +0 -0
  163. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/icons/x-thick.png +0 -0
  164. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/img/statement.png +0 -0
  165. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/add-subtransaction.js +0 -0
  166. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/autocomplete-transaction.js +0 -0
  167. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/define-filter.js +0 -0
  168. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
  169. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/display-new-bank-inputs.js +0 -0
  170. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
  171. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/expand-bank-account.js +0 -0
  172. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/expand-bank.js +0 -0
  173. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/expand-transaction.js +0 -0
  174. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/flip-card.js +0 -0
  175. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/hide-homepage-block.js +0 -0
  176. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/highlight-discrepant-transactions.js +0 -0
  177. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/infer-card.js +0 -0
  178. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/infer-statement.js +0 -0
  179. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/make-payment.js +0 -0
  180. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/ajax.js +0 -0
  181. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/autocomplete-input.js +0 -0
  182. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/expand-box-row.js +0 -0
  183. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/form-suggestions.js +0 -0
  184. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
  185. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/manage-overlays.js +0 -0
  186. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/manage-subforms.js +0 -0
  187. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/modules/update-database-widget.js +0 -0
  188. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/show-credit-activity-loader.js +0 -0
  189. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/show-linked-transaction.js +0 -0
  190. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/toggle-navigation.js +0 -0
  191. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-account-statement-parameters.js +0 -0
  192. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-bank-name.js +0 -0
  193. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-card-status.js +0 -0
  194. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-statement-parameters.js +0 -0
  195. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/update-statements-display.js +0 -0
  196. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/use-suggested-amount.js +0 -0
  197. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/static/js/use-suggested-merchant.js +0 -0
  198. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/auth/change_password.html +0 -0
  199. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/auth/login.html +0 -0
  200. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/auth/register.html +0 -0
  201. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_form/account_form.html +0 -0
  202. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/account_summaries_page.html +0 -0
  203. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
  204. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transaction_form.html +0 -0
  205. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
  206. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
  207. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
  208. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
  209. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
  210. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
  211. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
  212. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/form_page.html +0 -0
  213. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transaction_form/subform.html +0 -0
  214. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
  215. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
  216. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
  217. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
  218. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
  219. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
  220. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
  221. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/credits.html +0 -0
  222. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/400.html +0 -0
  223. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/401.html +0 -0
  224. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/403.html +0 -0
  225. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/404.html +0 -0
  226. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/405.html +0 -0
  227. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/408.html +0 -0
  228. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/418.html +0 -0
  229. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/425.html +0 -0
  230. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/500.html +0 -0
  231. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/errors/error.html +0 -0
  232. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/core/story.html +0 -0
  233. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_form/card_form.html +0 -0
  234. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
  235. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_graphic/card_back.html +0 -0
  236. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
  237. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/card_submission_page.html +0 -0
  238. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/cards.html +0 -0
  239. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/cards_page.html +0 -0
  240. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/discrepant_records.html +0 -0
  241. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html +0 -0
  242. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/summary.html +0 -0
  243. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statement_reconciliation/unrecorded_activities.html +0 -0
  244. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/statements.html +0 -0
  245. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
  246. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
  247. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
  248. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +9 -9
  249. {monopyly-1.5.2 → monopyly-1.6.1}/monopyly/templates/credit/transactions_table/transaction_field_titles.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: monopyly
3
- Version: 1.5.2
3
+ Version: 1.6.1
4
4
  Summary: A homemade personal finance manager.
5
5
  Project-URL: Download, https://pypi.org/project/monopyly
6
6
  Project-URL: Homepage, http://monopyly.com
@@ -22,16 +22,16 @@ Classifier: Programming Language :: Python
22
22
  Classifier: Topic :: Office/Business :: Financial
23
23
  Classifier: Topic :: Office/Business :: Financial :: Accounting
24
24
  Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
25
- Requires-Python: <3.11,>=3.10
26
- Requires-Dist: dry-foundation==1.3.0
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: dry-foundation==1.6.0
27
27
  Requires-Dist: flask-wtf==1.2.2
28
28
  Requires-Dist: flask==3.1.2
29
29
  Requires-Dist: gunicorn==23.0.0
30
- Requires-Dist: markdown==3.9
31
- Requires-Dist: nltk==3.9.1
30
+ Requires-Dist: markdown==3.10
31
+ Requires-Dist: nltk==3.9.2
32
32
  Requires-Dist: python-dateutil==2.9.0
33
- Requires-Dist: rich==14.1.0
34
- Requires-Dist: sqlalchemy==2.0.43
33
+ Requires-Dist: rich==14.2.0
34
+ Requires-Dist: sqlalchemy==2.0.45
35
35
  Description-Content-Type: text/markdown
36
36
 
37
37
  <div id="header">
@@ -159,7 +159,7 @@ Card balances are also visible by visiting the pages for individual statements.
159
159
  A full history of statements for each card is available off the homepage.
160
160
  Each statement's page gives the statement's balance, transactions, and due date.
161
161
 
162
- <img class="screenshot" src="https://raw.githubusercontent.com/mitchnegus/monopyly/main/monopyly/static/img/about/statement-details.png" alt="statement details" width="800px" />
162
+ <img class="screenshot" src="https://raw.githubusercontent.com/mitchnegus/monopyly/main/monopyly/static/img/about/credit-statement-details.png" alt="statement details" width="800px" />
163
163
 
164
164
  Payments can be made directly from a statement's page and can be linked to a bank account in the _Monopyly_ system for simplified tracking.
165
165
  (Note that even linked transactions must be edited independently, as there are times when a user may wish to have separate values for linked transactions. For example, a credit card payment may be processed on a given date while it is only registered as a bank account transaction several days later.)
@@ -222,4 +222,26 @@
222
222
  - Bump dependencies (including support for recent SQLAlchemy versions)
223
223
 
224
224
 
225
+ ### 1.6.0
226
+
227
+ - Enable additional transactions to be loaded on bank/credit transactions tables
228
+ - Create a script to take application screenshots (e.g., for the 'About' page)
229
+ - Remove Python version requirement (allow Python versions after 3.10)
230
+ - Use Jinja recursive loops for transaction tag tree structures in templates
231
+ - Refactor to include ruff-based linting checks
232
+ - Set 'Credit payments' to be a default tag for all users
233
+ - Protect globally defined tags from user deletion
234
+ - Hide transaction tags when the Escape key is pressed
235
+ - Ensure that the 'Record Transfer' functionality only ever adds one input box
236
+ - Update dependencies (including using ruff in place of _Black_ and _isort_)
237
+ - Update JavaScript libraries (jQuery, Chartist)
238
+
239
+
240
+ ### 1.6.1
241
+
242
+ - Allow pie chart labels to overflow the SVG container
243
+ - Define JavaScript elements in header (deferring execution when logical)
244
+ - Bump dependencies
245
+
246
+
225
247
  <a name="bottom" id="bottom"></a>
@@ -123,7 +123,7 @@ Card balances are also visible by visiting the pages for individual statements.
123
123
  A full history of statements for each card is available off the homepage.
124
124
  Each statement's page gives the statement's balance, transactions, and due date.
125
125
 
126
- <img class="screenshot" src="monopyly/static/img/about/statement-details.png" alt="statement details" width="800px" />
126
+ <img class="screenshot" src="monopyly/static/img/about/credit-statement-details.png" alt="statement details" width="800px" />
127
127
 
128
128
  Payments can be made directly from a statement's page and can be linked to a bank account in the _Monopyly_ system for simplified tracking.
129
129
  (Note that even linked transactions must be edited independently, as there are times when a user may wish to have separate values for linked transactions. For example, a credit card payment may be processed on a given date while it is only registered as a bank account transaction several days later.)
@@ -8,7 +8,7 @@ from .core.errors import render_error_template
8
8
  from .database import SQLAlchemy
9
9
 
10
10
 
11
- @Factory(db_interface=SQLAlchemy)
11
+ @Factory(db_interface=SQLAlchemy, echo_engine=False)
12
12
  def create_app(config=None):
13
13
  """
14
14
  Create the Flask application.
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.5.2'
16
- __version_tuple__ = version_tuple = (1, 5, 2)
15
+ __version__ = version = '1.6.1'
16
+ __version_tuple__ = version_tuple = (1, 6, 1)
@@ -9,3 +9,5 @@ bp = Blueprint("auth", __name__, url_prefix="/auth")
9
9
 
10
10
  # Import routes after defining blueprint to avoid circular imports
11
11
  from . import routes
12
+
13
+ __all__ = ["routes"]
@@ -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 user := identify_user(username):
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 werkzeug.exceptions import abort
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 _validate_authorization(cls, entry_id):
118
- account_type = super()._validate_authorization(entry_id)
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 UserList, namedtuple
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 : list
34
- A list containing (x, y) pairs, each consisting of the Unix
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 list(_BalanceChartData(transactions))
38
+ return _BalanceChartData(transactions).data
38
39
 
39
40
 
40
- class _BalanceChartData(UserList):
41
+ class _BalanceChartData(UserDict):
41
42
  """
42
- A list of balances to be passed to a `chartist.js` chart constructor.
43
+ A mapping of balances to be passed to a `chartist.js` chart constructor.
43
44
 
44
- A special list-like object containing transaction data formatted for
45
- use in a balance chart created by the `chartist.js` library. This
46
- converts each transaction into an (x, y) pair consisting of a Unix
47
- timestamp (in milleseconds) and a corresponding bank account
48
- balance. For transactions occurring on the same day (the finest
49
- granularity recorded by the Monopyly app), a slight offset is
50
- added to each timestamp to guarantee a smooth representation in the
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
- self.data.append((adjusted_timestamp, transaction.balance))
87
+ chart_data.append({"x": adjusted_timestamp, "y": transaction.balance})
88
+ return chart_data
@@ -9,3 +9,5 @@ bp = Blueprint("banking", __name__, url_prefix="/banking")
9
9
 
10
10
  # Import routes after defining blueprint to avoid circular imports
11
11
  from . import filters, routes
12
+
13
+ __all__ = ["filters", "routes"]
@@ -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(transaction):
10
- """Check if the transfer is linked ot another transaction at the same bank."""
11
- if transaction.internal_transaction:
12
- linked_bank_transactions = transaction.internal_transaction.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].account.bank_id
14
+ common_bank_id = linked_bank_transactions[0].account_view.bank_id
15
15
  return all(
16
- transaction.account.bank_id == common_bank_id
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, StringField, SubmitField
5
+ from wtforms.fields import BooleanField, FieldList, FormField, SubmitField
6
6
  from wtforms.validators import DataRequired, Optional
7
7
 
8
8
  from ..common.forms import AcquisitionSubform, EntryForm, EntrySubform, TransactionForm
9
9
  from ..common.forms.fields import (
10
10
  CustomChoiceSelectField,
11
- DateField,
12
11
  LastFourDigitsField,
13
12
  StringField,
14
13
  )
15
14
  from ..common.forms.utils import Autocompleter
16
- from ..common.utils import parse_date
17
15
  from ..database.models import (
18
16
  Bank,
19
17
  BankAccountTypeView,
@@ -117,6 +115,10 @@ class BankAccountForm(EntryForm):
117
115
  }
118
116
  return account_type_data
119
117
 
118
+ def gather_entry_data(self, entry):
119
+ """Gather data for the form from the given database entry."""
120
+ raise NotImplementedError
121
+
120
122
  # Fields to identify the bank/account type information for the account
121
123
  bank_info = FormField(BankSubform)
122
124
  account_type_info = FormField(AccountTypeSubform)
@@ -179,7 +181,7 @@ class BankTransactionForm(TransactionForm):
179
181
  data = {
180
182
  "bank_name": entry.bank.bank_name,
181
183
  "last_four_digits": entry.last_four_digits,
182
- "type_name": entry.account_type.type_name,
184
+ "type_name": entry.account_type_view.type_name,
183
185
  }
184
186
  elif isinstance(entry, Bank):
185
187
  data = {"bank_name": entry.bank_name}
@@ -278,7 +280,7 @@ class BankTransactionForm(TransactionForm):
278
280
  """Gather data for the form from the given database entry."""
279
281
  if isinstance(entry, BankTransactionView):
280
282
  data = self._gather_transaction_data(entry)
281
- account_info = entry.account
283
+ account_info = entry.account_view
282
284
  # Do not prepopulate any transfer information
283
285
  elif isinstance(entry, (BankAccountView, Bank)):
284
286
  data = {}
@@ -3,7 +3,7 @@ Routes for banking financials.
3
3
  """
4
4
 
5
5
  from dry_foundation.database import db_transaction
6
- from flask import jsonify, redirect, render_template, request, url_for
6
+ from flask import g, jsonify, redirect, render_template, request, url_for
7
7
 
8
8
  from ..auth.tools import login_required
9
9
  from ..common.forms.utils import extend_field_list_for_ajax
@@ -13,7 +13,10 @@ from .actions import get_balance_chart_data, get_bank_account_type_grouping
13
13
  from .banks import BankHandler
14
14
  from .blueprint import bp
15
15
  from .forms import BankAccountForm, BankTransactionForm
16
- from .transactions import BankTransactionHandler, save_transaction
16
+ from .transactions import BankTagHandler, BankTransactionHandler, save_transaction
17
+
18
+ # Set a limit on the number of transactions loaded at one time for certain routes
19
+ TRANSACTION_LIMIT = 100
17
20
 
18
21
 
19
22
  @bp.route("/accounts")
@@ -34,7 +37,7 @@ def add_account(bank_id):
34
37
  form = BankAccountForm()
35
38
  # Check if an account was submitted and add it to the database
36
39
  if request.method == "POST":
37
- account = save_account(form)
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 = list(
73
- BankTransactionHandler.get_transactions(
74
- account_ids=(account_id,), sort_order="DESC"
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
- account_transactions=transactions[:100],
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=None, limit=None
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.account.bank.bank_name,
216
+ merchant=transfer.account_view.bank.bank_name,
218
217
  )
219
218
  transaction = BankTransactionHandler.add_entry(**transaction_data)
220
219
  return transaction
@@ -0,0 +1,15 @@
1
+ from ._forms import (
2
+ AcquisitionSubform,
3
+ EntryForm,
4
+ EntrySubform,
5
+ TransactionForm,
6
+ form_err_msg,
7
+ )
8
+
9
+ __all__ = [
10
+ "AcquisitionSubform",
11
+ "EntryForm",
12
+ "EntrySubform",
13
+ "TransactionForm",
14
+ "form_err_msg",
15
+ ]
@@ -2,26 +2,20 @@
2
2
  General form constructions.
3
3
  """
4
4
 
5
- from abc import ABC, abstractmethod
5
+ from abc import abstractmethod
6
6
  from datetime import date
7
7
 
8
- from flask_wtf import FlaskForm
9
- from wtforms.fields import FieldList, FormField, SelectField, StringField, SubmitField
8
+ from dry_foundation.forms import AbstractFlaskForm, FlaskSubform
9
+ from wtforms.fields import StringField, SubmitField
10
10
  from wtforms.validators import DataRequired
11
11
 
12
12
  from .fields import CurrencyField, DateField
13
- from .validators import SelectionNotBlank
14
13
 
15
14
  # Define a custom form error messaage
16
15
  form_err_msg = "There was an improper value in your form. Please try again."
17
16
 
18
17
 
19
- class AbstractEntryFormMixinMeta(type(FlaskForm), ABC):
20
- # Defined to allow the forms to also to be abstract base classes
21
- pass
22
-
23
-
24
- class EntryForm(FlaskForm, metaclass=AbstractEntryFormMixinMeta):
18
+ class EntryForm(AbstractFlaskForm):
25
19
  """
26
20
  A form designed to accept database entry information.
27
21
 
@@ -86,11 +80,8 @@ class EntryForm(FlaskForm, metaclass=AbstractEntryFormMixinMeta):
86
80
  )
87
81
 
88
82
 
89
- class EntrySubform(EntryForm):
90
- """Subform disabling CSRF (CSRF is REQUIRED in encapsulating form)."""
91
-
92
- def __init__(self, *args, **kwargs):
93
- super().__init__(meta={"csrf": False}, *args, **kwargs)
83
+ class EntrySubform(FlaskSubform, EntryForm):
84
+ """A subform implementing ``EntryForm`` behavior."""
94
85
 
95
86
 
96
87
  class AcquisitionSubform(EntrySubform):
@@ -150,7 +141,6 @@ class TransactionForm(EntryForm):
150
141
  }
151
142
  return data
152
143
 
153
- @abstractmethod
154
144
  def gather_entry_data(self, entry):
155
145
  if self.subtransaction_model is None:
156
146
  raise RuntimeError(
@@ -4,12 +4,10 @@ General form constructions.
4
4
 
5
5
  from abc import ABC, abstractmethod
6
6
 
7
- from flask_wtf import FlaskForm
8
7
  from wtforms import fields as wtforms_fields
9
8
  from wtforms.validators import Length
10
9
  from wtforms.widgets import NumberInput
11
10
 
12
- from ...banking.banks import BankHandler
13
11
  from ..utils import parse_date
14
12
  from .validators import NumeralsOnly, SelectionNotBlank
15
13
 
@@ -26,15 +24,6 @@ class DateField(wtforms_fields.DateField):
26
24
  super().__init__(*args, filters=filters, **kwargs)
27
25
 
28
26
 
29
- class OptionalDateField(DateField):
30
- """A date field where the field value is optional."""
31
-
32
- def process_formdata(self, valuelist):
33
- """Process data from the form, ignoring an ommitted value."""
34
- if valuelist != [""]:
35
- super().process_formdata(valuelist)
36
-
37
-
38
27
  class CurrencyField(wtforms_fields.DecimalField):
39
28
  """A decimal field with currency-specific customizations."""
40
29
 
@@ -75,7 +75,7 @@ class Autocompleter:
75
75
  # Get information from the database to use for autocompletion
76
76
  query = model.select_for_user(getattr(model, field))
77
77
  values = current_app.db.session.scalars(query)
78
- suggestions = sort_by_frequency([value for value in values])
78
+ suggestions = sort_by_frequency(list(values))
79
79
  return suggestions
80
80
 
81
81
  def _sort_suggestions_by_field(
@@ -113,9 +113,9 @@ def extend_field_list_for_ajax(form_class, field_list_name, field_list_count):
113
113
 
114
114
  Parameters
115
115
  ----------
116
- form_class : flask_wtf.FlaskForm
117
- The class (not class instance) containing the field list to be
118
- extended.
116
+ form_class : type
117
+ The form class (not class instance) containing the field list to
118
+ be extended.
119
119
  field_list_name : str
120
120
  The name of the field list to be extended.
121
121
  field_list_count : int