diff --git a/front-end/cypress/e2e/transactions/memo-code-checked.cy.ts b/front-end/cypress/e2e/transactions/memo-code-checked.cy.ts index fa9f424e7a..e742893524 100644 --- a/front-end/cypress/e2e/transactions/memo-code-checked.cy.ts +++ b/front-end/cypress/e2e/transactions/memo-code-checked.cy.ts @@ -47,7 +47,7 @@ describe('Tests that memos have the memo_code checkbox checked', () => { enterContact(transaction.contact, true, true); cy.medWait(); - cy.contains('The dollar amount in a memo item is not incorporated into the total figure for the schedule').should( + cy.contains('span', 'The dollar amount in a memo item is not incorporated into the total figures for the schedule').should( 'exist' ); cy.longWait(); diff --git a/front-end/cypress/support/transaction_nav_trees.spec.ts b/front-end/cypress/support/transaction_nav_trees.spec.ts index 5dc45b9360..a545e70db1 100644 --- a/front-end/cypress/support/transaction_nav_trees.spec.ts +++ b/front-end/cypress/support/transaction_nav_trees.spec.ts @@ -45,13 +45,15 @@ export type SchATransactionName = | 'Party National Party Pres. Nominating Convention Account' | 'Tribal National Party Recount/Legal Proceedings Account' | 'Partnership National Party Recount/Legal Proceedings Account' + | 'Partnership National Party Headquarters Buildings Account' | 'Unregistered Receipt from Person - Returned/Bounced Receipt' | 'Partnership Receipt' | 'PAC Returned/Bounced Receipt' | 'Party Returned/Bounced Receipt' | 'Earmark Receipt for Recount/Legal Proceedings Account (Contribution)' | 'Earmark Receipt for Pres. Nominating Convention Account (Contribution)' - | 'Earmark Receipt for Headquarters Buildings Account (Contribution)'; + | 'Earmark Receipt for Headquarters Buildings Account (Contribution)' + | 'Partnership Recount Account Receipt'; export type ChildTransactionName = | 'PAC Joint Fundraising Transfer Memo' @@ -74,9 +76,11 @@ export type ChildTransactionName = | 'PAC National Party Headquarters Buildings Account JF Transfer Memo' | 'Partnership Memo' | 'Partnership National Party Recount/Legal Proceedings Account Memo' + | 'Partnership National Party Headquarters Buildings Account Memo' | 'Earmark Memo for Recount Account (Contribution)' | 'Earmark Memo for Convention Account (Contribution)' - | 'Earmark Memo for Headquarters Account (Contribution)'; + | 'Earmark Memo for Headquarters Account (Contribution)' + | 'Partnership Recount Account Receipt Memo'; export type TransactionGroup = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'AG' | 'FG'; @@ -442,7 +446,7 @@ const tribalNationalPartyRecountAccount: TransactionForm = { }; const partnershipNationalPartyRecountAccount: TransactionForm = { - transaction_name: 'Partnership National Party Recount/Legal Proceedings Account', + transaction_name: 'Partnership National Party Recount/Legal Proceedings Account Memo', transaction_category: 'OTHER', transaction_group: 'D', aggregation_group: 'NATIONAL_PARTY_RECOUNT_ACCOUNT', @@ -465,6 +469,30 @@ const partnershipNationalPartyRecountAccountMemo: ChildTransactionForm = { }, }; +const partnershipNationalPartyHeadquartersAccount: TransactionForm = { + transaction_name: 'Partnership National Party Headquarters Buildings Account', + transaction_category: 'OTHER', + transaction_group: 'D', + aggregation_group: 'NATIONAL_PARTY_HEADQUARTERS_ACCOUNT', + ...entityOrganization, + fields: { + ...memoFields, + ...purposeDescriptionFieldsRequired, + }, +}; + +const partnershipNationalPartyHeadquartersAccountMemo: ChildTransactionForm = { + transaction_name: 'Partnership National Party Headquarters Buildings Account Memo', + transaction_group: 'A', + aggregation_group: 'NATIONAL_PARTY_HEADQUARTERS_ACCOUNT', + ...entityIndividual, + childOf: 'Partnership National Party Headquarters Buildings Account', + fields: { + ...memoFields, + ...contributionFields, + }, +}; + const tribalNPRJFTransMemo: ChildTransactionForm = { transaction_name: 'Tribal National Party Recount/Legal Proceedings Account JF Transfer Memo', transaction_group: 'D', @@ -992,6 +1020,30 @@ const partnershipMemo: ChildTransactionForm = { }, }; +const partnershipRecountAccountReceipt: TransactionForm = { + transaction_name: 'Partnership Recount Account Receipt', + transaction_category: 'OTHER', + transaction_group: 'D', + aggregation_group: 'RECOUNT_ACCOUNT', + ...entityOrganization, + fields: { + ...memoFields, + ...purposeDescriptionFieldsRequired, + }, +}; + +const partnershipRecountAccountReceiptMemo: ChildTransactionForm = { + transaction_name: 'Partnership Recount Account Receipt Memo', + transaction_group: 'A', + aggregation_group: 'RECOUNT_ACCOUNT', + ...entityIndividual, + childOf: 'Partnership Recount Account Receipt', + fields: { + ...memoFields, + ...contributionFields, + }, +}; + const partyReturn: TransactionForm = { transaction_name: 'Party Returned/Bounced Receipt', transaction_category: 'REGISTERED FILERS', @@ -1164,9 +1216,11 @@ export const schedANavTree: TransactionNavTree = { 'Party National Party Pres. Nominating Convention Account': partyNationalPartyConventionAccount, 'Tribal National Party Recount/Legal Proceedings Account': tribalNationalPartyRecountAccount, 'Partnership National Party Recount/Legal Proceedings Account': partnershipNationalPartyRecountAccount, + 'Partnership National Party Headquarters Buildings Account': partnershipNationalPartyHeadquartersAccount, 'Earmark Receipt for Recount/Legal Proceedings Account (Contribution)': earmarkRecountReceipt, 'Earmark Receipt for Pres. Nominating Convention Account (Contribution)': earmarkConventionReceipt, 'Earmark Receipt for Headquarters Buildings Account (Contribution)': earmarkHeadquartersReceipt, + 'Partnership Recount Account Receipt': partnershipRecountAccountReceipt, }, }; @@ -1191,4 +1245,10 @@ export const childTransactionTree = { 'Partnership National Party Recount/Legal Proceedings Account': { 'Partnership National Party Recount/Legal Proceedings Account Memo': partnershipNationalPartyRecountAccountMemo, }, + 'Partnership National Party Headquarters Buildings Account': { + 'Partnership National Party Headquarters Buildings Account Memo': partnershipNationalPartyHeadquartersAccountMemo, + }, + 'Partnership Recount Account Receipt': { + 'Partnership Recount Account Receipt Memo': partnershipRecountAccountReceiptMemo, + }, }; diff --git a/front-end/package-lock.json b/front-end/package-lock.json index 5f735bbebd..7432de6532 100644 --- a/front-end/package-lock.json +++ b/front-end/package-lock.json @@ -22,7 +22,7 @@ "@popperjs/core": "^2.10.2", "bootstrap": "5.1.3", "class-transformer": "^0.5.1", - "fecfile-validate": "https://github.com/fecgov/fecfile-validate#5e102d5ca2dbe965f09fdf66e7d83c88c6cc7976", + "fecfile-validate": "https://github.com/fecgov/fecfile-validate#4f4595f250eb39ecdddf393047a3e763662431b9", "intl-tel-input": "^17.0.18", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", @@ -695,9 +695,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", - "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", "dev": true, "engines": { "node": ">=6.9.0" @@ -824,15 +824,15 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", - "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", + "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", "@babel/helper-replace-supers": "^7.20.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", @@ -846,13 +846,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", - "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", + "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.2.1" + "regexpu-core": "^5.3.1" }, "engines": { "node": ">=6.9.0" @@ -909,13 +909,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -948,12 +948,12 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", - "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", "dev": true, "dependencies": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -972,9 +972,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", @@ -983,8 +983,8 @@ "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" }, "engines": { "node": ">=6.9.0" @@ -1129,9 +1129,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -1167,14 +1167,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", - "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "dependencies": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.13", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -1209,9 +1209,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", - "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1287,12 +1287,12 @@ } }, "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz", - "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.20.7", + "@babel/helper-create-class-features-plugin": "^7.21.0", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -1435,9 +1435,9 @@ } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz", - "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", @@ -1468,13 +1468,13 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", - "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", + "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-create-class-features-plugin": "^7.21.0", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, @@ -1741,9 +1741,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.14.tgz", - "integrity": "sha512-sMPepQtsOs5fM1bwNvuJJHvaCfOEQfmc01FGw0ELlTpTJj5Ql/zuNRRldYhAPys4ghXdBIQJbRVYi44/7QflQQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.20.2" @@ -1756,15 +1756,15 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz", - "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-compilation-targets": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-replace-supers": "^7.20.7", @@ -1871,12 +1871,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", - "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", + "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1949,12 +1949,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", - "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", + "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-module-transforms": "^7.21.2", "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-simple-access": "^7.20.2" }, @@ -2357,6 +2357,12 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, "node_modules/@babel/runtime": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz", @@ -2384,19 +2390,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", - "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.13", - "@babel/types": "^7.20.7", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2405,13 +2411,14 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", - "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "dev": true, "dependencies": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -2433,9 +2440,9 @@ } }, "node_modules/@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.19.4", @@ -2930,15 +2937,39 @@ "node": ">=10.0.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", + "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", + "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", + "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -3042,6 +3073,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/js": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", + "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -3315,9 +3355,9 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "engines": { "node": ">=12" @@ -3391,18 +3431,18 @@ } }, "node_modules/@nrwl/cli": { - "version": "14.8.6", - "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.8.6.tgz", - "integrity": "sha512-R4udxekMd4jhoRPEksJu+224DocOIrAqenFo0D2R36epE5FaCnZQX7xg+b3TjRbdS10e426i4D9LuXdQmP5jJg==", + "version": "14.8.7", + "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.8.7.tgz", + "integrity": "sha512-wkVa2VpAgNs8p3piOtILY622HqPQmZFfcoLeQ1B6OaUWKXDyFEcYIoFmIblXQKhhQVkBHC8NJyMt0zfRkPvVBA==", "dev": true, "dependencies": { - "nx": "14.8.6" + "nx": "14.8.7" } }, "node_modules/@nrwl/devkit": { - "version": "14.8.6", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-14.8.6.tgz", - "integrity": "sha512-+3KqohOKeUuyS176jrwY0yeB3E2IFQ3jMkS0KizzsHGsZWdZbQ2WQ46hZ0/bvRh9Efl8CAg6n4fUWR0BXUePMA==", + "version": "14.8.7", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-14.8.7.tgz", + "integrity": "sha512-VTE+1IAdv/S3r7VzX/3Mlyyfzbq7mpQxk9mDrm6lSQVsMbOht8ItlZG8fO/viCQjx7LWu0BzyR8g0aUC3OmVpA==", "dev": true, "dependencies": { "@phenomnomnominal/tsquery": "4.1.1", @@ -3415,12 +3455,12 @@ } }, "node_modules/@nrwl/tao": { - "version": "14.8.6", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.8.6.tgz", - "integrity": "sha512-CByqrsfSJeonOd7TLAHP8bRYNWgDksxA7j+yncSzgQnFLEbZdJGG/AqqIovx8g6g2v0JS+nRgGC+w5UPf04UrQ==", + "version": "14.8.7", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.8.7.tgz", + "integrity": "sha512-M6uro//M7zebnemDHwbR0dFTTOG6Y332MPknYNnr3FYgmqt/BdNoA7vXia6BdrfP6uNqKCPY/HMgRW0tiMUzcg==", "dev": true, "dependencies": { - "nx": "14.8.6" + "nx": "14.8.7" }, "bin": { "tao": "index.js" @@ -3665,9 +3705,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.4.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", - "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.1.tgz", + "integrity": "sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==", "dev": true, "dependencies": { "@types/estree": "*", @@ -3691,13 +3731,13 @@ "dev": true }, "node_modules/@types/express": { - "version": "4.17.16", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.16.tgz", - "integrity": "sha512-LkKpqRZ7zqXJuvoELakaFYuETHjZkSol8EV6cNnyishutDBCCdv6+dsKPbKkCcIk57qRphOLY5sEgClw1bO3gA==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", "dev": true, "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.31", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } @@ -3714,9 +3754,9 @@ } }, "node_modules/@types/http-proxy": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", - "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "version": "1.17.10", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz", + "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==", "dev": true, "dependencies": { "@types/node": "*" @@ -3816,9 +3856,9 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", "dev": true, "dependencies": { "@types/mime": "*", @@ -4336,9 +4376,9 @@ "dev": true }, "node_modules/@yarnpkg/parsers": { - "version": "3.0.0-rc.37", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.37.tgz", - "integrity": "sha512-MPKHrD11PgNExFMCXcgA/MnfYbITbiHYQjB8TNZmE4t9Z+zRCB1RTJKOppp8K8QOf+OEo8CybufVNcZZMLt2tw==", + "version": "3.0.0-rc.40", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.40.tgz", + "integrity": "sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q==", "dev": true, "dependencies": { "js-yaml": "^3.10.0", @@ -4461,28 +4501,19 @@ } }, "node_modules/agentkeepalive": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", - "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", "dev": true, "dependencies": { "debug": "^4.1.0", - "depd": "^1.1.2", + "depd": "^2.0.0", "humanize-ms": "^1.2.1" }, "engines": { "node": ">= 8.0.0" } }, - "node_modules/agentkeepalive/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -4758,9 +4789,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", "dev": true, "funding": [ { @@ -4773,8 +4804,8 @@ } ], "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -4815,9 +4846,9 @@ } }, "node_modules/axios": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.0.tgz", - "integrity": "sha512-oCye5nHhTypzkdLIvF9SaHfr8UAquqCn1KY3j8vsrjeol8yohAdGxIpRPbF1bOLsx33HOAatdfMX1yzsj2cHwg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", @@ -5043,13 +5074,13 @@ "dev": true }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -5057,7 +5088,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -5269,9 +5300,9 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "engines": { "node": ">=12" @@ -5318,9 +5349,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001450", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", - "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", + "version": "1.0.30001465", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001465.tgz", + "integrity": "sha512-HvjgL3MYAJjceTDCcjRnQGjwUz/5qec9n7JPOzUursUoOTIsYCSDOb1l7RsnZE8mjbxG78zVRCKfrBXyvChBag==", "dev": true, "funding": [ { @@ -5444,9 +5475,9 @@ } }, "node_modules/ci-info": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", - "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true, "funding": [ { @@ -5914,12 +5945,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.27.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", - "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", + "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", "dev": true, "dependencies": { - "browserslist": "^4.21.4" + "browserslist": "^4.21.5" }, "funding": { "type": "opencollective", @@ -6315,9 +6346,9 @@ } }, "node_modules/cypress-mochawesome-reporter": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.2.3.tgz", - "integrity": "sha512-gUtIYBH+KvnAeCkTFu/t9niX1KHnAbEZyxBFvEHQRtrQNXLERjLJjGOHIW6kuESIdVMiEFOcvuNF2LuBpORW9A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.3.0.tgz", + "integrity": "sha512-X4HU1JpuB62MXLh46660KmIs/L6noWV2KpxaXPDorz1zwgj26NN+BPCLP80D9cCFUwX3hNH0pKFZDwVR7vM8wg==", "dev": true, "dependencies": { "fs-extra": "^10.0.1", @@ -6328,6 +6359,9 @@ "engines": { "node": ">=14" }, + "funding": { + "url": "https://github.com/sponsors/LironEr" + }, "peerDependencies": { "cypress": ">=6.2.0" } @@ -6347,9 +6381,9 @@ } }, "node_modules/cypress/node_modules/@types/node": { - "version": "14.18.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz", - "integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==", + "version": "14.18.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.37.tgz", + "integrity": "sha512-7GgtHCs/QZrBrDzgIJnQtuSvhFSwhyYSI2uafSwZoNt1iOGhEN5fwNrQMjtONyHm9+/LoA4453jH0CMYcr06Pg==", "dev": true }, "node_modules/cypress/node_modules/ansi-styles": { @@ -6845,9 +6879,9 @@ "dev": true }, "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "dev": true, "dependencies": { "jake": "^10.8.5" @@ -6860,9 +6894,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.328", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.328.tgz", + "integrity": "sha512-DE9tTy2PNmy1v55AZAO542ui+MLC2cvINMK4P2LXGsJdput/ThVG9t+QGecPuAZZSgC8XoI+Jh9M1OG9IoNSCw==", "dev": true }, "node_modules/emoji-regex": { @@ -6922,9 +6956,9 @@ } }, "node_modules/engine.io": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", - "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", + "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -6936,7 +6970,7 @@ "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" + "ws": "~8.11.0" }, "engines": { "node": ">=10.0.0" @@ -6952,9 +6986,9 @@ } }, "node_modules/engine.io/node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "dev": true, "engines": { "node": ">=10.0.0" @@ -7448,12 +7482,15 @@ } }, "node_modules/eslint": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", - "integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", + "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.1", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.1", + "@eslint/js": "8.36.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -7464,10 +7501,9 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "espree": "^9.5.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", @@ -7488,7 +7524,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -7819,9 +7854,9 @@ } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", "dev": true, "dependencies": { "acorn": "^8.8.0", @@ -7849,9 +7884,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -8027,6 +8062,30 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/express/node_modules/cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -8084,6 +8143,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/express/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -8219,8 +8293,8 @@ }, "node_modules/fecfile-validate": { "version": "0.0.1", - "resolved": "git+ssh://git@github.com/fecgov/fecfile-validate.git#5e102d5ca2dbe965f09fdf66e7d83c88c6cc7976", - "integrity": "sha512-CgPU46XI9ieniP2jEnzIL89NQMbsB2/O0pdzPPodyhiqwATw0QF2Zyx+6mTCdfx71LijmVpXfnUSoHpQ7dtjKg==", + "resolved": "git+ssh://git@github.com/fecgov/fecfile-validate.git#4f4595f250eb39ecdddf393047a3e763662431b9", + "integrity": "sha512-ouYi7GGsPsn31xWciY63B0DmY2RcwjmZ3xa1tzByE6aUvvKjFmgB2b2CBrm+qakbmMi4eZNSdP8qq5znHwne9Q==", "hasInstallScript": true, "license": "CC0-1.0", "dependencies": { @@ -8809,9 +8883,9 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "engines": { "node": ">=12" @@ -8830,9 +8904,9 @@ } }, "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { "core-util-is": "~1.0.0", @@ -9102,9 +9176,9 @@ "dev": true }, "node_modules/immutable": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.2.tgz", - "integrity": "sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", + "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", "dev": true }, "node_modules/import-fresh": { @@ -9294,9 +9368,9 @@ "dev": true }, "node_modules/intl-tel-input": { - "version": "17.0.19", - "resolved": "https://registry.npmjs.org/intl-tel-input/-/intl-tel-input-17.0.19.tgz", - "integrity": "sha512-GBNoUT4JVgm2e1N+yFMaBQ24g5EQfZhDznGneCM9IEZwfKsMUAUa1dS+v0wOiKpRAZ5IPNLJMIEEFGgqlCI22A==" + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/intl-tel-input/-/intl-tel-input-17.0.21.tgz", + "integrity": "sha512-TfyPxLe41QZPOf6RqBxRE2dpQ0FThB/PBD/gRbxVhGW7IuYg30QD90x/vjmEo4vkZw7j8etxpVcjIZVRcG+Otw==" }, "node_modules/ip": { "version": "2.0.0", @@ -10816,16 +10890,16 @@ } }, "node_modules/log4js": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.1.tgz", - "integrity": "sha512-lzbd0Eq1HRdWM2abSD7mk6YIVY0AogGJzb/z+lqzRk+8+XJP+M6L1MS5FUSc3jjGru4dbKjEMJmqlsoYYpuivQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", "dev": true, "dependencies": { "date-format": "^4.0.14", "debug": "^4.3.4", "flatted": "^3.2.7", "rfdc": "^1.3.0", - "streamroller": "^3.1.3" + "streamroller": "^3.1.5" }, "engines": { "node": ">=8.0" @@ -10936,9 +11010,9 @@ } }, "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "engines": { "node": ">=12" @@ -11119,9 +11193,9 @@ } }, "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11558,9 +11632,9 @@ } }, "node_modules/mochawesome-merge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/mochawesome-merge/-/mochawesome-merge-4.2.2.tgz", - "integrity": "sha512-ZHeZcChGhb3If7fjSVuyB9rmDH86inNtsTb1ONYq1h0L1IduldFu38bJDcow46alMpiYQgJ7cPhv6nwpCwbJQw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mochawesome-merge/-/mochawesome-merge-4.3.0.tgz", + "integrity": "sha512-1roR6g+VUlfdaRmL8dCiVpKiaUhbPVm1ZQYUM6zHX46mWk+tpsKVZR6ba98k2zc8nlPvYd71yn5gyH970pKBSw==", "dev": true, "dependencies": { "fs-extra": "^7.0.1", @@ -12214,9 +12288,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.9.tgz", - "integrity": "sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, "node_modules/nopt": { @@ -12419,14 +12493,14 @@ } }, "node_modules/nx": { - "version": "14.8.6", - "resolved": "https://registry.npmjs.org/nx/-/nx-14.8.6.tgz", - "integrity": "sha512-QLU3sip/g3JdNO8n5Nw2esN+0G26Jsy3u1LlrB9Giu4zf/+KsfN8CcXMbEVqOnPR1FkCS52xliaq7IBQfvvMQA==", + "version": "14.8.7", + "resolved": "https://registry.npmjs.org/nx/-/nx-14.8.7.tgz", + "integrity": "sha512-5Q+u2mcrI4l54VR7trtp3KBNAZknIAi3XFka5h7lp3m1gZbt++Ij27GCLleKPgOVnDRs0fFayyNgpKTGGHRq1A==", "dev": true, "hasInstallScript": true, "dependencies": { - "@nrwl/cli": "14.8.6", - "@nrwl/tao": "14.8.6", + "@nrwl/cli": "14.8.7", + "@nrwl/tao": "14.8.7", "@parcel/watcher": "2.0.4", "@yarnpkg/lockfile": "^1.1.0", "@yarnpkg/parsers": "^3.0.0-rc.18", @@ -14222,9 +14296,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", @@ -14387,9 +14461,9 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -14482,14 +14556,14 @@ } }, "node_modules/regexpu-core": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", - "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, "dependencies": { + "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.1.0", - "regjsgen": "^0.7.1", "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" @@ -14498,12 +14572,6 @@ "node": ">=4" } }, - "node_modules/regjsgen": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", - "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", - "dev": true - }, "node_modules/regjsparser": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", @@ -14724,12 +14792,12 @@ } }, "node_modules/robots-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.0.tgz", - "integrity": "sha512-6xkze3WRdneibICBAzMKcXyTKQw5shA3GbwoEJy7RSvxpZNGF0GMuYKE1T0VMP4fwx/fQs0n0mtriOqRtk5L1w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.1.tgz", + "integrity": "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==", "dev": true, "engines": { - "node": ">=0.10" + "node": ">=10.0.0" } }, "node_modules/run-async": { @@ -15259,16 +15327,16 @@ } }, "node_modules/socket.io": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz", - "integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", - "engine.io": "~6.2.1", - "socket.io-adapter": "~2.4.0", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.1" }, "engines": { @@ -15276,10 +15344,34 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", - "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", - "dev": true + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } }, "node_modules/socket.io-parser": { "version": "4.2.2", @@ -15433,9 +15525,9 @@ } }, "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -15459,9 +15551,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, "node_modules/spdx-ranges": { @@ -15578,9 +15670,9 @@ } }, "node_modules/streamroller": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.4.tgz", - "integrity": "sha512-Ha1Ccw2/N5C/IF8Do6zgNe8F3jQo8MPBnMBGvX0QjNv/I97BcNRzK6/mzOpZHHK7DjMLTI3c7Xw7Y1KvdChkvw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", "dev": true, "dependencies": { "date-format": "^4.0.14", @@ -15882,9 +15974,9 @@ } }, "node_modules/tar/node_modules/minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.1.tgz", - "integrity": "sha512-V9esFpNbK0arbN3fm2sxDKqMYgIp7XtVdE4Esj+PE4Qaaxdg1wIw48ITQIOn1sc8xXSmUviVL3cyjMqPlrVkiA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", "dev": true, "engines": { "node": ">=8" @@ -15930,16 +16022,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", + "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.5" }, "engines": { "node": ">= 10.13.0" @@ -15988,6 +16080,12 @@ "ajv": "^6.9.1" } }, + "node_modules/terser-webpack-plugin/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -16012,6 +16110,24 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz", + "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -16184,13 +16300,13 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } @@ -16317,9 +16433,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.34.tgz", + "integrity": "sha512-cJMeh/eOILyGu0ejgTKB95yKT3zOenSe9UGE3vj6WfiOwgGYnmATUsnDixMFvdU+rNMvWih83hrUP8VwhF9yXQ==", "dev": true, "funding": [ { @@ -16535,9 +16651,9 @@ } }, "node_modules/validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", "dev": true, "engines": { "node": ">= 0.10" @@ -16781,9 +16897,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", - "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "dev": true, "engines": { "node": ">=10.0.0" @@ -17624,9 +17740,9 @@ } }, "@babel/compat-data": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz", - "integrity": "sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", "dev": true }, "@babel/core": { @@ -17725,15 +17841,15 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", - "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz", + "integrity": "sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.20.7", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", "@babel/helper-replace-supers": "^7.20.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", @@ -17741,13 +17857,13 @@ } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", - "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz", + "integrity": "sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.2.1" + "regexpu-core": "^5.3.1" } }, "@babel/helper-define-polyfill-provider": { @@ -17788,13 +17904,13 @@ } }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" }, "dependencies": { "@babel/template": { @@ -17820,12 +17936,12 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", - "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz", + "integrity": "sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==", "dev": true, "requires": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.21.0" } }, "@babel/helper-module-imports": { @@ -17838,9 +17954,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", - "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", @@ -17849,8 +17965,8 @@ "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.10", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" }, "dependencies": { "@babel/template": { @@ -17960,9 +18076,9 @@ "dev": true }, "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", "dev": true }, "@babel/helper-wrap-function": { @@ -17991,14 +18107,14 @@ } }, "@babel/helpers": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz", - "integrity": "sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "requires": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.13", - "@babel/types": "^7.20.7" + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" }, "dependencies": { "@babel/template": { @@ -18026,9 +18142,9 @@ } }, "@babel/parser": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", - "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -18074,12 +18190,12 @@ } }, "@babel/plugin-proposal-class-static-block": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz", - "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.20.7", + "@babel/helper-create-class-features-plugin": "^7.21.0", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-class-static-block": "^7.14.5" } @@ -18168,9 +18284,9 @@ } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz", - "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.20.2", @@ -18189,13 +18305,13 @@ } }, "@babel/plugin-proposal-private-property-in-object": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", - "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", + "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-create-class-features-plugin": "^7.21.0", "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } @@ -18375,24 +18491,24 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.14.tgz", - "integrity": "sha512-sMPepQtsOs5fM1bwNvuJJHvaCfOEQfmc01FGw0ELlTpTJj5Ql/zuNRRldYhAPys4ghXdBIQJbRVYi44/7QflQQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-classes": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz", - "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-compilation-targets": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-optimise-call-expression": "^7.18.6", "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-replace-supers": "^7.20.7", @@ -18462,12 +18578,12 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", - "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz", + "integrity": "sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-function-name": { @@ -18510,12 +18626,12 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", - "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz", + "integrity": "sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-module-transforms": "^7.21.2", "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-simple-access": "^7.20.2" } @@ -18799,6 +18915,12 @@ "esutils": "^2.0.2" } }, + "@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, "@babel/runtime": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz", @@ -18820,31 +18942,32 @@ } }, "@babel/traverse": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz", - "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.13", - "@babel/types": "^7.20.7", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" }, "dependencies": { "@babel/generator": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", - "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "dev": true, "requires": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" } }, @@ -18862,9 +18985,9 @@ } }, "@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dev": true, "requires": { "@babel/helper-string-parser": "^7.19.4", @@ -19184,15 +19307,30 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, + "@eslint-community/eslint-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", + "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", + "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", + "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -19270,6 +19408,12 @@ } } }, + "@eslint/js": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", + "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "dev": true + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -19483,9 +19627,9 @@ }, "dependencies": { "lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true } } @@ -19539,18 +19683,18 @@ } }, "@nrwl/cli": { - "version": "14.8.6", - "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.8.6.tgz", - "integrity": "sha512-R4udxekMd4jhoRPEksJu+224DocOIrAqenFo0D2R36epE5FaCnZQX7xg+b3TjRbdS10e426i4D9LuXdQmP5jJg==", + "version": "14.8.7", + "resolved": "https://registry.npmjs.org/@nrwl/cli/-/cli-14.8.7.tgz", + "integrity": "sha512-wkVa2VpAgNs8p3piOtILY622HqPQmZFfcoLeQ1B6OaUWKXDyFEcYIoFmIblXQKhhQVkBHC8NJyMt0zfRkPvVBA==", "dev": true, "requires": { - "nx": "14.8.6" + "nx": "14.8.7" } }, "@nrwl/devkit": { - "version": "14.8.6", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-14.8.6.tgz", - "integrity": "sha512-+3KqohOKeUuyS176jrwY0yeB3E2IFQ3jMkS0KizzsHGsZWdZbQ2WQ46hZ0/bvRh9Efl8CAg6n4fUWR0BXUePMA==", + "version": "14.8.7", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-14.8.7.tgz", + "integrity": "sha512-VTE+1IAdv/S3r7VzX/3Mlyyfzbq7mpQxk9mDrm6lSQVsMbOht8ItlZG8fO/viCQjx7LWu0BzyR8g0aUC3OmVpA==", "dev": true, "requires": { "@phenomnomnominal/tsquery": "4.1.1", @@ -19560,12 +19704,12 @@ } }, "@nrwl/tao": { - "version": "14.8.6", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.8.6.tgz", - "integrity": "sha512-CByqrsfSJeonOd7TLAHP8bRYNWgDksxA7j+yncSzgQnFLEbZdJGG/AqqIovx8g6g2v0JS+nRgGC+w5UPf04UrQ==", + "version": "14.8.7", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-14.8.7.tgz", + "integrity": "sha512-M6uro//M7zebnemDHwbR0dFTTOG6Y332MPknYNnr3FYgmqt/BdNoA7vXia6BdrfP6uNqKCPY/HMgRW0tiMUzcg==", "dev": true, "requires": { - "nx": "14.8.6" + "nx": "14.8.7" } }, "@parcel/watcher": { @@ -19776,9 +19920,9 @@ } }, "@types/eslint": { - "version": "8.4.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", - "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.1.tgz", + "integrity": "sha512-rc9K8ZpVjNcLs8Fp0dkozd5Pt2Apk1glO4Vgz8ix1u6yFByxfqo5Yavpy65o+93TAe24jr7v+eSBtFLvOQtCRQ==", "dev": true, "requires": { "@types/estree": "*", @@ -19802,13 +19946,13 @@ "dev": true }, "@types/express": { - "version": "4.17.16", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.16.tgz", - "integrity": "sha512-LkKpqRZ7zqXJuvoELakaFYuETHjZkSol8EV6cNnyishutDBCCdv6+dsKPbKkCcIk57qRphOLY5sEgClw1bO3gA==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", "dev": true, "requires": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.31", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } @@ -19825,9 +19969,9 @@ } }, "@types/http-proxy": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", - "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "version": "1.17.10", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz", + "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==", "dev": true, "requires": { "@types/node": "*" @@ -19927,9 +20071,9 @@ } }, "@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", + "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", "dev": true, "requires": { "@types/mime": "*", @@ -20311,9 +20455,9 @@ "dev": true }, "@yarnpkg/parsers": { - "version": "3.0.0-rc.37", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.37.tgz", - "integrity": "sha512-MPKHrD11PgNExFMCXcgA/MnfYbITbiHYQjB8TNZmE4t9Z+zRCB1RTJKOppp8K8QOf+OEo8CybufVNcZZMLt2tw==", + "version": "3.0.0-rc.40", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.40.tgz", + "integrity": "sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q==", "dev": true, "requires": { "js-yaml": "^3.10.0", @@ -20412,22 +20556,14 @@ } }, "agentkeepalive": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", - "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", "dev": true, "requires": { "debug": "^4.1.0", - "depd": "^1.1.2", + "depd": "^2.0.0", "humanize-ms": "^1.2.1" - }, - "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true - } } }, "aggregate-error": { @@ -20622,13 +20758,13 @@ "dev": true }, "autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", "dev": true, "requires": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -20654,9 +20790,9 @@ "dev": true }, "axios": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.0.tgz", - "integrity": "sha512-oCye5nHhTypzkdLIvF9SaHfr8UAquqCn1KY3j8vsrjeol8yohAdGxIpRPbF1bOLsx33HOAatdfMX1yzsj2cHwg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", "dev": true, "requires": { "follow-redirects": "^1.15.0", @@ -20834,13 +20970,13 @@ "dev": true }, "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "requires": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -20848,7 +20984,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -21004,9 +21140,9 @@ }, "dependencies": { "lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true } } @@ -21040,9 +21176,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001450", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz", - "integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==", + "version": "1.0.30001465", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001465.tgz", + "integrity": "sha512-HvjgL3MYAJjceTDCcjRnQGjwUz/5qec9n7JPOzUursUoOTIsYCSDOb1l7RsnZE8mjbxG78zVRCKfrBXyvChBag==", "dev": true }, "caseless": { @@ -21123,9 +21259,9 @@ "dev": true }, "ci-info": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", - "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true }, "class-transformer": { @@ -21481,12 +21617,12 @@ } }, "core-js-compat": { - "version": "3.27.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", - "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.29.1.tgz", + "integrity": "sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==", "dev": true, "requires": { - "browserslist": "^4.21.4" + "browserslist": "^4.21.5" } }, "core-util-is": { @@ -21784,9 +21920,9 @@ }, "dependencies": { "@types/node": { - "version": "14.18.36", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz", - "integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==", + "version": "14.18.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.37.tgz", + "integrity": "sha512-7GgtHCs/QZrBrDzgIJnQtuSvhFSwhyYSI2uafSwZoNt1iOGhEN5fwNrQMjtONyHm9+/LoA4453jH0CMYcr06Pg==", "dev": true }, "ansi-styles": { @@ -21852,9 +21988,9 @@ } }, "cypress-mochawesome-reporter": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.2.3.tgz", - "integrity": "sha512-gUtIYBH+KvnAeCkTFu/t9niX1KHnAbEZyxBFvEHQRtrQNXLERjLJjGOHIW6kuESIdVMiEFOcvuNF2LuBpORW9A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cypress-mochawesome-reporter/-/cypress-mochawesome-reporter-3.3.0.tgz", + "integrity": "sha512-X4HU1JpuB62MXLh46660KmIs/L6noWV2KpxaXPDorz1zwgj26NN+BPCLP80D9cCFUwX3hNH0pKFZDwVR7vM8wg==", "dev": true, "requires": { "fs-extra": "^10.0.1", @@ -22181,18 +22317,18 @@ "dev": true }, "ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "dev": true, "requires": { "jake": "^10.8.5" } }, "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.328", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.328.tgz", + "integrity": "sha512-DE9tTy2PNmy1v55AZAO542ui+MLC2cvINMK4P2LXGsJdput/ThVG9t+QGecPuAZZSgC8XoI+Jh9M1OG9IoNSCw==", "dev": true }, "emoji-regex": { @@ -22245,9 +22381,9 @@ } }, "engine.io": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", - "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", + "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", "dev": true, "requires": { "@types/cookie": "^0.4.1", @@ -22259,13 +22395,13 @@ "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.0.3", - "ws": "~8.2.3" + "ws": "~8.11.0" }, "dependencies": { "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "dev": true, "requires": {} } @@ -22539,12 +22675,15 @@ "dev": true }, "eslint": { - "version": "8.33.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", - "integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", + "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.4.1", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.1", + "@eslint/js": "8.36.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -22555,10 +22694,9 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "espree": "^9.5.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", @@ -22579,7 +22717,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -22805,9 +22942,9 @@ "dev": true }, "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", "dev": true, "requires": { "acorn": "^8.8.0", @@ -22822,9 +22959,9 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -22968,6 +23105,26 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, "cookie": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", @@ -23013,6 +23170,18 @@ "side-channel": "^1.0.4" } }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -23125,9 +23294,9 @@ } }, "fecfile-validate": { - "version": "git+ssh://git@github.com/fecgov/fecfile-validate.git#5e102d5ca2dbe965f09fdf66e7d83c88c6cc7976", - "integrity": "sha512-CgPU46XI9ieniP2jEnzIL89NQMbsB2/O0pdzPPodyhiqwATw0QF2Zyx+6mTCdfx71LijmVpXfnUSoHpQ7dtjKg==", - "from": "fecfile-validate@https://github.com/fecgov/fecfile-validate#5e102d5ca2dbe965f09fdf66e7d83c88c6cc7976", + "version": "git+ssh://git@github.com/fecgov/fecfile-validate.git#4f4595f250eb39ecdddf393047a3e763662431b9", + "integrity": "sha512-ouYi7GGsPsn31xWciY63B0DmY2RcwjmZ3xa1tzByE6aUvvKjFmgB2b2CBrm+qakbmMi4eZNSdP8qq5znHwne9Q==", + "from": "fecfile-validate@https://github.com/fecgov/fecfile-validate#4f4595f250eb39ecdddf393047a3e763662431b9", "requires": { "ajv": "^8.11.0" } @@ -23573,9 +23742,9 @@ }, "dependencies": { "lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true } } @@ -23593,9 +23762,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -23803,9 +23972,9 @@ "dev": true }, "immutable": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.2.tgz", - "integrity": "sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", + "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", "dev": true }, "import-fresh": { @@ -23956,9 +24125,9 @@ "dev": true }, "intl-tel-input": { - "version": "17.0.19", - "resolved": "https://registry.npmjs.org/intl-tel-input/-/intl-tel-input-17.0.19.tgz", - "integrity": "sha512-GBNoUT4JVgm2e1N+yFMaBQ24g5EQfZhDznGneCM9IEZwfKsMUAUa1dS+v0wOiKpRAZ5IPNLJMIEEFGgqlCI22A==" + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/intl-tel-input/-/intl-tel-input-17.0.21.tgz", + "integrity": "sha512-TfyPxLe41QZPOf6RqBxRE2dpQ0FThB/PBD/gRbxVhGW7IuYg30QD90x/vjmEo4vkZw7j8etxpVcjIZVRcG+Otw==" }, "ip": { "version": "2.0.0", @@ -25125,16 +25294,16 @@ } }, "log4js": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.1.tgz", - "integrity": "sha512-lzbd0Eq1HRdWM2abSD7mk6YIVY0AogGJzb/z+lqzRk+8+XJP+M6L1MS5FUSc3jjGru4dbKjEMJmqlsoYYpuivQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", "dev": true, "requires": { "date-format": "^4.0.14", "debug": "^4.3.4", "flatted": "^3.2.7", "rfdc": "^1.3.0", - "streamroller": "^3.1.3" + "streamroller": "^3.1.5" } }, "lookup-closest-locale": { @@ -25223,9 +25392,9 @@ }, "dependencies": { "lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true } } @@ -25357,9 +25526,9 @@ } }, "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, "minipass": { @@ -25746,9 +25915,9 @@ } }, "mochawesome-merge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/mochawesome-merge/-/mochawesome-merge-4.2.2.tgz", - "integrity": "sha512-ZHeZcChGhb3If7fjSVuyB9rmDH86inNtsTb1ONYq1h0L1IduldFu38bJDcow46alMpiYQgJ7cPhv6nwpCwbJQw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mochawesome-merge/-/mochawesome-merge-4.3.0.tgz", + "integrity": "sha512-1roR6g+VUlfdaRmL8dCiVpKiaUhbPVm1ZQYUM6zHX46mWk+tpsKVZR6ba98k2zc8nlPvYd71yn5gyH970pKBSw==", "dev": true, "requires": { "fs-extra": "^7.0.1", @@ -26194,9 +26363,9 @@ "dev": true }, "node-releases": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.9.tgz", - "integrity": "sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", "dev": true }, "nopt": { @@ -26356,13 +26525,13 @@ } }, "nx": { - "version": "14.8.6", - "resolved": "https://registry.npmjs.org/nx/-/nx-14.8.6.tgz", - "integrity": "sha512-QLU3sip/g3JdNO8n5Nw2esN+0G26Jsy3u1LlrB9Giu4zf/+KsfN8CcXMbEVqOnPR1FkCS52xliaq7IBQfvvMQA==", + "version": "14.8.7", + "resolved": "https://registry.npmjs.org/nx/-/nx-14.8.7.tgz", + "integrity": "sha512-5Q+u2mcrI4l54VR7trtp3KBNAZknIAi3XFka5h7lp3m1gZbt++Ij27GCLleKPgOVnDRs0fFayyNgpKTGGHRq1A==", "dev": true, "requires": { - "@nrwl/cli": "14.8.6", - "@nrwl/tao": "14.8.6", + "@nrwl/cli": "14.8.7", + "@nrwl/tao": "14.8.7", "@parcel/watcher": "2.0.4", "@yarnpkg/lockfile": "^1.1.0", "@yarnpkg/parsers": "^3.0.0-rc.18", @@ -27597,9 +27766,9 @@ "dev": true }, "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "requires": { "bytes": "3.1.2", @@ -27740,9 +27909,9 @@ } }, "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -27819,25 +27988,19 @@ "dev": true }, "regexpu-core": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", - "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, "requires": { + "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.1.0", - "regjsgen": "^0.7.1", "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" } }, - "regjsgen": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", - "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", - "dev": true - }, "regjsparser": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", @@ -28009,9 +28172,9 @@ } }, "robots-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.0.tgz", - "integrity": "sha512-6xkze3WRdneibICBAzMKcXyTKQw5shA3GbwoEJy7RSvxpZNGF0GMuYKE1T0VMP4fwx/fQs0n0mtriOqRtk5L1w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.1.tgz", + "integrity": "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==", "dev": true }, "run-async": { @@ -28405,24 +28568,36 @@ "dev": true }, "socket.io": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz", - "integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", "dev": true, "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.2", - "engine.io": "~6.2.1", - "socket.io-adapter": "~2.4.0", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.1" } }, "socket.io-adapter": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", - "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", - "dev": true + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "requires": { + "ws": "~8.11.0" + }, + "dependencies": { + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "requires": {} + } + } }, "socket.io-parser": { "version": "4.2.2", @@ -28546,9 +28721,9 @@ } }, "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -28572,9 +28747,9 @@ } }, "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, "spdx-ranges": { @@ -28671,9 +28846,9 @@ "dev": true }, "streamroller": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.4.tgz", - "integrity": "sha512-Ha1Ccw2/N5C/IF8Do6zgNe8F3jQo8MPBnMBGvX0QjNv/I97BcNRzK6/mzOpZHHK7DjMLTI3c7Xw7Y1KvdChkvw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", "dev": true, "requires": { "date-format": "^4.0.14", @@ -28868,9 +29043,9 @@ }, "dependencies": { "minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.1.tgz", - "integrity": "sha512-V9esFpNbK0arbN3fm2sxDKqMYgIp7XtVdE4Esj+PE4Qaaxdg1wIw48ITQIOn1sc8xXSmUviVL3cyjMqPlrVkiA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", "dev": true }, "yallist": { @@ -28950,16 +29125,16 @@ } }, "terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", + "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.5" }, "dependencies": { "ajv": { @@ -28981,6 +29156,12 @@ "dev": true, "requires": {} }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -28997,6 +29178,18 @@ "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } + }, + "terser": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.6.tgz", + "integrity": "sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } } } }, @@ -29135,13 +29328,13 @@ "dev": true }, "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, "requires": { "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" }, @@ -29241,9 +29434,9 @@ "dev": true }, "ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.34.tgz", + "integrity": "sha512-cJMeh/eOILyGu0ejgTKB95yKT3zOenSe9UGE3vj6WfiOwgGYnmATUsnDixMFvdU+rNMvWih83hrUP8VwhF9yXQ==", "dev": true }, "unbzip2-stream": { @@ -29397,9 +29590,9 @@ } }, "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", "dev": true }, "vary": { @@ -29611,9 +29804,9 @@ } }, "ws": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", - "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "dev": true, "requires": {} } diff --git a/front-end/package.json b/front-end/package.json index 8f0309d0f9..19f5da95e8 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -37,7 +37,7 @@ "@popperjs/core": "^2.10.2", "bootstrap": "5.1.3", "class-transformer": "^0.5.1", - "fecfile-validate": "https://github.com/fecgov/fecfile-validate#5e102d5ca2dbe965f09fdf66e7d83c88c6cc7976", + "fecfile-validate": "https://github.com/fecgov/fecfile-validate#4f4595f250eb39ecdddf393047a3e763662431b9", "intl-tel-input": "^17.0.18", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", diff --git a/front-end/src/app/contacts/contact-detail/contact-detail.component.ts b/front-end/src/app/contacts/contact-detail/contact-detail.component.ts index 02089ed83d..e8411d0187 100644 --- a/front-end/src/app/contacts/contact-detail/contact-detail.component.ts +++ b/front-end/src/app/contacts/contact-detail/contact-detail.component.ts @@ -1,15 +1,13 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { ContactService } from 'app/shared/services/contact.service'; -import { ValidateService } from 'app/shared/services/validate.service'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; import { schema as contactCandidateSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Candidate'; import { schema as contactCommitteeSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Committee'; import { schema as contactIndividualSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Individual'; import { schema as contactOrganizationSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Organization'; import { LazyLoadEvent, MessageService } from 'primeng/api'; -import { - Contact -} from '../../shared/models/contact.model'; +import { Contact, ContactType } from '../../shared/models/contact.model'; @Component({ selector: 'app-contact-detail', @@ -38,12 +36,12 @@ export class ContactDetailComponent { formSubmitted = false; form: FormGroup = this.fb.group( - this.validateService.getFormGroupFields([ + ValidateUtils.getFormGroupFields([ ...new Set([ - ...this.validateService.getSchemaProperties(contactIndividualSchema), - ...this.validateService.getSchemaProperties(contactCandidateSchema), - ...this.validateService.getSchemaProperties(contactCommitteeSchema), - ...this.validateService.getSchemaProperties(contactOrganizationSchema), + ...ValidateUtils.getSchemaProperties(contactIndividualSchema), + ...ValidateUtils.getSchemaProperties(contactCandidateSchema), + ...ValidateUtils.getSchemaProperties(contactCommitteeSchema), + ...ValidateUtils.getSchemaProperties(contactOrganizationSchema), ]), ]) ); @@ -51,7 +49,6 @@ export class ContactDetailComponent { constructor( private messageService: MessageService, private contactService: ContactService, - private validateService: ValidateService, private fb: FormBuilder ) { } @@ -67,7 +64,12 @@ export class ContactDetailComponent { return; } - const payload: Contact = Contact.fromJSON({ ...this.contact, ...this.validateService.getFormValues(this.form) }); + const payload: Contact = Contact.fromJSON({ + ...this.contact, + ...ValidateUtils.getFormValues(this.form, + ContactService.getSchemaByType( + this.form.get('type')?.value as ContactType)) + }); if (payload.id) { this.contactService.update(payload).subscribe(() => { @@ -105,5 +107,4 @@ export class ContactDetailComponent { this.form.reset(); this.formSubmitted = false; } - } diff --git a/front-end/src/app/contacts/contact-list/contact-list.component.ts b/front-end/src/app/contacts/contact-list/contact-list.component.ts index 0d8fbdfbef..e33f8d2ee8 100644 --- a/front-end/src/app/contacts/contact-list/contact-list.component.ts +++ b/front-end/src/app/contacts/contact-list/contact-list.component.ts @@ -18,9 +18,10 @@ export class ContactListComponent extends TableListBaseComponent { searchTerm = ''; // contact lookup - contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.COMMITTEE, ContactTypes.INDIVIDUAL].includes(option.code as ContactTypes) - ); + contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ + ContactTypes.COMMITTEE, + ContactTypes.INDIVIDUAL, + ]); constructor( protected override messageService: MessageService, diff --git a/front-end/src/app/layout/sidebar/menu-report/menu-report.component.ts b/front-end/src/app/layout/sidebar/menu-report/menu-report.component.ts index 9efda9348f..ab0f3e3f4f 100644 --- a/front-end/src/app/layout/sidebar/menu-report/menu-report.component.ts +++ b/front-end/src/app/layout/sidebar/menu-report/menu-report.component.ts @@ -162,8 +162,8 @@ export class MenuReportComponent implements OnInit, OnDestroy { } // Slice indexes are determined by the number of entries in each urlMatch group - this.items[0].expanded = this.isActive(this.urlMatch.slice(0, 3), event.url); - this.items[1].expanded = this.isActive(this.urlMatch.slice(3, 7), event.url); + this.items[0].expanded = this.isActive(this.urlMatch.slice(0, 4), event.url); + this.items[1].expanded = this.isActive(this.urlMatch.slice(4, 7), event.url); this.items[2].expanded = this.isActive(this.urlMatch.slice(7, 10), event.url); } } diff --git a/front-end/src/app/login/login/login.component.spec.ts b/front-end/src/app/login/login/login.component.spec.ts index 3d32453bf8..07d8fcd08c 100644 --- a/front-end/src/app/login/login/login.component.spec.ts +++ b/front-end/src/app/login/login/login.component.spec.ts @@ -3,6 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; +import { DashboardComponent } from 'app/dashboard/dashboard.component'; import { UserLoginData } from 'app/shared/models/user.model'; import { testMockStore } from 'app/shared/utils/unit-test.utils'; import { environment } from 'environments/environment'; @@ -18,7 +19,13 @@ describe('LoginComponent', () => { beforeEach(async () => { window.onbeforeunload = jasmine.createSpy(); await TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule, ReactiveFormsModule], + imports: [ + HttpClientTestingModule, + RouterTestingModule.withRoutes([{ + path: 'dashboard', component: DashboardComponent + }]), + ReactiveFormsModule + ], providers: [{ provide: Window, useValue: window }, provideMockStore(testMockStore)], declarations: [LoginComponent], }).compileComponents(); diff --git a/front-end/src/app/reports/f3x/create-workflow/cash-on-hand.component.ts b/front-end/src/app/reports/f3x/create-workflow/cash-on-hand.component.ts index 6a0b607466..5850d07405 100644 --- a/front-end/src/app/reports/f3x/create-workflow/cash-on-hand.component.ts +++ b/front-end/src/app/reports/f3x/create-workflow/cash-on-hand.component.ts @@ -1,15 +1,15 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Subject, takeUntil } from 'rxjs'; +import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; -import { setCashOnHandAction } from 'app/store/cash-on-hand.actions'; -import { MessageService } from 'primeng/api'; -import { ValidateService } from 'app/shared/services/validate.service'; -import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; import { F3xSummary } from 'app/shared/models/f3x-summary.model'; import { F3xSummaryService } from 'app/shared/services/f3x-summary.service'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; import { selectActiveReport } from 'app/store/active-report.selectors'; +import { setCashOnHandAction } from 'app/store/cash-on-hand.actions'; +import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; +import { MessageService } from 'primeng/api'; +import { Subject, takeUntil } from 'rxjs'; @Component({ selector: 'app-cash-on-hand', @@ -20,16 +20,15 @@ export class CashOnHandComponent implements OnInit, OnDestroy { formProperties: string[] = ['L6a_cash_on_hand_jan_1_ytd', 'cash_on_hand_date']; report: F3xSummary | undefined; formSubmitted = false; - form: FormGroup = this.fb.group(this.validateService.getFormGroupFields(this.formProperties)); + form: FormGroup = this.fb.group(ValidateUtils.getFormGroupFields(this.formProperties)); constructor( public router: Router, private f3xSummaryService: F3xSummaryService, - private validateService: ValidateService, private fb: FormBuilder, private messageService: MessageService, private store: Store - ) {} + ) { } ngOnInit(): void { this.store @@ -38,8 +37,6 @@ export class CashOnHandComponent implements OnInit, OnDestroy { .subscribe((report) => (this.report = report as F3xSummary)); // Initialize validation tracking of current JSON schema and form data - this.validateService.formValidatorSchema = f3xSchema; - this.validateService.formValidatorForm = this.form; this.form.controls['L6a_cash_on_hand_jan_1_ytd'].addValidators([Validators.required]); this.form.controls['cash_on_hand_date'].addValidators([Validators.required]); @@ -61,7 +58,7 @@ export class CashOnHandComponent implements OnInit, OnDestroy { const payload: F3xSummary = F3xSummary.fromJSON({ ...this.report, - ...this.validateService.getFormValues(this.form, this.formProperties), + ...ValidateUtils.getFormValues(this.form, f3xSchema, this.formProperties), ...{ cash_on_hand_date: this.form.controls['cash_on_hand_date'].value, L6a_year_for_above_ytd: String(this.form.controls['cash_on_hand_date'].value.getYear() + 1900), diff --git a/front-end/src/app/reports/f3x/create-workflow/create-f3x-step1.component.ts b/front-end/src/app/reports/f3x/create-workflow/create-f3x-step1.component.ts index 3521796a8f..0acb16fe6f 100644 --- a/front-end/src/app/reports/f3x/create-workflow/create-f3x-step1.component.ts +++ b/front-end/src/app/reports/f3x/create-workflow/create-f3x-step1.component.ts @@ -1,20 +1,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; -import { Router, ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { Store } from '@ngrx/store'; import { F3xCoverageDates, F3xFormTypes, F3xSummary } from 'app/shared/models/f3x-summary.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; import { F3xSummaryService } from 'app/shared/services/f3x-summary.service'; -import { ValidateService } from 'app/shared/services/validate.service'; import { LabelUtils, PrimeOptions, StatesCodeLabels } from 'app/shared/utils/label.utils'; -import { selectCommitteeAccount } from 'app/store/committee-account.selectors'; -import { selectActiveReport } from 'app/store/active-report.selectors'; -import { environment } from 'environments/environment'; -import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; -import { MessageService } from 'primeng/api'; -import { Subject, switchMap, of, takeUntil, zip, map, combineLatest, startWith } from 'rxjs'; -import { ReportService } from '../../../shared/services/report.service'; -import { selectCashOnHand } from '../../../store/cash-on-hand.selectors'; import { electionReportCodes, F3xReportCodes, @@ -23,8 +14,17 @@ import { monthlyElectionYearReportCodes, monthlyNonElectionYearReportCodes, quarterlyElectionYearReportCodes, - quarterlyNonElectionYearReportCodes, + quarterlyNonElectionYearReportCodes } from 'app/shared/utils/report-code.utils'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; +import { selectActiveReport } from 'app/store/active-report.selectors'; +import { selectCommitteeAccount } from 'app/store/committee-account.selectors'; +import { environment } from 'environments/environment'; +import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; +import { MessageService } from 'primeng/api'; +import { combineLatest, map, of, startWith, Subject, switchMap, takeUntil, zip } from 'rxjs'; +import { ReportService } from '../../../shared/services/report.service'; +import { selectCashOnHand } from '../../../store/cash-on-hand.selectors'; @Component({ selector: 'app-create-f3x-step1', @@ -46,14 +46,13 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy { stateOptions: PrimeOptions = []; formSubmitted = false; - form: FormGroup = this.fb.group(this.validateService.getFormGroupFields(this.formProperties)); + form: FormGroup = this.fb.group(ValidateUtils.getFormGroupFields(this.formProperties)); readonly F3xReportTypeCategories = F3xReportTypeCategories; public f3xCoverageDatesList: F3xCoverageDates[] | undefined; constructor( private store: Store, - private validateService: ValidateService, private fecDatePipe: FecDatePipe, private fb: FormBuilder, private f3xSummaryService: F3xSummaryService, @@ -61,7 +60,7 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy { protected router: Router, private activatedRoute: ActivatedRoute, private reportService: ReportService - ) {} + ) { } ngOnInit(): void { const reportId = this.activatedRoute.snapshot.data['reportId']; @@ -131,9 +130,9 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy { } }); + ValidateUtils.addJsonSchemaValidators(this.form, f3xSchema, false); + // Initialize validation tracking of current JSON schema and form data - this.validateService.formValidatorSchema = f3xSchema; - this.validateService.formValidatorForm = this.form; this.form.addValidators(this.buildCoverageDatesValidator()); } @@ -245,7 +244,8 @@ export class CreateF3XStep1Component implements OnInit, OnDestroy { return; } - const summary: F3xSummary = F3xSummary.fromJSON(this.validateService.getFormValues(this.form, this.formProperties)); + const summary: F3xSummary = F3xSummary.fromJSON(ValidateUtils.getFormValues( + this.form, f3xSchema, this.formProperties)); // If a termination report, set the form_type appropriately. if (summary.report_code === F3xReportCodes.TER) { diff --git a/front-end/src/app/reports/f3x/report-level-memo/report-level-memo.component.ts b/front-end/src/app/reports/f3x/report-level-memo/report-level-memo.component.ts index cebaf8cf98..d4c0a32965 100644 --- a/front-end/src/app/reports/f3x/report-level-memo/report-level-memo.component.ts +++ b/front-end/src/app/reports/f3x/report-level-memo/report-level-memo.component.ts @@ -1,16 +1,16 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { Router } from '@angular/router'; -import { Subject, takeUntil } from 'rxjs'; import { Store } from '@ngrx/store'; import { F3xSummary } from 'app/shared/models/f3x-summary.model'; import { MemoText } from 'app/shared/models/memo-text.model'; import { MemoTextService } from 'app/shared/services/memo-text.service'; -import { ValidateService } from 'app/shared/services/validate.service'; -import { selectCommitteeAccount } from 'app/store/committee-account.selectors'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; import { selectActiveReport } from 'app/store/active-report.selectors'; +import { selectCommitteeAccount } from 'app/store/committee-account.selectors'; import { schema as textSchema } from 'fecfile-validate/fecfile_validate_js/dist/Text'; import { MessageService } from 'primeng/api'; +import { Subject, takeUntil } from 'rxjs'; @Component({ selector: 'app-report-level-memo', @@ -41,7 +41,6 @@ export class ReportLevelMemoComponent implements OnInit, OnDestroy { constructor( private store: Store, - protected validateService: ValidateService, protected fb: FormBuilder, public router: Router, public memoTextService: MemoTextService, @@ -54,11 +53,7 @@ export class ReportLevelMemoComponent implements OnInit, OnDestroy { ngOnInit(): void { // Intialize FormGroup, this must be done here. Not working when initialized only above the constructor(). - this.form = this.fb.group(this.validateService.getFormGroupFields(this.formProperties)); - - // Initialize validation tracking of current JSON schema and form data - this.validateService.formValidatorSchema = textSchema; - this.validateService.formValidatorForm = this.form; + this.form = this.fb.group(ValidateUtils.getFormGroupFields(this.formProperties)); this.store .select(selectCommitteeAccount) @@ -79,6 +74,8 @@ export class ReportLevelMemoComponent implements OnInit, OnDestroy { }); } }); + + ValidateUtils.addJsonSchemaValidators(this.form, textSchema, false); } ngOnDestroy(): void { @@ -95,7 +92,7 @@ export class ReportLevelMemoComponent implements OnInit, OnDestroy { const payload: MemoText = MemoText.fromJSON({ ...this.assignedMemoText, - ...this.validateService.getFormValues(this.form, this.formProperties), + ...ValidateUtils.getFormValues(this.form, textSchema, this.formProperties), }); payload.report_id = this.report.id; diff --git a/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step1.component.spec.ts b/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step1.component.spec.ts index c2d4c83fbd..1c73275112 100644 --- a/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step1.component.spec.ts +++ b/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step1.component.spec.ts @@ -1,21 +1,20 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; +import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; +import { F3xSummary } from 'app/shared/models/f3x-summary.model'; import { testMockStore } from 'app/shared/utils/unit-test.utils'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { of } from 'rxjs'; import { MessageService, SharedModule } from 'primeng/api'; -import { DividerModule } from 'primeng/divider'; import { CheckboxModule } from 'primeng/checkbox'; +import { DividerModule } from 'primeng/divider'; import { RadioButtonModule } from 'primeng/radiobutton'; -import { SubmitF3xStep1Component } from './submit-f3x-step1.component'; -import { F3xSummary } from 'app/shared/models/f3x-summary.model'; +import { of } from 'rxjs'; import { CommitteeAccount } from '../../../shared/models/committee-account.model'; -import { ValidateService } from '../../../shared/services/validate.service'; import { F3xSummaryService } from '../../../shared/services/f3x-summary.service'; import { ReportsModule } from '../../reports.module'; +import { SubmitF3xStep1Component } from './submit-f3x-step1.component'; describe('SubmitF3xStep1Component', () => { let component: SubmitF3xStep1Component; @@ -37,7 +36,6 @@ describe('SubmitF3xStep1Component', () => { ], declarations: [SubmitF3xStep1Component], providers: [ - ValidateService, FormBuilder, F3xSummaryService, MessageService, diff --git a/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step1.component.ts b/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step1.component.ts index be6f0aa6f6..9cda309c97 100644 --- a/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step1.component.ts +++ b/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step1.component.ts @@ -1,17 +1,17 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; import { FormBuilder, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; -import { Observable, Subject, takeUntil } from 'rxjs'; +import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; -import { MessageService } from 'primeng/api'; -import { selectCommitteeAccount } from 'app/store/committee-account.selectors'; -import { selectActiveReport } from 'app/store/active-report.selectors'; -import { LabelUtils, PrimeOptions, StatesCodeLabels, CountryCodeLabels } from 'app/shared/utils/label.utils'; -import { ValidateService } from 'app/shared/services/validate.service'; -import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; +import { CommitteeAccount } from 'app/shared/models/committee-account.model'; import { F3xSummary } from 'app/shared/models/f3x-summary.model'; import { F3xSummaryService } from 'app/shared/services/f3x-summary.service'; -import { CommitteeAccount } from 'app/shared/models/committee-account.model'; +import { CountryCodeLabels, LabelUtils, PrimeOptions, StatesCodeLabels } from 'app/shared/utils/label.utils'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; +import { selectActiveReport } from 'app/store/active-report.selectors'; +import { selectCommitteeAccount } from 'app/store/committee-account.selectors'; +import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; +import { MessageService } from 'primeng/api'; +import { Observable, Subject, takeUntil } from 'rxjs'; @Component({ selector: 'app-submit-f3x-step1', @@ -34,16 +34,15 @@ export class SubmitF3xStep1Component implements OnInit, OnDestroy { formSubmitted = false; destroy$: Subject = new Subject(); committeeAccount$: Observable = this.store.select(selectCommitteeAccount); - form: FormGroup = this.fb.group(this.validateService.getFormGroupFields(this.formProperties)); + form: FormGroup = this.fb.group(ValidateUtils.getFormGroupFields(this.formProperties)); constructor( public router: Router, private f3xSummaryService: F3xSummaryService, - private validateService: ValidateService, private fb: FormBuilder, private store: Store, private messageService: MessageService - ) {} + ) { } ngOnInit(): void { this.stateOptions = LabelUtils.getPrimeOptions(StatesCodeLabels); @@ -60,8 +59,6 @@ export class SubmitF3xStep1Component implements OnInit, OnDestroy { .subscribe((committeeAccount) => this.setDefaultFormValues(committeeAccount)); // Initialize validation tracking of current JSON schema and form data - this.validateService.formValidatorSchema = f3xSchema; - this.validateService.formValidatorForm = this.form; this.form.controls['confirmation_email_1'].addValidators([ Validators.required, Validators.maxLength(44), @@ -71,6 +68,8 @@ export class SubmitF3xStep1Component implements OnInit, OnDestroy { Validators.maxLength(44), this.buildEmailValidator('confirmation_email_2'), ]); + + ValidateUtils.addJsonSchemaValidators(this.form, f3xSchema, false); } setDefaultFormValues(committeeAccount: CommitteeAccount) { @@ -146,7 +145,8 @@ export class SubmitF3xStep1Component implements OnInit, OnDestroy { } else { addressFields = { change_of_address: true, - ...this.validateService.getFormValues(this.form, this.formProperties), + ...ValidateUtils.getFormValues(this.form, + f3xSchema, this.formProperties), }; } diff --git a/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step2.component.spec.ts b/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step2.component.spec.ts index d157943475..10e2c5c1e8 100644 --- a/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step2.component.spec.ts +++ b/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step2.component.spec.ts @@ -1,22 +1,21 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { Router, ActivatedRoute } from '@angular/router'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; import { provideMockStore } from '@ngrx/store/testing'; +import { F3xSummary } from 'app/shared/models/f3x-summary.model'; import { testMockStore } from 'app/shared/utils/unit-test.utils'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { of } from 'rxjs'; -import { MessageService, SharedModule, ConfirmationService } from 'primeng/api'; -import { DividerModule } from 'primeng/divider'; +import { ConfirmationService, MessageService, SharedModule } from 'primeng/api'; import { CheckboxModule } from 'primeng/checkbox'; +import { DividerModule } from 'primeng/divider'; import { RadioButtonModule } from 'primeng/radiobutton'; -import { SubmitF3xStep2Component } from './submit-f3x-step2.component'; -import { F3xSummary } from 'app/shared/models/f3x-summary.model'; +import { of } from 'rxjs'; import { CommitteeAccount } from '../../../shared/models/committee-account.model'; -import { ValidateService } from '../../../shared/services/validate.service'; import { F3xSummaryService } from '../../../shared/services/f3x-summary.service'; -import { ReportsModule } from '../../reports.module'; import { ReportService } from '../../../shared/services/report.service'; +import { ReportsModule } from '../../reports.module'; +import { SubmitF3xStep2Component } from './submit-f3x-step2.component'; describe('SubmitF3xStep2Component', () => { let component: SubmitF3xStep2Component; @@ -39,7 +38,6 @@ describe('SubmitF3xStep2Component', () => { ], declarations: [SubmitF3xStep2Component], providers: [ - ValidateService, FormBuilder, F3xSummaryService, MessageService, diff --git a/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step2.component.ts b/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step2.component.ts index ef85e7029f..0e4d508860 100644 --- a/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step2.component.ts +++ b/front-end/src/app/reports/f3x/submission-workflow/submit-f3x-step2.component.ts @@ -1,20 +1,20 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Observable, Subject, takeUntil } from 'rxjs'; +import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { selectCommitteeAccount } from 'app/store/committee-account.selectors'; -import { selectCashOnHand } from 'app/store/cash-on-hand.selectors'; -import { selectActiveReport } from 'app/store/active-report.selectors'; import { CashOnHand } from 'app/shared/interfaces/report.interface'; -import { LabelUtils, PrimeOptions, StatesCodeLabels, CountryCodeLabels } from 'app/shared/utils/label.utils'; -import { ValidateService } from 'app/shared/services/validate.service'; -import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; -import { F3xSummary } from 'app/shared/models/f3x-summary.model'; -import { F3xSummaryService } from 'app/shared/services/f3x-summary.service'; import { CommitteeAccount } from 'app/shared/models/committee-account.model'; +import { F3xSummary } from 'app/shared/models/f3x-summary.model'; import { ApiService } from 'app/shared/services/api.service'; +import { F3xSummaryService } from 'app/shared/services/f3x-summary.service'; +import { CountryCodeLabels, LabelUtils, PrimeOptions, StatesCodeLabels } from 'app/shared/utils/label.utils'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; +import { selectActiveReport } from 'app/store/active-report.selectors'; +import { selectCashOnHand } from 'app/store/cash-on-hand.selectors'; +import { selectCommitteeAccount } from 'app/store/committee-account.selectors'; +import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { Observable, Subject, takeUntil } from 'rxjs'; import { ReportService } from '../../../shared/services/report.service'; @Component({ @@ -37,7 +37,7 @@ export class SubmitF3xStep2Component implements OnInit, OnDestroy { formSubmitted = false; destroy$: Subject = new Subject(); committeeAccount$: Observable = this.store.select(selectCommitteeAccount); - form: FormGroup = this.fb.group(this.validateService.getFormGroupFields(this.formProperties)); + form: FormGroup = this.fb.group(ValidateUtils.getFormGroupFields(this.formProperties)); loading: 0 | 1 | 2 = 0; cashOnHand: CashOnHand = { report_id: undefined, @@ -47,14 +47,13 @@ export class SubmitF3xStep2Component implements OnInit, OnDestroy { constructor( public router: Router, private f3xSummaryService: F3xSummaryService, - private validateService: ValidateService, private fb: FormBuilder, private store: Store, private messageService: MessageService, protected confirmationService: ConfirmationService, private apiService: ApiService, private reportService: ReportService - ) {} + ) { } ngOnInit(): void { this.stateOptions = LabelUtils.getPrimeOptions(StatesCodeLabels); @@ -76,10 +75,10 @@ export class SubmitF3xStep2Component implements OnInit, OnDestroy { .subscribe((cashOnHand: CashOnHand) => (this.cashOnHand = cashOnHand)); // Initialize validation tracking of current JSON schema and form data - this.validateService.formValidatorSchema = f3xSchema; - this.validateService.formValidatorForm = this.form; - this.form.controls['filing_password'].addValidators(this.validateService.passwordValidator()); + this.form.controls['filing_password'].addValidators(ValidateUtils.passwordValidator()); this.form.controls['truth_statement'].addValidators(Validators.requiredTrue); + + ValidateUtils.addJsonSchemaValidators(this.form, f3xSchema, false); } setDefaultFormValues(committeeAccount: CommitteeAccount) { @@ -167,7 +166,8 @@ export class SubmitF3xStep2Component implements OnInit, OnDestroy { this.loading = 1; const payload: F3xSummary = F3xSummary.fromJSON({ ...this.report, - ...this.validateService.getFormValues(this.form, this.formProperties), + ...ValidateUtils.getFormValues(this.form, + f3xSchema, this.formProperties), }); return this.f3xSummaryService.update(payload, this.formProperties); diff --git a/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.html b/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.html new file mode 100644 index 0000000000..8d3eb2f88d --- /dev/null +++ b/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.html @@ -0,0 +1,29 @@ + + +
+
+
+ + + + {{ getFormType(selectedType)?.label }}: {{ getFormType(selectedType)?.description }} + + + {{ getFormType(type)?.label }}: {{ getFormType(type)?.description }} + + +
+
+
+
+ + + +
diff --git a/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.spec.ts b/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.spec.ts new file mode 100644 index 0000000000..1f7e85349e --- /dev/null +++ b/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.spec.ts @@ -0,0 +1,35 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; +import { FormTypes } from 'app/shared/utils/form-type.utils'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { FormTypeDialogComponent } from './form-type-dialog.component'; + +describe('FormTypeDialogComponent', () => { + let component: FormTypeDialogComponent; + let fixture: ComponentFixture; + let router: Router; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([])], + declarations: [FormTypeDialogComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(FormTypeDialogComponent); + router = TestBed.inject(Router); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('#goToReportForm should route properly', () => { + const navigateSpy = spyOn(router, 'navigateByUrl'); + component.selectedType = FormTypes.F3X; + component.goToReportForm(); + expect(navigateSpy).toHaveBeenCalledWith('/reports/f3x/create/step1'); + }); +}); diff --git a/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.ts b/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.ts new file mode 100644 index 0000000000..7b08519775 --- /dev/null +++ b/front-end/src/app/reports/form-type-dialog/form-type-dialog.component.ts @@ -0,0 +1,22 @@ +import { Component, Input } from '@angular/core'; +import { Router } from '@angular/router'; +import { FormType, FORM_TYPES, FormTypes } from 'app/shared/utils/form-type.utils'; + +@Component({ + selector: 'app-form-type-dialog', + templateUrl: './form-type-dialog.component.html', +}) +export class FormTypeDialogComponent { + @Input() visible = false; + formTypeOptions: FormTypes[] = Array.from(FORM_TYPES, (mapping) => mapping[0]); + selectedType?: FormTypes; + constructor(public router: Router) {} + + goToReportForm(): void { + this.router.navigateByUrl(this.getFormType(this.selectedType)?.createRoute || ''); + } + + getFormType(type?: FormTypes): FormType | undefined { + return type ? FORM_TYPES.get(type) : undefined; + } +} diff --git a/front-end/src/app/reports/report-list/report-list.component.html b/front-end/src/app/reports/report-list/report-list.component.html index f081ad1ca3..f38cb0ceb4 100644 --- a/front-end/src/app/reports/report-list/report-list.component.html +++ b/front-end/src/app/reports/report-list/report-list.component.html @@ -59,7 +59,7 @@
Recent reports
- {{ item.form_type | label: f3xFormTypeLabels }} + {{ item.form_type | label : f3xFormTypeLabels }} {{ item.report_code_label }} Recent reports > {{ item.report_status }} - {{ item.form_type | label: f3xFormVerionLabels }} + {{ item.form_type | label : f3xFormVerionLabels }} {{ item.upload_submission?.created | fecDate }} - + + +
    +
  • + +
  • +
+
+
@@ -89,3 +101,5 @@
Recent reports
+ + diff --git a/front-end/src/app/reports/report-list/report-list.component.spec.ts b/front-end/src/app/reports/report-list/report-list.component.spec.ts index ef3fd8c79a..453efc96c2 100644 --- a/front-end/src/app/reports/report-list/report-list.component.spec.ts +++ b/front-end/src/app/reports/report-list/report-list.component.spec.ts @@ -11,6 +11,8 @@ import { F3xSummary, F3xFormTypes } from '../../shared/models/f3x-summary.model' import { Report } from '../../shared/interfaces/report.interface'; import { RouterTestingModule } from '@angular/router/testing'; import { Router } from '@angular/router'; +import { UploadSubmission } from 'app/shared/models/upload-submission.model'; +import { RowAction } from 'app/shared/components/table-list-base/table-list-base.component'; describe('ReportListComponent', () => { let component: ReportListComponent; @@ -41,17 +43,25 @@ describe('ReportListComponent', () => { expect(item.id).toBe(undefined); }); - it('#addItem should route properly', () => { + it('#editItem should route properly', () => { const navigateSpy = spyOn(router, 'navigateByUrl'); - component.addItem(); - expect(navigateSpy).toHaveBeenCalledWith('/reports/f3x/create/step1'); + component.editItem({ id: '999' } as F3xSummary); // 999 is the cash on hand report + expect(navigateSpy).toHaveBeenCalledWith('/reports/f3x/create/cash-on-hand/999'); + component.editItem({ id: '888' } as F3xSummary); + expect(navigateSpy).toHaveBeenCalledWith('/transactions/report/888/list'); + component.editItem({ + id: '777', + upload_submission: UploadSubmission.fromJSON({ fec_status: 'ACCEPTED' }), + } as F3xSummary); + expect(navigateSpy).toHaveBeenCalledWith('/reports/f3x/submit/status/777'); }); - it('#editItem should route properly', () => { + it('#onActionClick should route properly', () => { const navigateSpy = spyOn(router, 'navigateByUrl'); - - component.editItem({ id: '999' } as F3xSummary); - expect(navigateSpy).toHaveBeenCalledWith('/reports/f3x/create/cash-on-hand/999'); + component.onActionClick(new RowAction('', component.editItem.bind(component)), { id: '888' } as F3xSummary); + expect(navigateSpy).toHaveBeenCalledWith('/transactions/report/888/list'); + component.onActionClick(new RowAction('', component.goToTest.bind(component)), { id: '888' } as F3xSummary); + expect(navigateSpy).toHaveBeenCalledWith('/reports/f3x/test-dot-fec/888'); }); it('#displayName should display the item form_type code', () => { diff --git a/front-end/src/app/reports/report-list/report-list.component.ts b/front-end/src/app/reports/report-list/report-list.component.ts index d49ba353b9..99e2ed74d8 100644 --- a/front-end/src/app/reports/report-list/report-list.component.ts +++ b/front-end/src/app/reports/report-list/report-list.component.ts @@ -3,7 +3,7 @@ import { Subject, takeUntil } from 'rxjs'; import { Store } from '@ngrx/store'; import { selectCashOnHand } from '../../store/cash-on-hand.selectors'; import { ConfirmationService, MessageService } from 'primeng/api'; -import { TableListBaseComponent } from '../../shared/components/table-list-base/table-list-base.component'; +import { RowAction, TableListBaseComponent } from '../../shared/components/table-list-base/table-list-base.component'; import { Report, CashOnHand } from '../../shared/interfaces/report.interface'; import { LabelList } from '../../shared/utils/label.utils'; import { ReportService } from '../../shared/services/report.service'; @@ -21,6 +21,19 @@ export class ReportListComponent extends TableListBaseComponent implemen report_id: undefined, value: undefined, }; + public actionOptions: RowAction[] = [ + new RowAction( + 'Edit report', + this.editItem.bind(this), + (report: F3xSummary) => report.report_status === 'In-Progress' + ), + new RowAction( + 'Review report', + this.editItem.bind(this), + (report: F3xSummary) => report.report_status !== 'In-Progress' + ), + new RowAction('Download as .fec', this.goToTest.bind(this)), + ]; private destroy$ = new Subject(); constructor( @@ -55,10 +68,6 @@ export class ReportListComponent extends TableListBaseComponent implemen return new F3xSummary(); } - public override addItem(): void { - this.router.navigateByUrl('/reports/f3x/create/step1'); - } - public override editItem(item: Report): void { if (!this.itemService.isEditable(item)) { this.router.navigateByUrl(`/reports/f3x/submit/status/${item.id}`); @@ -73,8 +82,8 @@ export class ReportListComponent extends TableListBaseComponent implemen this.router.navigateByUrl(`/reports/f3x/test-dot-fec/${item.id}`); } - public createTransaction(item: Report): void { - this.router.navigateByUrl(`/transactions/report/${item.id}/create`); + public onActionClick(action: RowAction, report: Report) { + action.action(report); } /** diff --git a/front-end/src/app/reports/reports.module.ts b/front-end/src/app/reports/reports.module.ts index 2ce3a1c0d1..16a807f361 100644 --- a/front-end/src/app/reports/reports.module.ts +++ b/front-end/src/app/reports/reports.module.ts @@ -9,6 +9,8 @@ import { CheckboxModule } from 'primeng/checkbox'; import { ConfirmDialogModule } from 'primeng/confirmdialog'; import { DividerModule } from 'primeng/divider'; import { DropdownModule } from 'primeng/dropdown'; +import { OverlayPanelModule } from 'primeng/overlaypanel'; +import { ListboxModule } from 'primeng/listbox'; import { InputTextModule } from 'primeng/inputtext'; import { InputTextareaModule } from 'primeng/inputtextarea'; import { ProgressSpinnerModule } from 'primeng/progressspinner'; @@ -34,6 +36,8 @@ import { ReportsRoutingModule } from './reports-routing.module'; import { CashOnHandComponent } from './f3x/create-workflow/cash-on-hand.component'; import { AppSelectButtonComponent } from '../shared/components/app-selectbutton'; import { InputNumberModule } from 'primeng/inputnumber'; +import { FormTypeDialogComponent } from './form-type-dialog/form-type-dialog.component'; +import { DialogModule } from 'primeng/dialog'; @NgModule({ declarations: [ @@ -49,6 +53,7 @@ import { InputNumberModule } from 'primeng/inputnumber'; TestDotFecComponent, CashOnHandComponent, AppSelectButtonComponent, + FormTypeDialogComponent, ], imports: [ CommonModule, @@ -59,7 +64,10 @@ import { InputNumberModule } from 'primeng/inputnumber'; ToolbarModule, ButtonModule, DividerModule, + DialogModule, DropdownModule, + OverlayPanelModule, + ListboxModule, RadioButtonModule, CheckboxModule, InputTextModule, diff --git a/front-end/src/app/shared/components/contact-form/contact-form.component.html b/front-end/src/app/shared/components/contact-form/contact-form.component.html index d0fd33a389..153368f4e2 100644 --- a/front-end/src/app/shared/components/contact-form/contact-form.component.html +++ b/front-end/src/app/shared/components/contact-form/contact-form.component.html @@ -6,16 +6,18 @@

Contact

-
+
- + +
diff --git a/front-end/src/app/shared/components/contact-form/contact-form.component.spec.ts b/front-end/src/app/shared/components/contact-form/contact-form.component.spec.ts index 1e95dc1dad..851277d488 100644 --- a/front-end/src/app/shared/components/contact-form/contact-form.component.spec.ts +++ b/front-end/src/app/shared/components/contact-form/contact-form.component.spec.ts @@ -1,11 +1,14 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { provideMockStore } from '@ngrx/store/testing'; -import { JsonSchema } from 'app/shared/interfaces/json-schema.interface'; -import { CandidateOfficeTypes, ContactTypes } from 'app/shared/models/contact.model'; +import { Candidate } from 'app/shared/models/candidate.model'; +import { CommitteeAccount } from 'app/shared/models/committee-account.model'; +import { CandidateOfficeTypes, Contact, ContactTypes, FecApiCandidateLookupData, FecApiCommitteeLookupData } from 'app/shared/models/contact.model'; +import { FecApiService } from 'app/shared/services/fec-api.service'; import { testMockStore } from 'app/shared/utils/unit-test.utils'; import { DropdownModule } from 'primeng/dropdown'; +import { of } from 'rxjs'; import { ErrorMessagesComponent } from '../error-messages/error-messages.component'; import { FecInternationalPhoneInputComponent } from '../fec-international-phone-input/fec-international-phone-input.component'; import { ContactFormComponent } from './contact-form.component'; @@ -13,15 +16,21 @@ import { ContactFormComponent } from './contact-form.component'; describe('ContactFormComponent', () => { let component: ContactFormComponent; let fixture: ComponentFixture; + let testFecApiService: FecApiService; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, FormsModule, - ReactiveFormsModule, DropdownModule], + imports: [ + HttpClientTestingModule, + FormsModule, + ReactiveFormsModule, + DropdownModule, + ], declarations: [ContactFormComponent, ErrorMessagesComponent, FecInternationalPhoneInputComponent], providers: [FormBuilder, provideMockStore(testMockStore)], }).compileComponents(); + testFecApiService = TestBed.inject(FecApiService); }); beforeEach(() => { @@ -79,15 +88,90 @@ describe('ContactFormComponent', () => { expect(component.form.get('state')?.value).toBe('ZZ'); }); - it('#getSchemaByType should return the correct schema', () => { - let schema: JsonSchema = component['getSchemaByType'](ContactTypes.COMMITTEE); - expect(schema.$id).toBe('https://github.com/fecgov/fecfile-validate/blob/main/schema/Contact_Committee.json'); + it('#onContactLookupSelect CANDIDATE Contact happy path', () => { + const testContact = new Contact(); + const testLastName = "testLastName"; + const testZip = "12345"; + testContact.type = ContactTypes.CANDIDATE; + testContact.last_name = testLastName; + testContact.zip = testZip; - schema = component['getSchemaByType'](ContactTypes.ORGANIZATION); - expect(schema.$id).toBe('https://github.com/fecgov/fecfile-validate/blob/main/schema/Contact_Organization.json'); + component.onContactLookupSelect({ value: testContact }); - schema = component['getSchemaByType'](ContactTypes.CANDIDATE); - expect(schema.$id).toBe('https://github.com/fecgov/fecfile-validate/blob/main/schema/Contact_Candidate.json'); + expect(component.form.get('last_name')?.value).toBe(testLastName); + expect(component.form.get('zip')?.value).toBe(testZip); + + component.form = new FormGroup({}); + component.onContactLookupSelect({ value: testContact }); + }); + + it('#onContactLookupSelect COMMITTEE Contact happy path', () => { + const testContact = new Contact(); + const testCommitteeId = "C1234568"; + const testZip = "12345"; + testContact.type = ContactTypes.COMMITTEE; + testContact.committee_id = testCommitteeId; + testContact.zip = testZip; + + component.onContactLookupSelect({ value: testContact }); + + expect(component.form.get('committee_id')?.value).toBe(testCommitteeId); + expect(component.form.get('zip')?.value).toBe(testZip); + + component.form = new FormGroup({}); + component.onContactLookupSelect({ value: testContact }); + }); + + it('#onContactLookupSelect FecApiCandidateLookupData happy path', () => { + const testId = 'P12345678' + const testOfficeSought = 'P'; + const testName = 'testName'; + const testAddressCity = 'testAddressCity'; + const testFecApiCandidateLookupData = new FecApiCandidateLookupData({ + id: testId, + office_sought: testOfficeSought, + name: testName + } as FecApiCandidateLookupData); + const testResponse = new Candidate(); + testResponse.candidate_id = testId; + testResponse.address_city = testAddressCity; + + spyOn(testFecApiService, 'getCandidateDetails').and.returnValue(of(testResponse)); + + component.onContactLookupSelect({ value: testFecApiCandidateLookupData }); + + expect(component.form.get('type')?.value).toBe(ContactTypes.CANDIDATE); + expect(component.form.get('candidate_id')?.value).toBe(testId); + expect(component.form.get('city')?.value).toBe(testAddressCity); + + component.form = new FormGroup({}); + component.onContactLookupSelect({ value: testFecApiCandidateLookupData }); + }); + + it('#onContactLookupSelect FecApiCommitteeLookupData happy path', () => { + const testId = 'C12345678' + const testIsActive = true; + const testName = 'testName'; + const testPhone = '1234567890'; + const testFecApiCommitteeLookupData = new FecApiCommitteeLookupData({ + id: testId, + is_active: testIsActive, + name: testName + } as FecApiCommitteeLookupData); + const testResponse = new CommitteeAccount(); + testResponse.committee_id = testId; + testResponse.treasurer_phone = testPhone; + + spyOn(testFecApiService, 'getCommitteeDetails').and.returnValue(of(testResponse)); + + component.onContactLookupSelect({ value: testFecApiCommitteeLookupData }); + + expect(component.form.get('type')?.value).toBe(ContactTypes.COMMITTEE); + expect(component.form.get('committee_id')?.value).toBe(testId); + expect(component.form.get('telephone')?.value).toBe('+1 ' + testPhone); + + component.form = new FormGroup({}); + component.onContactLookupSelect({ value: testFecApiCommitteeLookupData }); }); }); diff --git a/front-end/src/app/shared/components/contact-form/contact-form.component.ts b/front-end/src/app/shared/components/contact-form/contact-form.component.ts index 52970bd60c..f2e037d69d 100644 --- a/front-end/src/app/shared/components/contact-form/contact-form.component.ts +++ b/front-end/src/app/shared/components/contact-form/contact-form.component.ts @@ -1,8 +1,9 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; -import { JsonSchema } from 'app/shared/interfaces/json-schema.interface'; -import { ValidateService } from 'app/shared/services/validate.service'; +import { ContactService } from 'app/shared/services/contact.service'; +import { FecApiService } from 'app/shared/services/fec-api.service'; import { CountryCodeLabels, LabelUtils, PrimeOptions, StatesCodeLabels } from 'app/shared/utils/label.utils'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; import { schema as contactCandidateSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Candidate'; import { schema as contactCommitteeSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Committee'; import { schema as contactIndividualSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Individual'; @@ -10,8 +11,11 @@ import { schema as contactOrganizationSchema } from 'fecfile-validate/fecfile_va import { Subject, takeUntil } from 'rxjs'; import { CandidateOfficeTypeLabels, - CandidateOfficeTypes, ContactTypeLabels, - ContactTypes + CandidateOfficeTypes, + Contact, + ContactTypeLabels, + ContactTypes, FecApiCandidateLookupData, + FecApiCommitteeLookupData } from '../../models/contact.model'; @Component({ @@ -20,12 +24,12 @@ import { }) export class ContactFormComponent implements OnInit, OnDestroy { @Input() form: FormGroup = this.fb.group( - this.validateService.getFormGroupFields([ + ValidateUtils.getFormGroupFields([ ...new Set([ - ...this.validateService.getSchemaProperties(contactIndividualSchema), - ...this.validateService.getSchemaProperties(contactCandidateSchema), - ...this.validateService.getSchemaProperties(contactCommitteeSchema), - ...this.validateService.getSchemaProperties(contactOrganizationSchema), + ...ValidateUtils.getSchemaProperties(contactIndividualSchema), + ...ValidateUtils.getSchemaProperties(contactCandidateSchema), + ...ValidateUtils.getSchemaProperties(contactCommitteeSchema), + ...ValidateUtils.getSchemaProperties(contactOrganizationSchema), ]), ]) ); @@ -42,8 +46,8 @@ export class ContactFormComponent implements OnInit, OnDestroy { candidateDistrictOptions: PrimeOptions = []; constructor( - private validateService: ValidateService, - private fb: FormBuilder + private fb: FormBuilder, + private fecApiService: FecApiService ) { } ngOnInit(): void { @@ -53,21 +57,18 @@ export class ContactFormComponent implements OnInit, OnDestroy { this.countryOptions = LabelUtils.getPrimeOptions(CountryCodeLabels); this.candidateStateOptions = LabelUtils.getPrimeOptions(LabelUtils.getStateCodeLabelsWithoutMilitary()); - // Initialize validation tracking of current JSON schema and form data - this.validateService.formValidatorSchema = contactIndividualSchema; - this.validateService.formValidatorForm = this.form; - this.form ?.get('type') ?.valueChanges.pipe(takeUntil(this.destroy$)) .subscribe((value: string) => { - // Update validator JSON schema to the selected contact type - this.validateService.formValidatorSchema = this.getSchemaByType(value as ContactTypes); + const schema = ContactService.getSchemaByType( + value as ContactTypes); + ValidateUtils.addJsonSchemaValidators(this.form, schema, true); // Clear out non-schema form values const formValues: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - const schemaProperties: string[] = this.validateService.getSchemaProperties( - this.validateService.formValidatorSchema + const schemaProperties: string[] = ValidateUtils.getSchemaProperties( + schema ); Object.keys(this.form.controls).forEach((property: string) => { if (!schemaProperties.includes(property)) { @@ -133,6 +134,7 @@ export class ContactFormComponent implements OnInit, OnDestroy { this.candidateDistrictOptions = []; } }); + } ngOnDestroy(): void { @@ -147,23 +149,97 @@ export class ContactFormComponent implements OnInit, OnDestroy { return CandidateOfficeTypes; } - /** - * Given the type of contact given, return the appropriate JSON schema doc - * @param {ContactTypes} type - * @returns {JsonSchema} schema - */ - private getSchemaByType(type: ContactTypes): JsonSchema { - let schema: JsonSchema = contactIndividualSchema; - if (type === ContactTypes.CANDIDATE) { - schema = contactCandidateSchema; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onContactLookupSelect(event: any) { + if (event && event.value) { + if (event.value instanceof Contact) { + this.onContactSelect(event.value); + } else if (event.value instanceof FecApiCandidateLookupData) { + this.onFecApiCandidateLookupDataSelect(event.value); + } else if (event.value instanceof FecApiCommitteeLookupData) { + this.onFecApiCommitteeLookupDataSelect(event.value); + } + } + } + + onContactSelect(contact: Contact) { + if (contact) { + switch (contact.type) { + case ContactTypes.CANDIDATE: + this.form.get('type')?.setValue(contact.type); + this.form.get('candidate_id')?.setValue(contact.candidate_id); + this.form.get('last_name')?.setValue(contact.last_name); + this.form.get('first_name')?.setValue(contact.first_name); + this.form.get('middle_name')?.setValue(contact.middle_name); + this.form.get('prefix')?.setValue(contact.prefix); + this.form.get('suffix')?.setValue(contact.suffix); + this.form.get('employer')?.setValue(contact.employer); + this.form.get('occupation')?.setValue(contact.occupation); + this.form.get('candidate_office')?.setValue(contact.candidate_office); + this.form.get('candidate_state')?.setValue(contact.candidate_state); + this.form.get('candidate_district')?.setValue(contact.candidate_district); + break; + case ContactTypes.COMMITTEE: + this.form.get('type')?.setValue(contact.type); + this.form.get('committee_id')?.setValue(contact.committee_id); + this.form.get('name')?.setValue(contact.name); + break; + } + this.form.get('country')?.setValue(contact.country); + this.form.get('street_1')?.setValue(contact.street_1); + this.form.get('street_2')?.setValue(contact.street_2); + this.form.get('city')?.setValue(contact.city); + this.form.get('state')?.setValue(contact.state); + this.form.get('zip')?.setValue(contact.zip); + this.form.get('telephone')?.setValue(contact.telephone); } - if (type === ContactTypes.COMMITTEE) { - schema = contactCommitteeSchema; + } + + onFecApiCandidateLookupDataSelect(data: FecApiCandidateLookupData) { + if (data.id) { + this.fecApiService.getCandidateDetails(data.id).subscribe((candidate) => { + // TODO: fix once we get info from api and set all names below properly + const nameSplit = candidate.name?.split(", "); + + this.form.get('type')?.setValue(ContactTypes.CANDIDATE); + this.form.get('candidate_id')?.setValue(candidate.candidate_id); + this.form.get('last_name')?.setValue(nameSplit?.[0]); + this.form.get('first_name')?.setValue(nameSplit?.[1]); + this.form.get('middle_name')?.setValue(''); + this.form.get('prefix')?.setValue(''); + this.form.get('suffix')?.setValue(''); + this.form.get('street_1')?.setValue(candidate.address_street_1); + this.form.get('street_2')?.setValue(candidate.address_street_2); + this.form.get('city')?.setValue(candidate.address_city); + this.form.get('state')?.setValue(candidate.address_state); + this.form.get('zip')?.setValue(candidate.address_zip); + this.form.get('employer')?.setValue(''); + this.form.get('occupation')?.setValue(''); + this.form.get('candidate_office')?.setValue(candidate.office); + this.form.get('candidate_state')?.setValue(candidate.state); + this.form.get('candidate_district')?.setValue(candidate.district); + }); } - if (type === ContactTypes.ORGANIZATION) { - schema = contactOrganizationSchema; + } + + onFecApiCommitteeLookupDataSelect(data: FecApiCommitteeLookupData) { + if (data.id) { + this.fecApiService.getCommitteeDetails(data.id).subscribe((committeeAccount) => { + let phone; + if (committeeAccount?.treasurer_phone) { + phone = '+1 ' + committeeAccount.treasurer_phone; + } + this.form.get('type')?.setValue(ContactTypes.COMMITTEE); + this.form.get('committee_id')?.setValue(committeeAccount.committee_id); + this.form.get('name')?.setValue(committeeAccount.name); + this.form.get('street_1')?.setValue(committeeAccount.street_1); + this.form.get('street_2')?.setValue(committeeAccount.street_2); + this.form.get('city')?.setValue(committeeAccount.city); + this.form.get('state')?.setValue(committeeAccount.state); + this.form.get('zip')?.setValue(committeeAccount.zip); + this.form.get('telephone')?.setValue(phone); + }); } - return schema; } } diff --git a/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.html b/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.html index 98c904cc9c..8748f39179 100644 --- a/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.html +++ b/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.html @@ -1,93 +1,50 @@ -
-
-
-
- - -
-
- - -
{{ group.label }}
-
- Try another search or create a new contact -
-
- - - In contacts - -
-
-
- -
+ +
+
+ +
- - - - - - - -
-
- -
-
- -
+
+ + +
{{ group.label }}
+
+ Try another search or create a new contact +
+
+ + + In contacts + +
+
+
+ - - -
+
+
+ diff --git a/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.spec.ts b/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.spec.ts index 0b8cd14a04..ade773a1b5 100644 --- a/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.spec.ts +++ b/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.spec.ts @@ -1,27 +1,24 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { EventEmitter } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { provideMockStore } from '@ngrx/store/testing'; import { + CandidateLookupResponse, CommitteeLookupResponse, Contact, ContactTypes, - FecApiCommitteeLookupData, - FecApiLookupData, - FecfileCommitteeLookupData, + FecApiCommitteeLookupData, FecfileCandidateLookupData, FecfileCommitteeLookupData, FecfileIndividualLookupData, FecfileOrganizationLookupData, IndividualLookupResponse, - OrganizationLookupResponse, + OrganizationLookupResponse } from 'app/shared/models/contact.model'; import { ContactService } from 'app/shared/services/contact.service'; import { testMockStore } from 'app/shared/utils/unit-test.utils'; import { DropdownModule } from 'primeng/dropdown'; import { of } from 'rxjs'; -import { CommitteeAccount } from 'app/shared/models/committee-account.model'; -import { FecApiService } from 'app/shared/services/fec-api.service'; import { SelectItem } from 'primeng/api'; import { AutoCompleteModule } from 'primeng/autocomplete'; import { DialogModule } from 'primeng/dialog'; @@ -32,7 +29,6 @@ describe('ContactLookupComponent', () => { let fixture: ComponentFixture; let testContactService: ContactService; - let testFecApiService: FecApiService; beforeEach(async () => { await TestBed.configureTestingModule({ @@ -45,11 +41,10 @@ describe('ContactLookupComponent', () => { DropdownModule, AutoCompleteModule, ], - providers: [FormBuilder, ContactService, FecApiService, EventEmitter, provideMockStore(testMockStore)], + providers: [FormBuilder, ContactService, EventEmitter, provideMockStore(testMockStore)], }).compileComponents(); testContactService = TestBed.inject(ContactService); - testFecApiService = TestBed.inject(FecApiService); }); beforeEach(() => { @@ -70,6 +65,59 @@ describe('ContactLookupComponent', () => { expect(component.contactLookupList.length === 0).toBeTrue(); })); + it('#onDropdownSearch CAN undefined fec_api_candidates', fakeAsync(() => { + const testCandidateLookupResponse = new CandidateLookupResponse(); + testCandidateLookupResponse.fecfile_candidates = [ + { + id: 123, + first_name: 'testFirstName', + last_na2me: 'testLastName', + } as unknown as FecfileCandidateLookupData, + ]; + spyOn(testContactService, 'candidateLookup').and.returnValue(of(testCandidateLookupResponse)); + const testEvent = { query: 'hi' }; + component.contactTypeFormControl.setValue('CAN'); + component.onDropdownSearch(testEvent); + expect(component.contactLookupList[1].items.length === 0).toBeTrue(); + })); + + it('#onDropdownSearch CAN undefined fecfile_candidates', fakeAsync(() => { + const testCandidateLookupResponse = new CandidateLookupResponse(); + spyOn(testContactService, 'candidateLookup').and.returnValue(of(testCandidateLookupResponse)); + const testEvent = { query: 'hi' }; + component.contactTypeFormControl.setValue('CAN'); + component.onDropdownSearch(testEvent); + tick(500); + expect(component.contactLookupList[0].items.length === 0).toBeTrue(); + })); + + it('#onDropdownSearch CAN happy path', fakeAsync(() => { + const testCandidateLookupResponse = new CandidateLookupResponse(); + testCandidateLookupResponse.fecfile_candidates = [ + new FecfileCandidateLookupData({ + id: 123, + last_name: 'testLastName', + first_name: 'testFirstName', + type: ContactTypes.CANDIDATE, + } as unknown as FecfileCandidateLookupData), + ]; + spyOn(testContactService, 'candidateLookup').and.returnValue(of(testCandidateLookupResponse)); + const testEvent = { query: 'hi' }; + component.contactTypeFormControl.setValue('CAN'); + component.onDropdownSearch(testEvent); + tick(500); + expect( + JSON.stringify(component.contactLookupList) === JSON.stringify( + testCandidateLookupResponse.toSelectItemGroups(true)) + ).toBeTrue(); + expect( + JSON.stringify([ + { label: 'There are no matching candidates', items: [] }, + { label: 'There are no matching registered candidates', items: [] }, + ]) === JSON.stringify(new CandidateLookupResponse().toSelectItemGroups(true)) + ).toBeTrue(); + })); + it('#onDropdownSearch COM undefined fec_api_committees', fakeAsync(() => { const testCommitteeLookupResponse = new CommitteeLookupResponse(); testCommitteeLookupResponse.fecfile_committees = [ @@ -121,13 +169,13 @@ describe('ContactLookupComponent', () => { component.contactTypeFormControl.setValue('COM'); component.onDropdownSearch(testEvent); expect( - JSON.stringify(component.contactLookupList) === JSON.stringify(testCommitteeLookupResponse.toSelectItemGroups()) + JSON.stringify(component.contactLookupList) === JSON.stringify(testCommitteeLookupResponse.toSelectItemGroups(true)) ).toBeTrue(); expect( JSON.stringify([ { label: 'There are no matching committees', items: [] }, { label: 'There are no matching registered committees', items: [] }, - ]) === JSON.stringify(new CommitteeLookupResponse().toSelectItemGroups()) + ]) === JSON.stringify(new CommitteeLookupResponse().toSelectItemGroups(true)) ).toBeTrue(); })); @@ -195,7 +243,7 @@ describe('ContactLookupComponent', () => { tick(500); expect( JSON.stringify(component.contactLookupList) === - JSON.stringify(testOrganizationLookupResponse.toSelectItemGroups()) + JSON.stringify(testOrganizationLookupResponse.toSelectItemGroups()) ).toBeTrue(); expect( JSON.stringify([ @@ -207,8 +255,8 @@ describe('ContactLookupComponent', () => { ).toBeTrue(); })); - it('#onContactSelect Contact happy path', fakeAsync(() => { - const eventEmitterEmitSpy = spyOn(component.contactSelect, 'emit'); + it('#onContactLookupSelect Contact happy path', fakeAsync(() => { + const eventEmitterEmitSpy = spyOn(component.contactLookupSelect, 'emit'); const testContact = Contact.fromJSON({ id: 123, last_name: 'testLastName', @@ -218,39 +266,16 @@ describe('ContactLookupComponent', () => { const testValue = { value: testContact, } as SelectItem; - component.onContactSelect(testValue); + component.onContactLookupSelect(testValue); tick(500); expect(eventEmitterEmitSpy).toHaveBeenCalledOnceWith(testValue); })); - it('#onContactSelect FecApiLookupData createContactForm null vals', fakeAsync(() => { - const testFecApiLookupData = new FecApiCommitteeLookupData({ id: 'C12345678' } as FecApiCommitteeLookupData); - const testValue = { - value: testFecApiLookupData, - } as SelectItem; - spyOn(testFecApiService, 'getDetails').and.returnValue(of(new CommitteeAccount())); - component.createContactForm.removeControl('committee_id'); - component.createContactForm.removeControl('name'); - component.createContactForm.removeControl('street_1'); - component.createContactForm.removeControl('street_2'); - component.createContactForm.removeControl('city'); - component.createContactForm.removeControl('state'); - component.createContactForm.removeControl('zip'); - component.onContactSelect(testValue); + it('#onCreateNewContactSelect Contact happy path', fakeAsync(() => { + const eventEmitterEmitSpy = spyOn(component.createNewContactSelect, 'emit'); + component.onCreateNewContactSelect(); tick(500); - expect(component.createContactDialogVisible).toEqual(true); - })); - - it('#onContactSelect FecApiLookupData happy path', fakeAsync(() => { - const testFecApiLookupData = new FecApiCommitteeLookupData({ id: 'C12345678' } as FecApiCommitteeLookupData); - const testValue = { - value: testFecApiLookupData, - } as SelectItem; - spyOn(testFecApiService, 'getDetails').and.returnValue(of(new CommitteeAccount())); - - component.onContactSelect(testValue); - tick(500); - expect(component.createContactDialogVisible).toEqual(true); + expect(eventEmitterEmitSpy).toHaveBeenCalled(); })); it('#isContact happy path', () => { @@ -260,35 +285,4 @@ describe('ContactLookupComponent', () => { expect(retval).toEqual(expectedRetval); }); - it('#onCreateContactDialogOpen null form control', () => { - component.createContactForm = new FormGroup({}); - component.selectedFecCommitteeAccount = {} as CommitteeAccount; - - component.onCreateContactDialogOpen(); - expect(component.createContactFormSubmitted).toBeFalse(); - component.selectedFecCommitteeAccount = undefined; - component.onCreateContactDialogOpen(); - }); - - it('#createNewContact happy path', () => { - component.createNewContact(); - component.closeCreateContactDialog(); - component.createContactSave(); - component.selectedFecCommitteeAccount = { - committee_id: 'testCommitteeId', - name: 'testName', - street_1: 'testStreet1', - street_2: 'testStreet2', - city: 'testCity', - state: 'testState', - zip: 'testZip', - treasurer_phone: 'testTreasPhone', - } as CommitteeAccount; - component.onCreateContactDialogOpen(); - expect(component.createContactForm.get('committee_id')?.value).toBe( - component.selectedFecCommitteeAccount.committee_id - ); - component.onCreateContactDialogClose(); - expect(component.createContactFormSubmitted).toBeFalse(); - }); }); diff --git a/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.ts b/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.ts index a71ab9c449..e65e71aec2 100644 --- a/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.ts +++ b/front-end/src/app/shared/components/contact-lookup/contact-lookup.component.ts @@ -1,15 +1,8 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; -import { CommitteeAccount } from 'app/shared/models/committee-account.model'; -import { Contact, ContactTypes, FecApiCommitteeLookupData, FecApiLookupData } from 'app/shared/models/contact.model'; +import { Contact, ContactTypes, FecApiLookupData } from 'app/shared/models/contact.model'; import { ContactService } from 'app/shared/services/contact.service'; -import { FecApiService } from 'app/shared/services/fec-api.service'; -import { ValidateService } from 'app/shared/services/validate.service'; import { PrimeOptions } from 'app/shared/utils/label.utils'; -import { schema as contactCandidateSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Candidate'; -import { schema as contactCommitteeSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Committee'; -import { schema as contactIndividualSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Individual'; -import { schema as contactOrganizationSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Organization'; import { SelectItem, SelectItemGroup } from 'primeng/api'; @Component({ @@ -19,17 +12,19 @@ import { SelectItem, SelectItemGroup } from 'primeng/api'; }) export class ContactLookupComponent { @Input() contactTypeOptions: PrimeOptions = []; - @Input() contactTypeInputId = 'entity_type'; @Input() contactTypeFormControl: FormControl = new FormControl(); @Input() contactTypeReadOnly = false; - @Input() contactTypeStyleClass = ''; + @Input() showSearchBox = true; + @Input() showCreateNewContactButton = true; @Input() maxFecCommitteeResults = 5; @Input() maxFecfileCommitteeResults = 5; @Input() maxFecfileIndividualResults = 10; @Input() maxFecfileOrganizationResults = 10; + @Input() includeFecfileResults = true; - @Output() contactSelect = new EventEmitter>(); + @Output() contactLookupSelect = new EventEmitter(); + @Output() createNewContactSelect = new EventEmitter(); selectedContact: FormControl | null = null; @@ -42,29 +37,9 @@ export class ContactLookupComponent { searchTerm = ''; - createContactDialogVisible = false; - createContactFormSubmitted = false; - createContactForm: FormGroup = this.formBuilder.group( - this.validateService.getFormGroupFields([ - ...new Set([ - ...this.validateService.getSchemaProperties(contactIndividualSchema), - ...this.validateService.getSchemaProperties(contactCandidateSchema), - ...this.validateService.getSchemaProperties(contactCommitteeSchema), - ...this.validateService.getSchemaProperties(contactOrganizationSchema), - ]), - ]) - ); - - selectedFecCommitteeAccount: CommitteeAccount | undefined; - - workingValidatorSchema = this.validateService.formValidatorSchema; - workingValidatorForm = this.validateService.formValidatorForm; - constructor( private formBuilder: FormBuilder, - private validateService: ValidateService, - private contactService: ContactService, - private fecApiService: FecApiService, + private contactService: ContactService ) { } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -73,11 +48,20 @@ export class ContactLookupComponent { if (searchTerm) { this.searchTerm = searchTerm; switch (this.contactTypeFormControl.value) { + case ContactTypes.CANDIDATE: + this.contactService + .candidateLookup(searchTerm, this.maxFecCommitteeResults, this.maxFecfileCommitteeResults) + .subscribe((response) => { + this.contactLookupList = response && response.toSelectItemGroups( + this.includeFecfileResults); + }); + break; case ContactTypes.COMMITTEE: this.contactService .committeeLookup(searchTerm, this.maxFecCommitteeResults, this.maxFecfileCommitteeResults) .subscribe((response) => { - this.contactLookupList = response && response.toSelectItemGroups(); + this.contactLookupList = response && response.toSelectItemGroups( + this.includeFecfileResults); }); break; case ContactTypes.INDIVIDUAL: @@ -99,99 +83,17 @@ export class ContactLookupComponent { } // eslint-disable-next-line @typescript-eslint/no-explicit-any - onContactSelect(event: any) { - if (event && event.value) { - if (event.value instanceof Contact) { - this.contactSelect.emit(event); - } else if (event.value instanceof FecApiCommitteeLookupData) { - const value: FecApiCommitteeLookupData = event.value - if (value.id) { - this.fecApiService.getDetails(value.id) - .subscribe((committeeAccount) => { - this.openCreateContactDialog(committeeAccount); - }); - } - } - this.contactLookupForm.patchValue({ selectedContact: '' }); - } + onContactLookupSelect(event: any) { + this.contactLookupSelect.emit(event); + this.contactLookupForm.patchValue({ selectedContact: '' }); } - createNewContact() { - this.openCreateContactDialog(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onCreateNewContactSelect() { + this.createNewContactSelect.emit(); } isContact(value: Contact | FecApiLookupData) { return value instanceof Contact; } - - openCreateContactDialog(value?: CommitteeAccount) { - this.selectedFecCommitteeAccount = value; - // Need these since contact-form sets these for validation - this.workingValidatorSchema = this.validateService.formValidatorSchema; - this.workingValidatorForm = this.validateService.formValidatorForm; - - this.createContactDialogVisible = true; - } - - closeCreateContactDialog() { - // Need these since contact-form sets these for validation - this.validateService.formValidatorSchema = - this.workingValidatorSchema; - this.validateService.formValidatorForm = - this.workingValidatorForm; - - this.createContactDialogVisible = false; - } - - createContactSave() { - this.createContactFormSubmitted = true; - if (this.createContactForm.invalid) { - return; - } - - const createdContact = Contact.fromJSON({ - ...this.validateService.getFormValues(this.createContactForm) - }); - this.contactSelect.emit({ - value: createdContact - }); - this.closeCreateContactDialog(); - } - - onCreateContactDialogOpen() { - this.createContactForm.reset(); - this.createContactFormSubmitted = false; - const typeFormControl = this.createContactForm.get('type'); - typeFormControl?.setValue(this.contactTypeFormControl.value); - typeFormControl?.disable(); - let phone; - if (this.selectedFecCommitteeAccount?.treasurer_phone) { - phone = '+1 ' + this.selectedFecCommitteeAccount.treasurer_phone; - } - if (this.selectedFecCommitteeAccount) { - this.createContactForm.get('committee_id')?.setValue( - this.selectedFecCommitteeAccount.committee_id); - this.createContactForm.get('name')?.setValue( - this.selectedFecCommitteeAccount.name); - this.createContactForm.get('street_1')?.setValue( - this.selectedFecCommitteeAccount.street_1); - this.createContactForm.get('street_2')?.setValue( - this.selectedFecCommitteeAccount.street_2); - this.createContactForm.get('city')?.setValue( - this.selectedFecCommitteeAccount.city); - this.createContactForm.get('state')?.setValue( - this.selectedFecCommitteeAccount.state); - this.createContactForm.get('zip')?.setValue( - this.selectedFecCommitteeAccount.zip); - this.createContactForm.get('telephone')?.setValue(phone); - } - } - - onCreateContactDialogClose() { - this.selectedFecCommitteeAccount = undefined; - this.createContactForm.reset(); - this.createContactFormSubmitted = false; - this.createContactDialogVisible = false; - } - } diff --git a/front-end/src/app/shared/components/error-messages/error-messages.component.spec.ts b/front-end/src/app/shared/components/error-messages/error-messages.component.spec.ts index e678eb75c1..d934144cfa 100644 --- a/front-end/src/app/shared/components/error-messages/error-messages.component.spec.ts +++ b/front-end/src/app/shared/components/error-messages/error-messages.component.spec.ts @@ -1,14 +1,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder } from '@angular/forms'; import { JsonSchema } from 'app/shared/interfaces/json-schema.interface'; -import { ValidateService } from 'app/shared/services/validate.service'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; import { ErrorMessagesComponent } from './error-messages.component'; describe('ErrorMessagesComponent', () => { let component: ErrorMessagesComponent; let fixture: ComponentFixture; - let validateService: ValidateService; const testSchema: JsonSchema = { $schema: 'http://json-schema.org/draft-07/schema#', @@ -41,12 +40,10 @@ describe('ErrorMessagesComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ErrorMessagesComponent], - providers: [ValidateService], }).compileComponents(); }); beforeEach(() => { - validateService = TestBed.inject(ValidateService); fixture = TestBed.createComponent(ErrorMessagesComponent); component = fixture.componentInstance; @@ -58,12 +55,13 @@ describe('ErrorMessagesComponent', () => { }); it('should provide default error messages', () => { - validateService.formValidatorSchema = testSchema; const fb: FormBuilder = new FormBuilder(); - validateService.formValidatorForm = fb.group( - validateService.getFormGroupFields(['in_between', 'low_high', 'exclusive_low_high', 'exclusive_negative_amount']) + const formValidatorForm = fb.group( + ValidateUtils.getFormGroupFields(['in_between', + 'low_high', 'exclusive_low_high', 'exclusive_negative_amount']) ); - component.form = validateService.formValidatorForm; + ValidateUtils.addJsonSchemaValidators(formValidatorForm, testSchema, false); + component.form = formValidatorForm; component.fieldName = 'in_between'; component.ngOnInit(); component.form.patchValue({ in_between: 'short' }); @@ -88,10 +86,10 @@ describe('ErrorMessagesComponent', () => { it('should present a unique error message when a negative contribution amount is required', () => { //This has to be done separately because a new exclusiveMaxErrorMessage has to be generated - validateService.formValidatorSchema = testSchema; const fb: FormBuilder = new FormBuilder(); - validateService.formValidatorForm = fb.group(validateService.getFormGroupFields(['exclusive_negative_amount'])); - component.form = validateService.formValidatorForm; + const formValidatorForm = fb.group(ValidateUtils.getFormGroupFields(['exclusive_negative_amount'])); + ValidateUtils.addJsonSchemaValidators(formValidatorForm, testSchema, false); + component.form = formValidatorForm; component.fieldName = 'exclusive_negative_amount'; component.ngOnInit(); component.form.patchValue({ exclusive_negative_amount: 1 }); diff --git a/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.html b/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.html index 036ccac4f9..a4b04f4ff1 100644 --- a/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.html +++ b/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.html @@ -1,25 +1,22 @@
- + {{ purposeDescriptionLabelNotice }}
@@ -35,11 +32,33 @@ [cols]="30" pInputTextarea [autoResize]="true" - formControlName="memo_text_input" + [formControlName]="templateMap['memo_text_input']" > +
+
+
+
+
+
+ + +
diff --git a/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.spec.ts b/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.spec.ts index 8f1d974826..40d0bc434a 100644 --- a/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.spec.ts +++ b/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.spec.ts @@ -3,7 +3,7 @@ import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { InputTextareaModule } from 'primeng/inputtextarea'; import { ErrorMessagesComponent } from '../../error-messages/error-messages.component'; - +import { testTemplateMap } from 'app/shared/utils/unit-test.utils'; import { AdditionalInfoInputComponent } from './additional-info-input.component'; describe('AdditionalInfoInputComponent', () => { @@ -22,6 +22,7 @@ describe('AdditionalInfoInputComponent', () => { contribution_purpose_descrip: new FormControl(''), memo_text_input: new FormControl(''), }); + component.templateMap = testTemplateMap; component.descriptionIsSystemGenerated = true; fixture.detectChanges(); }); @@ -31,13 +32,13 @@ describe('AdditionalInfoInputComponent', () => { }); it('should have a read-only cpd if system generated', () => { - const cpd = fixture.debugElement.query(By.css('#contribution_purpose_descrip')); + const cpd = fixture.debugElement.query(By.css('#purpose_description')); expect(cpd.classes['readonly']).toBeTruthy(); }); it('should have a mutable cpd if not system generated', () => { component.descriptionIsSystemGenerated = false; - const cpd = fixture.debugElement.query(By.css('#contribution_purpose_descrip')); + const cpd = fixture.debugElement.query(By.css('#purpose_description')); fixture.detectChanges(); expect(cpd.classes['readonly']).toBeFalsy(); }); diff --git a/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.ts b/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.ts index 5ae158560b..59b37ab729 100644 --- a/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.ts +++ b/front-end/src/app/shared/components/inputs/additional-info-input/additional-info-input.component.ts @@ -1,5 +1,6 @@ import { Component, Input } from '@angular/core'; import { BaseInputComponent } from '../base-input.component'; +import { LabelUtils, PrimeOptions, CategoryCodeLabels } from 'app/shared/utils/label.utils'; @Component({ selector: 'app-additional-info-input', @@ -8,6 +9,7 @@ import { BaseInputComponent } from '../base-input.component'; }) export class AdditionalInfoInputComponent extends BaseInputComponent { @Input() descriptionIsSystemGenerated = false; - @Input() contributionPurposeDescriptionLabel = ''; + @Input() purposeDescriptionLabel = ''; @Input() purposeDescriptionLabelNotice?: string; + categoryCodeOptions: PrimeOptions = LabelUtils.getPrimeOptions(CategoryCodeLabels); } diff --git a/front-end/src/app/shared/components/inputs/address-input/address-input.component.html b/front-end/src/app/shared/components/inputs/address-input/address-input.component.html index 1b955af894..3fc32eac39 100644 --- a/front-end/src/app/shared/components/inputs/address-input/address-input.component.html +++ b/front-end/src/app/shared/components/inputs/address-input/address-input.component.html @@ -2,22 +2,22 @@
- - + +
- - + +
@@ -26,21 +26,21 @@
- - + +
- +
- - + +
diff --git a/front-end/src/app/shared/components/inputs/address-input/address-input.component.spec.ts b/front-end/src/app/shared/components/inputs/address-input/address-input.component.spec.ts index df9596c642..5c5b643a44 100644 --- a/front-end/src/app/shared/components/inputs/address-input/address-input.component.spec.ts +++ b/front-end/src/app/shared/components/inputs/address-input/address-input.component.spec.ts @@ -3,7 +3,7 @@ import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms'; import { DropdownModule } from 'primeng/dropdown'; import { InputTextModule } from 'primeng/inputtext'; import { ErrorMessagesComponent } from '../../error-messages/error-messages.component'; - +import { testTemplateMap } from 'app/shared/utils/unit-test.utils'; import { AddressInputComponent } from './address-input.component'; describe('AddressInputComponent', () => { @@ -25,6 +25,7 @@ describe('AddressInputComponent', () => { contributor_state: new FormControl(''), contributor_zip: new FormControl(''), }); + component.templateMap = testTemplateMap; fixture.detectChanges(); }); diff --git a/front-end/src/app/shared/components/inputs/amount-input/amount-input.component.html b/front-end/src/app/shared/components/inputs/amount-input/amount-input.component.html index ea379378f2..cb31a59109 100644 --- a/front-end/src/app/shared/components/inputs/amount-input/amount-input.component.html +++ b/front-end/src/app/shared/components/inputs/amount-input/amount-input.component.html @@ -2,15 +2,11 @@
- - + +
@@ -22,27 +18,29 @@ -
- {{ memoItemHelpText }} -
+
- +
- + { @@ -25,6 +25,7 @@ describe('AmountInputComponent', () => { contribution_amount: new FormControl(''), contribution_aggregate: new FormControl(''), }); + component.templateMap = testTemplateMap; fixture.detectChanges(); }); diff --git a/front-end/src/app/shared/components/inputs/amount-input/amount-input.component.ts b/front-end/src/app/shared/components/inputs/amount-input/amount-input.component.ts index d0bdc7ab64..b24c578c39 100644 --- a/front-end/src/app/shared/components/inputs/amount-input/amount-input.component.ts +++ b/front-end/src/app/shared/components/inputs/amount-input/amount-input.component.ts @@ -10,7 +10,7 @@ export class AmountInputComponent extends BaseInputComponent implements OnInit { @Input() memoCodeReadOnly = false; @Input() contributionAmountReadOnly = false; @Input() memoItemHelpText = - 'The dollar amount in a memo item is not incorporated into the total figure for the schedule.'; + 'The dollar amount in a memo item is not incorporated into the total figures for the schedule.'; @Input() negativeAmountValueOnly = false; @ViewChild('amountInput') amountInput!: InputNumber; diff --git a/front-end/src/app/shared/components/inputs/base-input.component.ts b/front-end/src/app/shared/components/inputs/base-input.component.ts index 64b618c9f4..7ed4e01c62 100644 --- a/front-end/src/app/shared/components/inputs/base-input.component.ts +++ b/front-end/src/app/shared/components/inputs/base-input.component.ts @@ -1,5 +1,6 @@ import { Component, Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; +import { TransactionTemplateMapType } from '../../models/transaction-type.model'; @Component({ template: '', @@ -7,4 +8,5 @@ import { FormGroup } from '@angular/forms'; export abstract class BaseInputComponent { @Input() form: FormGroup = new FormGroup([]); @Input() formSubmitted = false; + @Input() templateMap: TransactionTemplateMapType = {} as TransactionTemplateMapType; } diff --git a/front-end/src/app/shared/components/inputs/committee-input/committee-input.component.html b/front-end/src/app/shared/components/inputs/committee-input/committee-input.component.html index face6ac799..f6ee394071 100644 --- a/front-end/src/app/shared/components/inputs/committee-input/committee-input.component.html +++ b/front-end/src/app/shared/components/inputs/committee-input/committee-input.component.html @@ -2,27 +2,22 @@
- - + +
- - + +
diff --git a/front-end/src/app/shared/components/inputs/committee-input/committee-input.component.spec.ts b/front-end/src/app/shared/components/inputs/committee-input/committee-input.component.spec.ts index d6fade3b3e..0e527b64b1 100644 --- a/front-end/src/app/shared/components/inputs/committee-input/committee-input.component.spec.ts +++ b/front-end/src/app/shared/components/inputs/committee-input/committee-input.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms'; import { InputTextModule } from 'primeng/inputtext'; import { ErrorMessagesComponent } from '../../error-messages/error-messages.component'; - +import { testTemplateMap } from 'app/shared/utils/unit-test.utils'; import { CommitteeInputComponent } from './committee-input.component'; describe('CommitteeInputComponent', () => { @@ -21,6 +21,7 @@ describe('CommitteeInputComponent', () => { contributor_organization_name: new FormControl(''), donor_committee_fec_id: new FormControl(''), }); + component.templateMap = testTemplateMap; fixture.detectChanges(); }); diff --git a/front-end/src/app/shared/components/inputs/employer-input/employer-input.component.html b/front-end/src/app/shared/components/inputs/employer-input/employer-input.component.html index 375b10dee0..4fa55c055a 100644 --- a/front-end/src/app/shared/components/inputs/employer-input/employer-input.component.html +++ b/front-end/src/app/shared/components/inputs/employer-input/employer-input.component.html @@ -2,22 +2,22 @@
- - + +
- - + +
diff --git a/front-end/src/app/shared/components/inputs/employer-input/employer-input.component.spec.ts b/front-end/src/app/shared/components/inputs/employer-input/employer-input.component.spec.ts index da68df60c7..4139782369 100644 --- a/front-end/src/app/shared/components/inputs/employer-input/employer-input.component.spec.ts +++ b/front-end/src/app/shared/components/inputs/employer-input/employer-input.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms'; import { InputTextModule } from 'primeng/inputtext'; import { ErrorMessagesComponent } from '../../error-messages/error-messages.component'; - +import { testTemplateMap } from 'app/shared/utils/unit-test.utils'; import { EmployerInputComponent } from './employer-input.component'; describe('EmployerInputComponent', () => { @@ -21,6 +21,7 @@ describe('EmployerInputComponent', () => { contributor_employer: new FormControl(''), contributor_occupation: new FormControl(''), }); + component.templateMap = testTemplateMap; fixture.detectChanges(); }); diff --git a/front-end/src/app/shared/components/inputs/name-input/name-input.component.html b/front-end/src/app/shared/components/inputs/name-input/name-input.component.html index b33577875f..9fc65401aa 100644 --- a/front-end/src/app/shared/components/inputs/name-input/name-input.component.html +++ b/front-end/src/app/shared/components/inputs/name-input/name-input.component.html @@ -2,33 +2,33 @@
- - + +
- - + +
- - + +
@@ -37,22 +37,22 @@
- - + +
- - + +
diff --git a/front-end/src/app/shared/components/table-list-base/table-list-base.component.ts b/front-end/src/app/shared/components/table-list-base/table-list-base.component.ts index e6d414a3dd..96f95068db 100644 --- a/front-end/src/app/shared/components/table-list-base/table-list-base.component.ts +++ b/front-end/src/app/shared/components/table-list-base/table-list-base.component.ts @@ -185,3 +185,14 @@ export abstract class TableListBaseComponent implements OnInit, AfterViewInit return {}; } } + +export class RowAction { + label: string; + action: (item?: any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any + isAvailable: (item?: any) => boolean = () => true; // eslint-disable-line @typescript-eslint/no-explicit-any + constructor(label: string, action: (item?: any) => void, isAvailable?: (item?: any) => boolean) { // eslint-disable-line @typescript-eslint/no-explicit-any + this.label = label; + this.action = action; + this.isAvailable = isAvailable || this.isAvailable; + } +} diff --git a/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.html b/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.html new file mode 100644 index 0000000000..9ec7b2abca --- /dev/null +++ b/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.html @@ -0,0 +1,51 @@ +
+ + + + + + + + +
+
+ +
+
+ +
+
+
+
+
diff --git a/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.spec.ts b/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.spec.ts new file mode 100644 index 0000000000..9e02b7ff67 --- /dev/null +++ b/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.spec.ts @@ -0,0 +1,135 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { EventEmitter } from '@angular/core'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { provideMockStore } from '@ngrx/store/testing'; +import { + Contact, + ContactTypes, + FecApiCommitteeLookupData, + FecApiLookupData +} from 'app/shared/models/contact.model'; +import { testMockStore } from 'app/shared/utils/unit-test.utils'; +import { DropdownModule } from 'primeng/dropdown'; +import { of } from 'rxjs'; + +import { CommitteeAccount } from 'app/shared/models/committee-account.model'; +import { FecApiService } from 'app/shared/services/fec-api.service'; +import { SelectItem } from 'primeng/api'; +import { AutoCompleteModule } from 'primeng/autocomplete'; +import { DialogModule } from 'primeng/dialog'; +import { TransactionContactLookupComponent } from './transaction-contact-lookup.component'; + +describe('TransactionContactLookupComponent', () => { + let component: TransactionContactLookupComponent; + let fixture: ComponentFixture; + + let testFecApiService: FecApiService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TransactionContactLookupComponent], + imports: [ + FormsModule, + ReactiveFormsModule, + DialogModule, + HttpClientTestingModule, + DropdownModule, + AutoCompleteModule, + ], + providers: [FormBuilder, FecApiService, EventEmitter, provideMockStore(testMockStore)], + }).compileComponents(); + + testFecApiService = TestBed.inject(FecApiService); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TransactionContactLookupComponent); + component = fixture.componentInstance; + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('#onContactLookupSelect Contact happy path', fakeAsync(() => { + const eventEmitterEmitSpy = spyOn(component.contactSelect, 'emit'); + const testContact = Contact.fromJSON({ + id: 123, + last_name: 'testLastName', + first_name: 'testFirstName', + type: ContactTypes.COMMITTEE, + }); + const testValue = { + value: testContact, + } as SelectItem; + component.onContactLookupSelect(testValue); + tick(500); + expect(eventEmitterEmitSpy).toHaveBeenCalledOnceWith(testValue); + })); + + it('#onContactLookupSelect FecApiLookupData createContactForm null vals', fakeAsync(() => { + const testFecApiLookupData = new FecApiCommitteeLookupData({ id: 'C12345678' } as FecApiCommitteeLookupData); + const testValue = { + value: testFecApiLookupData, + } as SelectItem; + spyOn(testFecApiService, 'getCommitteeDetails').and.returnValue(of(new CommitteeAccount())); + component.createContactForm.removeControl('committee_id'); + component.createContactForm.removeControl('name'); + component.createContactForm.removeControl('street_1'); + component.createContactForm.removeControl('street_2'); + component.createContactForm.removeControl('city'); + component.createContactForm.removeControl('state'); + component.createContactForm.removeControl('zip'); + component.onContactLookupSelect(testValue); + tick(500); + expect(component.createContactDialogVisible).toEqual(true); + })); + + it('#onContactLookupSelect FecApiLookupData happy path', fakeAsync(() => { + const testFecApiLookupData = new FecApiCommitteeLookupData({ id: 'C12345678' } as FecApiCommitteeLookupData); + const testValue = { + value: testFecApiLookupData, + } as SelectItem; + spyOn(testFecApiService, 'getCommitteeDetails').and.returnValue(of(new CommitteeAccount())); + + component.onContactLookupSelect(testValue); + tick(500); + expect(component.createContactDialogVisible).toEqual(true); + })); + + it('#onCreateContactDialogOpen null form control', () => { + component.createContactForm = new FormGroup({}); + component.selectedFecCommitteeAccount = {} as CommitteeAccount; + + component.onCreateContactDialogOpen(); + expect(component.createContactFormSubmitted).toBeFalse(); + component.selectedFecCommitteeAccount = undefined; + component.onCreateContactDialogOpen(); + }); + + it('#createNewContact happy path', () => { + component.onCreateNewContactSelect(); + component.closeCreateContactDialog(); + component.createContactSave(); + component.selectedFecCommitteeAccount = { + committee_id: 'testCommitteeId', + name: 'testName', + street_1: 'testStreet1', + street_2: 'testStreet2', + city: 'testCity', + state: 'testState', + zip: 'testZip', + treasurer_phone: 'testTreasPhone', + } as CommitteeAccount; + component.onCreateContactDialogOpen(); + expect(component.createContactForm.get('committee_id')?.value).toBe( + component.selectedFecCommitteeAccount.committee_id + ); + component.onCreateContactDialogClose(); + expect(component.createContactFormSubmitted).toBeFalse(); + }); + +}); diff --git a/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.ts b/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.ts new file mode 100644 index 0000000000..cc81218c41 --- /dev/null +++ b/front-end/src/app/shared/components/transaction-contact-lookup/transaction-contact-lookup.component.ts @@ -0,0 +1,121 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { CommitteeAccount } from 'app/shared/models/committee-account.model'; +import { Contact, ContactType, FecApiCommitteeLookupData } from 'app/shared/models/contact.model'; +import { ContactService } from 'app/shared/services/contact.service'; +import { FecApiService } from 'app/shared/services/fec-api.service'; +import { PrimeOptions } from 'app/shared/utils/label.utils'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; +import { schema as contactCandidateSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Candidate'; +import { schema as contactCommitteeSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Committee'; +import { schema as contactIndividualSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Individual'; +import { schema as contactOrganizationSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Organization'; +import { SelectItem } from 'primeng/api'; + +@Component({ + selector: 'app-transaction-contact-lookup', + templateUrl: './transaction-contact-lookup.component.html', +}) +export class TransactionContactLookupComponent { + @Input() contactTypeOptions: PrimeOptions = []; + @Input() contactTypeFormControl: FormControl = new FormControl(); + @Input() contactTypeReadOnly = false; + + @Output() contactSelect = new EventEmitter>(); + + createContactDialogVisible = false; + createContactFormSubmitted = false; + createContactForm: FormGroup = this.formBuilder.group( + ValidateUtils.getFormGroupFields([ + ...new Set([ + ...ValidateUtils.getSchemaProperties(contactIndividualSchema), + ...ValidateUtils.getSchemaProperties(contactCandidateSchema), + ...ValidateUtils.getSchemaProperties(contactCommitteeSchema), + ...ValidateUtils.getSchemaProperties(contactOrganizationSchema), + ]), + ]) + ); + + selectedFecCommitteeAccount: CommitteeAccount | undefined; + + constructor( + private formBuilder: FormBuilder, + private fecApiService: FecApiService + ) { } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onContactLookupSelect(event: any) { + if (event && event.value) { + if (event.value instanceof Contact) { + this.contactSelect.emit(event); + } else if (event.value instanceof FecApiCommitteeLookupData) { + const value: FecApiCommitteeLookupData = event.value; + if (value.id) { + this.fecApiService.getCommitteeDetails(value.id).subscribe((committeeAccount) => { + this.openCreateContactDialog(committeeAccount); + }); + } + } + } + } + + onCreateNewContactSelect() { + this.openCreateContactDialog(); + } + + openCreateContactDialog(value?: CommitteeAccount) { + this.selectedFecCommitteeAccount = value; + this.createContactDialogVisible = true; + } + + closeCreateContactDialog() { + this.createContactDialogVisible = false; + } + + createContactSave() { + this.createContactFormSubmitted = true; + if (this.createContactForm.invalid) { + return; + } + + const createdContact = Contact.fromJSON({ + ...ValidateUtils.getFormValues(this.createContactForm, + ContactService.getSchemaByType( + this.createContactForm.get('type')?.value as ContactType)), + }); + this.contactSelect.emit({ + value: createdContact, + }); + this.closeCreateContactDialog(); + } + + onCreateContactDialogOpen() { + this.createContactForm.reset(); + this.createContactFormSubmitted = false; + const typeFormControl = this.createContactForm.get('type'); + typeFormControl?.setValue(this.contactTypeFormControl.value); + typeFormControl?.disable(); + let phone; + if (this.selectedFecCommitteeAccount?.treasurer_phone) { + phone = '+1 ' + this.selectedFecCommitteeAccount.treasurer_phone; + } + if (this.selectedFecCommitteeAccount) { + this.createContactForm.get('committee_id')?.setValue(this.selectedFecCommitteeAccount.committee_id); + this.createContactForm.get('name')?.setValue(this.selectedFecCommitteeAccount.name); + this.createContactForm.get('street_1')?.setValue(this.selectedFecCommitteeAccount.street_1); + this.createContactForm.get('street_2')?.setValue(this.selectedFecCommitteeAccount.street_2); + this.createContactForm.get('city')?.setValue(this.selectedFecCommitteeAccount.city); + this.createContactForm.get('state')?.setValue(this.selectedFecCommitteeAccount.state); + this.createContactForm.get('zip')?.setValue(this.selectedFecCommitteeAccount.zip); + this.createContactForm.get('telephone')?.setValue(phone); + } + } + + onCreateContactDialogClose() { + this.selectedFecCommitteeAccount = undefined; + this.createContactForm.reset(); + this.createContactFormSubmitted = false; + this.createContactDialogVisible = false; + } + +} diff --git a/front-end/src/app/shared/components/transaction-type-base/double-transaction-type-base.component.spec.ts b/front-end/src/app/shared/components/transaction-type-base/double-transaction-type-base.component.spec.ts index c4cf2c5da3..b050111878 100644 --- a/front-end/src/app/shared/components/transaction-type-base/double-transaction-type-base.component.spec.ts +++ b/front-end/src/app/shared/components/transaction-type-base/double-transaction-type-base.component.spec.ts @@ -1,12 +1,14 @@ +import { DatePipe } from '@angular/common'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; +import { SchATransaction, ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; +import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; import { TransactionService } from 'app/shared/services/transaction.service'; -import { ValidateService } from 'app/shared/services/validate.service'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; -import { MessageService } from 'primeng/api'; +import { getTestTransactionByType, testMockStore } from 'app/shared/utils/unit-test.utils'; +import { ConfirmationService, MessageService } from 'primeng/api'; import { DoubleTransactionTypeBaseComponent } from './double-transaction-type-base.component'; class TestDoubleTransactionTypeBaseComponent extends DoubleTransactionTypeBaseComponent { @@ -57,25 +59,39 @@ class TestDoubleTransactionTypeBaseComponent extends DoubleTransactionTypeBaseCo ]; } -describe('TransactionTypeBaseComponent', () => { +describe('DoubleTransactionTypeBaseComponent', () => { let component: TestDoubleTransactionTypeBaseComponent; let fixture: ComponentFixture; + let testTransaction: SchATransaction; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [TestDoubleTransactionTypeBaseComponent], imports: [RouterTestingModule, HttpClientTestingModule], - providers: [MessageService, FormBuilder, ValidateService, TransactionService, provideMockStore(testMockStore)], + providers: [ + DatePipe, + MessageService, + FormBuilder, + TransactionService, + ConfirmationService, + provideMockStore(testMockStore), + FecDatePipe, + ], }).compileComponents(); }); beforeEach(() => { + testTransaction = getTestTransactionByType(ScheduleATransactionTypes.PAC_EARMARK_RECEIPT) as SchATransaction; + testTransaction.children = [ + getTestTransactionByType(ScheduleATransactionTypes.PAC_EARMARK_MEMO) as SchATransaction, + ]; fixture = TestBed.createComponent(TestDoubleTransactionTypeBaseComponent); component = fixture.componentInstance; + component.transaction = testTransaction; fixture.detectChanges(); }); - xit('should create', () => { + it('should create', () => { expect(component).toBeTruthy(); }); }); diff --git a/front-end/src/app/shared/components/transaction-type-base/double-transaction-type-base.component.ts b/front-end/src/app/shared/components/transaction-type-base/double-transaction-type-base.component.ts index 6fffad81dd..5442955169 100644 --- a/front-end/src/app/shared/components/transaction-type-base/double-transaction-type-base.component.ts +++ b/front-end/src/app/shared/components/transaction-type-base/double-transaction-type-base.component.ts @@ -1,16 +1,16 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; -import { BehaviorSubject, Subject, takeUntil } from 'rxjs'; -import { SchATransaction } from 'app/shared/models/scha-transaction.model'; import { NavigationEvent } from 'app/shared/models/transaction-navigation-controls.model'; -import { Transaction } from 'app/shared/models/transaction.model'; -import { ValidateService } from 'app/shared/services/validate.service'; +import { TransactionTemplateMapType } from 'app/shared/models/transaction-type.model'; +import { ScheduleTransaction, Transaction } from 'app/shared/models/transaction.model'; import { LabelUtils, PrimeOptions } from 'app/shared/utils/label.utils'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; import { SelectItem } from 'primeng/api'; +import { BehaviorSubject, Subject, takeUntil } from 'rxjs'; import { Contact, ContactTypeLabels, ContactTypes } from '../../models/contact.model'; -import { TransactionTypeBaseComponent } from './transaction-type-base.component'; -import { TransactionFormUtils } from './transaction-form.utils'; import { TransactionContactUtils } from './transaction-contact.utils'; +import { TransactionFormUtils } from './transaction-form.utils'; +import { TransactionTypeBaseComponent } from './transaction-type-base.component'; /** * This component is to help manage a form that contains 2 transactions that the @@ -28,103 +28,102 @@ import { TransactionContactUtils } from './transaction-contact.utils'; }) export abstract class DoubleTransactionTypeBaseComponent extends TransactionTypeBaseComponent - implements OnInit, OnDestroy -{ + implements OnInit, OnDestroy { abstract childFormProperties: string[]; + childTransaction?: Transaction; childContactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels); childForm: FormGroup = this.fb.group({}); - childValidateService: ValidateService = new ValidateService(); childContactId$: Subject = new BehaviorSubject(''); - childContributionPurposeDescriptionLabel = ''; + childPurposeDescriptionLabel = ''; childNegativeAmountValueOnly = false; + childTemplateMap: TransactionTemplateMapType = {} as TransactionTemplateMapType; override ngOnInit(): void { // Initialize primary form. super.ngOnInit(); // Initialize child form. - this.childForm = this.fb.group(this.childValidateService.getFormGroupFields(this.childFormProperties)); - TransactionFormUtils.onInit( - this, - this.childForm, - this.childValidateService, - this.transactionType?.childTransactionType, - this.childContactId$ - ); - - this.childOnInit(); + this.childForm = this.fb.group(ValidateUtils.getFormGroupFields( + this.childFormProperties)); + if (this.transaction?.children) { + this.childTransaction = this.transaction?.children[0]; + if (this.childTransaction.transactionType?.templateMap) { + this.childTemplateMap = this.childTransaction.transactionType.templateMap; + } else { + throw new Error('Fecfile: Template map not found for double transaction component'); + } + TransactionFormUtils.onInit( + this, + this.childForm, + this.childTransaction, + this.childContactId$ + ); + this.childOnInit(); + } } childOnInit() { // Override contact type options if present in transactionType - if (this.transactionType?.childTransactionType && this.transactionType.childTransactionType.contactTypeOptions) { - this.childContactTypeOptions = LabelUtils.getPrimeOptions( - ContactTypeLabels, - this.transactionType.childTransactionType.contactTypeOptions - ); - } + this.childContactTypeOptions = LabelUtils.getPrimeOptions( + ContactTypeLabels, + this.childTransaction?.transactionType?.contactTypeOptions + ); - const contribution_amount_schema = - this.transactionType?.childTransactionType?.schema.properties['contribution_amount']; - if (contribution_amount_schema?.exclusiveMaximum === 0) { + const amountProperty = this.childTemplateMap.amount; + const amount_schema = this.childTransaction?.transactionType?.schema.properties[amountProperty]; + if (amount_schema?.exclusiveMaximum === 0) { this.childNegativeAmountValueOnly = true; this.childForm - .get('contribution_amount') + .get(amountProperty) ?.valueChanges.pipe(takeUntil(this.destroy$)) - .subscribe((contribution_amount) => { - if (typeof contribution_amount === 'number' && contribution_amount > 0) { - this.childForm.patchValue({ contribution_amount: -1 * contribution_amount }); + .subscribe((amount) => { + if (+amount > 0) { + this.childForm.patchValue({ amount: -1 * amount }); } }); } - if (this.transactionType?.childTransactionType?.generatePurposeDescriptionLabel) { - this.childContributionPurposeDescriptionLabel = - this.transactionType.childTransactionType.generatePurposeDescriptionLabel(); + if (this.childTransaction?.transactionType?.generatePurposeDescriptionLabel) { + this.childPurposeDescriptionLabel = this.childTransaction.transactionType.generatePurposeDescriptionLabel(); } // Default the child entity type to Committee - if (!this.transactionType?.childTransactionType?.transaction?.id) { + if (!this.childTransaction?.id) { this.childForm.get('entity_type')?.setValue(ContactTypes.COMMITTEE); } // Parent contribution purpose description updates with child contributor name updates. this.childForm - .get('contributor_organization_name') + .get(this.childTemplateMap.organization_name) ?.valueChanges.pipe(takeUntil(this.destroy$)) .subscribe((value) => { - const childTransaction: SchATransaction = this.transactionType?.childTransactionType - ?.transaction as SchATransaction; - childTransaction.contributor_organization_name = value; - this.updateContributionPurposeDescription(); + const key = this.childTemplateMap.organization_name as keyof ScheduleTransaction; + ((this.childTransaction as ScheduleTransaction)[key] as string) = value; + this.updateParentPurposeDescription(); }); this.childForm - .get('contributor_first_name') + .get(this.childTemplateMap.first_name) ?.valueChanges.pipe(takeUntil(this.destroy$)) .subscribe((value) => { - const memo: SchATransaction = this.transactionType?.childTransactionType?.transaction as SchATransaction; - if (memo) { - memo.contributor_first_name = value; - } - this.updateContributionPurposeDescription(); + const key = this.childTemplateMap.first_name as keyof ScheduleTransaction; + ((this.childTransaction as ScheduleTransaction)[key] as string) = value; + this.updateParentPurposeDescription(); }); this.childForm - .get('contributor_last_name') + .get(this.childTemplateMap.last_name) ?.valueChanges.pipe(takeUntil(this.destroy$)) .subscribe((value) => { - const memo: SchATransaction = this.transactionType?.childTransactionType?.transaction as SchATransaction; - if (memo) { - memo.contributor_last_name = value; - } - this.updateContributionPurposeDescription(); + const key = this.childTemplateMap.last_name as keyof ScheduleTransaction; + ((this.childTransaction as ScheduleTransaction)[key] as string) = value; + this.updateParentPurposeDescription(); }); // Child amount must match parent contribution amount this.form - .get('contribution_amount') + .get(this.templateMap.amount) ?.valueChanges.pipe(takeUntil(this.destroy$)) .subscribe((value) => { - this.childForm.get('contribution_amount')?.setValue(value); + this.childForm.get(this.childTemplateMap.amount)?.setValue(value); }); } @@ -133,14 +132,14 @@ export abstract class DoubleTransactionTypeBaseComponent this.childContactId$.complete(); } - private updateContributionPurposeDescription() { - const childTransaction: SchATransaction = this.transactionType?.childTransactionType - ?.transaction as SchATransaction; - childTransaction.entity_type = this.childForm.get('entity_type')?.value; + private updateParentPurposeDescription() { + (this.childTransaction as ScheduleTransaction).entity_type = this.childForm.get('entity_type')?.value; - if (this.transactionType?.generatePurposeDescription) { + if (this.transaction?.transactionType?.generatePurposeDescription) { this.form.patchValue({ - contribution_purpose_descrip: this.transactionType.generatePurposeDescriptionWrapper(), + [this.templateMap.purpose_description]: this.transaction.transactionType.generatePurposeDescriptionWrapper( + this.transaction + ), }); } } @@ -153,15 +152,13 @@ export abstract class DoubleTransactionTypeBaseComponent } const payload: Transaction = TransactionFormUtils.getPayloadTransaction( - this.transactionType, - this.validateService, + this.transaction, this.form, this.formProperties ); payload.children = [ TransactionFormUtils.getPayloadTransaction( - this.transactionType?.childTransactionType, - this.childValidateService, + this.childTransaction, this.childForm, this.childFormProperties ), @@ -176,25 +173,21 @@ export abstract class DoubleTransactionTypeBaseComponent if (payload.children?.length === 1) { this.confirmSave(payload.children[0], this.childForm, this.doSave, navigationEvent, payload, 'childDialog'); } else { - throw new Error('Parent transaction missing child transaction when trying to confirm save.'); + throw new Error('Fecfile: Parent transaction missing child transaction when trying to confirm save.'); } } override resetForm() { this.formSubmitted = false; - TransactionFormUtils.resetForm(this.form, this.transactionType, this.contactTypeOptions); - TransactionFormUtils.resetForm( - this.childForm, - this.transactionType?.childTransactionType, - this.childContactTypeOptions - ); + TransactionFormUtils.resetForm(this.form, this.transaction, this.contactTypeOptions); + TransactionFormUtils.resetForm(this.childForm, this.childTransaction, this.childContactTypeOptions); } childOnContactLookupSelect(selectItem: SelectItem) { TransactionContactUtils.onContactLookupSelect( selectItem, this.childForm, - this.transactionType?.childTransactionType, + this.childTransaction, this.childContactId$ ); } diff --git a/front-end/src/app/shared/components/transaction-type-base/transaction-contact.utils.ts b/front-end/src/app/shared/components/transaction-type-base/transaction-contact.utils.ts index cc561622af..4c4333ec3f 100644 --- a/front-end/src/app/shared/components/transaction-type-base/transaction-contact.utils.ts +++ b/front-end/src/app/shared/components/transaction-type-base/transaction-contact.utils.ts @@ -1,16 +1,18 @@ import { AbstractControl, FormGroup } from '@angular/forms'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionTemplateMapType } from 'app/shared/models/transaction-type.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; import { SelectItem } from 'primeng/api'; import { Subject } from 'rxjs'; import { Contact, ContactFields, ContactTypes } from '../../models/contact.model'; +import { Transaction } from 'app/shared/models/transaction.model'; export class TransactionContactUtils { static getEditTransactionContactConfirmationMessage( contactChanges: string[], contact: Contact | undefined, form: FormGroup, - fecDatePipe: FecDatePipe + fecDatePipe: FecDatePipe, + templateMap: TransactionTemplateMapType ): string | undefined { if (contact) { const changesMessage = 'Change(s):
    '.concat( @@ -21,7 +23,7 @@ export class TransactionContactUtils { contactName = `${contact.last_name}, ${contact.first_name}`; contactName += contact.middle_name ? ' ' + contact.middle_name : ''; } - const dateReceived = fecDatePipe.transform(form.get('contribution_date')?.value); + const dateReceived = fecDatePipe.transform(form.get(templateMap.date)?.value); return ( `By saving this transaction, you are also updating the contact for ` + `${contactName}. This change will only affect transactions with ` + @@ -31,22 +33,26 @@ export class TransactionContactUtils { return undefined; } - static getCreateTransactionContactConfirmationMessage(contactType: ContactTypes, form: FormGroup): string { + static getCreateTransactionContactConfirmationMessage( + contactType: ContactTypes, + form: FormGroup, + templateMap: TransactionTemplateMapType + ): string { let confirmationContactTitle = ''; switch (contactType) { case ContactTypes.INDIVIDUAL: confirmationContactTitle = `individual contact for ` + - `${form.get('contributor_last_name')?.value}, ` + - `${form.get('contributor_first_name')?.value}`; + `${form.get(templateMap.last_name)?.value}, ` + + `${form.get(templateMap.first_name)?.value}`; break; case ContactTypes.COMMITTEE: confirmationContactTitle = - `committee contact for ` + `${form.get('contributor_organization_name')?.value}`; + `committee contact for ` + `${form.get(templateMap.organization_name)?.value}`; break; case ContactTypes.ORGANIZATION: confirmationContactTitle = - `organization contact for ` + `${form.get('contributor_organization_name')?.value}`; + `organization contact for ` + `${form.get(templateMap.organization_name)?.value}`; break; } return `By saving this transaction, you're also creating a new ${confirmationContactTitle}.`; @@ -59,19 +65,30 @@ export class TransactionContactUtils { * first setting these values on the Contact object. * @returns string[] containing the changes in prose for the UI. */ - static setTransactionContactFormChanges(form: FormGroup, contact: Contact | undefined): string[] { - function getFormField(form: FormGroup, field: string): AbstractControl | null { + static setTransactionContactFormChanges( + form: FormGroup, + contact: Contact | undefined, + templateMap: TransactionTemplateMapType + ): string[] { + function getFormField( + form: FormGroup, + field: string, + templateMap: TransactionTemplateMapType + ): AbstractControl | null { if (field == 'committee_id') { - return form.get('donor_committee_fec_id'); + return form.get(templateMap.committee_fec_id); + } + if (field == 'name') { + return form.get(templateMap.organization_name); } - return form.get(`contributor_${field}`) || form.get(`contributor_organization_${field}`); + return form.get(templateMap[field as keyof TransactionTemplateMapType]); } if (contact) { return Object.entries(ContactFields) .map(([field, label]: string[]) => { const contactValue = contact[field as keyof typeof contact]; - const formField = getFormField(form, field); + const formField = getFormField(form, field, templateMap); if (formField && formField?.value !== contactValue) { contact[field as keyof typeof contact] = (formField.value || '') as never; @@ -87,37 +104,38 @@ export class TransactionContactUtils { static onContactLookupSelect( selectItem: SelectItem, form: FormGroup, - transactionType: TransactionType | undefined, + transaction: Transaction | undefined, contactId$: Subject ) { if (selectItem) { const contact: Contact = selectItem.value; - if (contact) { + const templateMap = transaction?.transactionType?.templateMap; + if (contact && templateMap) { switch (contact.type) { case ContactTypes.INDIVIDUAL: - form.get('contributor_last_name')?.setValue(contact.last_name); - form.get('contributor_first_name')?.setValue(contact.first_name); - form.get('contributor_middle_name')?.setValue(contact.middle_name); - form.get('contributor_prefix')?.setValue(contact.prefix); - form.get('contributor_suffix')?.setValue(contact.suffix); - form.get('contributor_employer')?.setValue(contact.employer); - form.get('contributor_occupation')?.setValue(contact.occupation); + form.get(templateMap.last_name)?.setValue(contact.last_name); + form.get(templateMap.first_name)?.setValue(contact.first_name); + form.get(templateMap.middle_name)?.setValue(contact.middle_name); + form.get(templateMap.prefix)?.setValue(contact.prefix); + form.get(templateMap.suffix)?.setValue(contact.suffix); + form.get(templateMap.employer)?.setValue(contact.employer); + form.get(templateMap.occupation)?.setValue(contact.occupation); break; case ContactTypes.COMMITTEE: - form.get('donor_committee_fec_id')?.setValue(contact.committee_id); - form.get('contributor_organization_name')?.setValue(contact.name); + form.get(templateMap.committee_fec_id)?.setValue(contact.committee_id); + form.get(templateMap.organization_name)?.setValue(contact.name); break; case ContactTypes.ORGANIZATION: - form.get('contributor_organization_name')?.setValue(contact.name); + form.get(templateMap.organization_name)?.setValue(contact.name); break; } - form.get('contributor_street_1')?.setValue(contact.street_1); - form.get('contributor_street_2')?.setValue(contact.street_2); - form.get('contributor_city')?.setValue(contact.city); - form.get('contributor_state')?.setValue(contact.state); - form.get('contributor_zip')?.setValue(contact.zip); - if (transactionType?.transaction) { - transactionType.transaction.contact = contact; + form.get(templateMap.street_1)?.setValue(contact.street_1); + form.get(templateMap.street_2)?.setValue(contact.street_2); + form.get(templateMap.city)?.setValue(contact.city); + form.get(templateMap.state)?.setValue(contact.state); + form.get(templateMap.zip)?.setValue(contact.zip); + if (transaction) { + transaction.contact = contact; } contactId$.next(contact.id || ''); } diff --git a/front-end/src/app/shared/components/transaction-type-base/transaction-form.utils.ts b/front-end/src/app/shared/components/transaction-type-base/transaction-form.utils.ts index 72f006cf5b..9e3b0b2031 100644 --- a/front-end/src/app/shared/components/transaction-type-base/transaction-form.utils.ts +++ b/front-end/src/app/shared/components/transaction-type-base/transaction-form.utils.ts @@ -1,14 +1,14 @@ import { FormGroup } from '@angular/forms'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; -import { SchATransaction } from 'app/shared/models/scha-transaction.model'; -import { Transaction } from 'app/shared/models/transaction.model'; -import { ValidateService } from 'app/shared/services/validate.service'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; +import { ScheduleTransaction, Transaction } from 'app/shared/models/transaction.model'; import { PrimeOptions } from 'app/shared/utils/label.utils'; +import { getFromJSON } from 'app/shared/utils/transaction-type.utils'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; import { combineLatestWith, Observable, of, startWith, Subject, switchMap, takeUntil } from 'rxjs'; import { ContactTypes } from '../../models/contact.model'; +import { DoubleTransactionTypeBaseComponent } from './double-transaction-type-base.component'; import { TransactionMemoUtils } from './transaction-memo.utils'; import { TransactionTypeBaseComponent } from './transaction-type-base.component'; -import { DoubleTransactionTypeBaseComponent } from './double-transaction-type-base.component'; export class TransactionFormUtils { /** @@ -18,99 +18,101 @@ export class TransactionFormUtils { * child transaction type defined in the DoubleTransactionTypeBase class. * @param component * @param form - parent or child (i.e. form or childForm) - * @param contactTypeOptions - parent or child (i.e. contactTypeOptions or childContactTypeOptions) - * @param validateService - parent or child (i.e. validateService or childValidateService) - * @param transactionType - parent or child (i.e. transactionType or transactionType?.childTransactionType) + * @param transaction - parent or child * @param contactId$ - parent or child (i.e. contactId$ or childContactId$) */ static onInit( component: TransactionTypeBaseComponent | DoubleTransactionTypeBaseComponent, form: FormGroup, - validateService: ValidateService, - transactionType: TransactionType | undefined, + transaction: Transaction | undefined, contactId$: Subject ): void { - // Initialize validation tracking of current JSON schema and form data - validateService.formValidatorSchema = transactionType?.schema; - validateService.formValidatorForm = form; - - // Intialize form values - function isExisting(transaction: Transaction | undefined) { - return !!transaction?.id; - } + if (transaction && transaction.id) { + form.patchValue({ ...transaction }); - if (isExisting(transactionType?.transaction)) { - const txn = { ...transactionType?.transaction } as SchATransaction; - form.patchValue({ ...txn }); - - TransactionMemoUtils.patchMemoText(transactionType, form); + TransactionMemoUtils.patchMemoText(transaction, form); form.get('entity_type')?.disable(); - contactId$.next(txn.contact_id || ''); + contactId$.next(transaction.contact_id || ''); } else { component.resetForm(); form.get('entity_type')?.enable(); contactId$.next(''); } + const templateMap = transaction?.transactionType?.templateMap; + if (!templateMap) { + throw new Error('Fecfile: Cannot find template map when initializing transaction form'); + } + form .get('entity_type') ?.valueChanges.pipe(takeUntil(component.destroy$)) .subscribe((value: string) => { if (value === ContactTypes.INDIVIDUAL || value === ContactTypes.CANDIDATE) { - form.get('contributor_organization_name')?.reset(); + form.get(templateMap.organization_name)?.reset(); } if (value === ContactTypes.ORGANIZATION || value === ContactTypes.COMMITTEE) { - form.get('contributor_last_name')?.reset(); - form.get('contributor_first_name')?.reset(); - form.get('contributor_middle_name')?.reset(); - form.get('contributor_prefix')?.reset(); - form.get('contributor_suffix')?.reset(); - form.get('contributor_employer')?.reset(); - form.get('contributor_occupation')?.reset(); + form.get(templateMap.last_name)?.reset(); + form.get(templateMap.first_name)?.reset(); + form.get(templateMap.middle_name)?.reset(); + form.get(templateMap.prefix)?.reset(); + form.get(templateMap.suffix)?.reset(); + form.get(templateMap.employer)?.reset(); + form.get(templateMap.occupation)?.reset(); } }); form - ?.get('contribution_aggregate') + ?.get(templateMap.aggregate) ?.valueChanges.pipe(takeUntil(component.destroy$)) .subscribe(() => { - form.get('contributor_employer')?.updateValueAndValidity(); - form.get('contributor_occupation')?.updateValueAndValidity(); + form.get(templateMap.employer)?.updateValueAndValidity(); + form.get(templateMap.occupation)?.updateValueAndValidity(); }); const previous_transaction$: Observable = - form.get('contribution_date')?.valueChanges.pipe( - startWith(form.get('contribution_date')?.value), + form.get(templateMap.date)?.valueChanges.pipe( + startWith(form.get(templateMap.date)?.value), combineLatestWith(contactId$), switchMap(([contribution_date, contactId]) => { - return component.transactionService.getPreviousTransaction(transactionType, contactId, contribution_date); + return component.transactionService.getPreviousTransaction(transaction, contactId, contribution_date); }) ) || of(undefined); form - .get('contribution_amount') + .get(templateMap.amount) ?.valueChanges.pipe( - startWith(form.get('contribution_amount')?.value), + startWith(form.get(templateMap.amount)?.value), combineLatestWith(previous_transaction$), takeUntil(component.destroy$) ) - .subscribe(([contribution_amount, previous_transaction]) => { - const previousAggregate = +((previous_transaction as SchATransaction)?.contribution_aggregate || 0); - form.get('contribution_aggregate')?.setValue(+contribution_amount + previousAggregate); + .subscribe(([amount, previous_transaction]) => { + const key = templateMap.aggregate as keyof ScheduleTransaction; + const previousAggregate = previous_transaction ? +((previous_transaction as ScheduleTransaction)[key] || 0) : 0; + form.get(templateMap.aggregate)?.setValue(+amount + previousAggregate); }); + + const schema = transaction.transactionType?.schema; + if (schema) { + ValidateUtils.addJsonSchemaValidators(form, schema, false, transaction); + } } static getPayloadTransaction( - transactionType: TransactionType | undefined, - validateService: ValidateService, + transaction: Transaction | undefined, form: FormGroup, formProperties: string[] - ): SchATransaction { - let formValues = validateService.getFormValues(form, formProperties); - if (transactionType) formValues = TransactionMemoUtils.retrieveMemoText(transactionType, form, formValues); + ): Transaction { + if (!transaction) { + throw new Error('Fecfile: Payload transaction not found'); + } - const payload: SchATransaction = SchATransaction.fromJSON({ - ...transactionType?.transaction, + let formValues = ValidateUtils.getFormValues(form, + transaction.transactionType?.schema, formProperties); + formValues = TransactionMemoUtils.retrieveMemoText(transaction, form, formValues); + + const payload: ScheduleTransaction = getFromJSON({ + ...transaction, ...formValues, }); if (payload.children) { @@ -120,17 +122,27 @@ export class TransactionFormUtils { return payload; } - static resetForm(form: FormGroup, transactionType: TransactionType | undefined, contactTypeOptions: PrimeOptions) { + static resetForm(form: FormGroup, transaction: Transaction | undefined, contactTypeOptions: PrimeOptions) { form.reset(); form.markAsPristine(); form.markAsUntouched(); - form.patchValue({ - entity_type: contactTypeOptions[0]?.code, - contribution_aggregate: '0', - memo_code: this.getMemoCodeConstant(transactionType), - contribution_purpose_descrip: transactionType?.generatePurposeDescriptionWrapper(), - }); + // Override the default entity_type value if called for by the defaultContactTypeOption + // in the TransactionType + let defaultContactTypeOption: string = contactTypeOptions[0]?.code; + if (transaction?.transactionType?.defaultContactTypeOption) { + defaultContactTypeOption = transaction.transactionType.defaultContactTypeOption; + } + + if (transaction?.transactionType) { + form.patchValue({ + entity_type: defaultContactTypeOption, + [transaction.transactionType.templateMap.aggregate]: '0', + memo_code: this.getMemoCodeConstant(transaction?.transactionType), + [transaction.transactionType.templateMap.purpose_description]: + transaction?.transactionType?.generatePurposeDescriptionWrapper(transaction), + }); + } } static getMemoCodeConstant(transactionType?: TransactionType): boolean | undefined { diff --git a/front-end/src/app/shared/components/transaction-type-base/transaction-memo.utils.ts b/front-end/src/app/shared/components/transaction-type-base/transaction-memo.utils.ts index a5554a4914..9a91536cc3 100644 --- a/front-end/src/app/shared/components/transaction-type-base/transaction-memo.utils.ts +++ b/front-end/src/app/shared/components/transaction-type-base/transaction-memo.utils.ts @@ -1,23 +1,23 @@ import { FormGroup } from '@angular/forms'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { Transaction } from 'app/shared/models/transaction.model'; import { MemoText } from 'app/shared/models/memo-text.model'; export class TransactionMemoUtils { // prettier-ignore - static retrieveMemoText(transactionType: TransactionType, form: FormGroup, formValues: any) { // eslint-disable-line @typescript-eslint/no-explicit-any + static retrieveMemoText(transaction: Transaction, form: FormGroup, formValues: any) { // eslint-disable-line @typescript-eslint/no-explicit-any const text = form.get('memo_text_input')?.value; if (text && text.length > 0) { const memo_text = MemoText.fromJSON({ text4000: text, - report_id: transactionType?.transaction?.report_id, + report_id: transaction?.report_id, rec_type: 'TEXT', - filer_committee_id_number: transactionType?.transaction?.filer_committee_id_number, + filer_committee_id_number: transaction?.filer_committee_id_number, transaction_id_number: '', - back_reference_sched_form_name: transactionType?.transaction?.form_type, + back_reference_sched_form_name: transaction?.form_type, }); - if (transactionType.transaction?.id) { - memo_text.transaction_uuid = transactionType.transaction.id; + if (transaction?.id) { + memo_text.transaction_uuid = transaction.id; } formValues['memo_text'] = memo_text; @@ -28,8 +28,8 @@ export class TransactionMemoUtils { return formValues; } - static patchMemoText(transactionType: TransactionType | undefined, form: FormGroup) { - const memo_text = transactionType?.transaction?.memo_text; + static patchMemoText(transaction: Transaction | undefined, form: FormGroup) { + const memo_text = transaction?.memo_text; if (memo_text?.text4000) { form.patchValue({ memo_text_input: memo_text.text4000 }); } diff --git a/front-end/src/app/shared/components/transaction-type-base/transaction-type-base.component.spec.ts b/front-end/src/app/shared/components/transaction-type-base/transaction-type-base.component.spec.ts index 5ae4e743a9..ee0bf13353 100644 --- a/front-end/src/app/shared/components/transaction-type-base/transaction-type-base.component.spec.ts +++ b/front-end/src/app/shared/components/transaction-type-base/transaction-type-base.component.spec.ts @@ -5,7 +5,6 @@ import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; import { NavigationAction, NavigationDestination, @@ -15,18 +14,14 @@ import { Contact, ContactTypes } from 'app/shared/models/contact.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; import { ApiService } from 'app/shared/services/api.service'; import { TransactionService } from 'app/shared/services/transaction.service'; -import { ValidateService } from 'app/shared/services/validate.service'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; +import { getTestTransactionByType, testMockStore, testIndividualReceipt } from 'app/shared/utils/unit-test.utils'; import { Confirmation, ConfirmationService, Message, MessageService, SelectItem } from 'primeng/api'; import { of } from 'rxjs'; import { TransactionTypeBaseComponent } from './transaction-type-base.component'; -import { TransactionTypeUtils } from '../../utils/transaction-type.utils'; import { SchATransaction, ScheduleATransactionTypes } from '../../models/scha-transaction.model'; import { MemoText } from 'app/shared/models/memo-text.model'; -import { JsonSchema } from 'app/shared/interfaces/json-schema.interface'; import { TransactionMemoUtils } from './transaction-memo.utils'; import { TransactionContactUtils } from './transaction-contact.utils'; -import { TransactionFormUtils } from './transaction-form.utils'; class TestTransactionTypeBaseComponent extends TransactionTypeBaseComponent { formProperties: string[] = [ @@ -61,7 +56,7 @@ const initTransactionData = { form_type: undefined, filer_committee_id_number: undefined, transaction_id: null, - transaction_type_identifier: 'INDIVIDUAL_RECEIPT', + transaction_type_identifier: ScheduleATransactionTypes.INDIVIDUAL_RECEIPT, contribution_purpose_descrip: undefined, parent_transaction_id: undefined, children: undefined, @@ -72,31 +67,7 @@ const initTransactionData = { memo_text_id: 'ID Goes Here', }; -const testTransaction = SchATransaction.fromJSON({ - id: '123', - report_id: '999', - contact: undefined, - contact_id: '333', - form_type: undefined, - filer_committee_id_number: undefined, - transaction_id: null, - transaction_type_identifier: 'INDIVIDUAL_RECEIPT', - aggregation_group: 'GENERAL', - contribution_amount: '202.2', - contribution_date: '2022-02-02', - contribution_purpose_descrip: undefined, - parent_transaction_id: undefined, - children: undefined, - parent_transaction: undefined, - fields_to_validate: undefined, - itemized: false, - memo_text: undefined, - memo_text_id: undefined, -}); - -const testTransactionType = - TransactionTypeUtils.factory(ScheduleATransactionTypes.INDIVIDUAL_RECEIPT) || ({} as TransactionType); -testTransactionType.transaction = testTransactionType?.getNewTransaction(); +let testTransaction: SchATransaction; describe('TransactionTypeBaseComponent', () => { let component: TestTransactionTypeBaseComponent; @@ -116,7 +87,6 @@ describe('TransactionTypeBaseComponent', () => { DatePipe, MessageService, FormBuilder, - ValidateService, TransactionService, ConfirmationService, provideMockStore(testMockStore), @@ -133,8 +103,10 @@ describe('TransactionTypeBaseComponent', () => { }); beforeEach(() => { + testTransaction = testIndividualReceipt; fixture = TestBed.createComponent(TestTransactionTypeBaseComponent); component = fixture.componentInstance; + component.transaction = testTransaction; fixture.detectChanges(); }); @@ -143,39 +115,17 @@ describe('TransactionTypeBaseComponent', () => { }); it('#retrieveMemoText should work', () => { - if (component.transactionType) component.transactionType.transaction = testTransaction; - else - component.transactionType = { - transaction: testTransaction, - scheduleId: 'TEST', - componentGroupId: 'TEST', - isDependentChild: false, - title: 'Title goes here', - getNewTransaction: () => { - return testTransaction; - }, - schema: { - $id: '10101', - $schema: 'string', - type: 'string', - required: ['string'], - properties: {}, - }, - updateParentOnSave: false, - getSchemaName: () => 'foo', - generatePurposeDescriptionWrapper: () => 'bar', - }; - + if (!component.transaction) throw new Error('Fecfile: transaction does not exist'); component.form = new FormGroup({ memo_text_input: new FormControl('memo'), }); - const formValues = TransactionMemoUtils.retrieveMemoText(component.transactionType, component.form, {}); + const formValues = TransactionMemoUtils.retrieveMemoText(component.transaction, component.form, {}); expect(formValues['memo_text']['text4000']).toBe('memo'); }); function addContact(component: TestTransactionTypeBaseComponent, contact: Contact) { - if (component.transactionType?.transaction) { - component.transactionType.transaction.contact = contact; + if (component.transaction) { + component.transaction.contact = contact; } } @@ -198,7 +148,7 @@ describe('TransactionTypeBaseComponent', () => { testContact.zip = '12345'; spyOn(testApiService, 'post').and.returnValue(of(testContact)); - spyOn(testTransactionService, 'create').and.returnValue(of(testTransaction1)); + spyOn(testTransactionService, 'update').and.returnValue(of(testTransaction1)); const confirmSpy = spyOn(testConfirmationService, 'confirm'); // test reject confirmSpy.and.callFake((confirmation: Confirmation) => { @@ -208,7 +158,7 @@ describe('TransactionTypeBaseComponent', () => { }); const componentNavigateToSpy = spyOn(component, 'navigateTo'); - component.transactionType = testTransactionType; + component.transaction = testTransaction; addContact(component, testContact); const listSaveEvent = new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, testTransaction1); @@ -224,20 +174,28 @@ describe('TransactionTypeBaseComponent', () => { const testContact2 = new Contact(); testContact2.type = ContactTypes.INDIVIDUAL; testContact2.id = 'testId'; - if (component.transactionType.transaction) { - component.transactionType.transaction.contact = testContact2; + if (component.transaction) { + component.transaction.contact = testContact2; } component.save(listSaveEvent); - if (component.transactionType.transaction) { - component.transactionType.transaction.contact = undefined; + if (component.transaction) { + component.transaction.contact = undefined; + } + if (testTransaction.transactionType) { + TransactionContactUtils.getEditTransactionContactConfirmationMessage( + [], + testContact, + component.form, + fecDatePipe, + testTransaction.transactionType?.templateMap + ); } - TransactionContactUtils.getEditTransactionContactConfirmationMessage([], testContact, component.form, fecDatePipe); expect(componentNavigateToSpy).toHaveBeenCalledTimes(3); }); function spyOnServices(contact: Contact, transaction: SchATransaction) { spyOn(testApiService, 'post').and.returnValue(of(contact)); - spyOn(testTransactionService, 'create').and.returnValue(of(transaction)); + spyOn(testTransactionService, 'update').and.returnValue(of(transaction)); spyOn(testConfirmationService, 'confirm').and.callFake((confirmation: Confirmation) => { if (confirmation.accept) { return confirmation.accept(); @@ -256,7 +214,7 @@ describe('TransactionTypeBaseComponent', () => { spyOnServices(testContact, testTransaction1); const componentNavigateToSpy = spyOn(component, 'navigateTo'); - component.transactionType = testTransactionType; + component.transaction = testTransaction; addContact(component, testContact); const listSaveEvent = new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, testTransaction1); @@ -267,8 +225,8 @@ describe('TransactionTypeBaseComponent', () => { const testContact2 = new Contact(); testContact2.type = ContactTypes.COMMITTEE; testContact2.id = 'testId'; - if (component.transactionType.transaction) { - component.transactionType.transaction.contact = testContact2; + if (component.transaction) { + component.transaction.contact = testContact2; } component.save(listSaveEvent); expect(componentNavigateToSpy).toHaveBeenCalledTimes(3); @@ -284,7 +242,7 @@ describe('TransactionTypeBaseComponent', () => { spyOnServices(orgContact, testTransaction1); const componentNavigateToSpy = spyOn(component, 'navigateTo'); - component.transactionType = testTransactionType; + component.transaction = testTransaction; addContact(component, orgContact); const listSaveEvent = new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, testTransaction1); @@ -294,8 +252,8 @@ describe('TransactionTypeBaseComponent', () => { const orgContact2 = new Contact(); orgContact2.type = ContactTypes.ORGANIZATION; orgContact2.id = 'testId'; - if (component.transactionType.transaction) { - component.transactionType.transaction.contact = orgContact2; + if (component.transaction) { + component.transaction.contact = orgContact2; } component.save(listSaveEvent); expect(componentNavigateToSpy).toHaveBeenCalledTimes(3); @@ -309,7 +267,7 @@ describe('TransactionTypeBaseComponent', () => { spyOnServices(testContact, testTransaction1); const componentNavigateToSpy = spyOn(component, 'navigateTo'); - component.transactionType = testTransactionType; + component.transaction = testTransaction; component.save(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, testTransaction1)); expect(componentNavigateToSpy).toHaveBeenCalledTimes(1); @@ -330,9 +288,7 @@ describe('TransactionTypeBaseComponent', () => { }); const componentNavigateToSpy = spyOn(component, 'navigateTo'); - component.transactionType = - TransactionTypeUtils.factory(ScheduleATransactionTypes.INDIVIDUAL_RECEIPT) || ({} as TransactionType); - component.transactionType.transaction = testTransaction; + component.transaction = testTransaction; component.save(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, testTransaction2)); tick(1000); @@ -355,12 +311,8 @@ describe('TransactionTypeBaseComponent', () => { it('#navigateTo NavigationDestination.CHILD should show popup + navigate', () => { const testTransactionId = '123'; const testTransactionTypeToAdd = ScheduleATransactionTypes.INDIVIDUAL_RECEIPT; - - component.transactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.INDIVIDUAL_RECEIPT); - if (component.transactionType) { - component.transactionType.transaction = testTransaction; - component.transactionType.transaction.report_id = '999'; - } + component.transaction = testTransaction; + component.transaction.report_id = '999'; const expectedMessage: Message = { severity: 'success', @@ -385,18 +337,6 @@ describe('TransactionTypeBaseComponent', () => { testTransaction3.id = '123'; testTransaction3.report_id = '99'; testTransaction3.contact_id = '33'; - component.transactionType = { - scheduleId: 'A', - componentGroupId: 'A', - isDependentChild: false, - title: '', - schema: { properties: {} } as JsonSchema, - getNewTransaction: () => SchATransaction.fromJSON({}), - transaction: testTransaction3, - updateParentOnSave: false, - getSchemaName: () => 'foo', - generatePurposeDescriptionWrapper: () => 'bar', - } as TransactionType; const expectedRoute = `/transactions/report/${testTransaction3.report_id}/list`; const routerNavigateByUrlSpy = spyOn(testRouter, 'navigateByUrl'); component.navigateTo(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, testTransaction3)); @@ -404,10 +344,7 @@ describe('TransactionTypeBaseComponent', () => { }); it('#navigateTo NavigationDestination.CHILD should navigate', () => { - component.transactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.INDIVIDUAL_RECEIPT); - if (component.transactionType) { - component.transactionType.transaction = testTransaction; - } + component.transaction = testTransaction; const expectedRoute = '/transactions/report/999/list/edit/123/create-sub-transaction/INDIVIDUAL_RECEIPT'; const routerNavigateByUrlSpy = spyOn(testRouter, 'navigateByUrl'); component.navigateTo( @@ -424,18 +361,6 @@ describe('TransactionTypeBaseComponent', () => { it('#navigateTo NavigationDestination.PARENT should navigate', () => { const transaction = { ...testTransaction } as SchATransaction; transaction.parent_transaction_id = '333'; - component.transactionType = { - scheduleId: 'A', - componentGroupId: 'A', - isDependentChild: false, - title: '', - schema: { properties: {} } as JsonSchema, - getNewTransaction: () => SchATransaction.fromJSON({}), - transaction: transaction, - updateParentOnSave: false, - getSchemaName: () => 'foo', - generatePurposeDescriptionWrapper: () => 'bar', - } as TransactionType; const expectedRoute = '/transactions/report/999/list/edit/333'; const routerNavigateByUrlSpy = spyOn(testRouter, 'navigateByUrl'); component.navigateTo(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.PARENT, transaction)); @@ -443,10 +368,7 @@ describe('TransactionTypeBaseComponent', () => { }); it('#navigateTo default should navigate', () => { - component.transactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.INDIVIDUAL_RECEIPT); - if (component.transactionType) { - component.transactionType.transaction = testTransaction; - } + component.transaction = testTransaction; const expectedRoute = '/transactions/report/999/list'; const routerNavigateByUrlSpy = spyOn(testRouter, 'navigateByUrl'); component.navigateTo(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, testTransaction)); @@ -574,17 +496,7 @@ describe('TransactionTypeBaseComponent', () => { }); it('#onContactLookupSelect INDIVIDUAL should calculate aggregate', () => { - component.transactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.INDIVIDUAL_RECEIPT); - component.transactionType.transaction = component.transactionType.getNewTransaction(); - TransactionFormUtils.onInit( - component, - component.form, - new ValidateService(), - component.transactionType, - component.contactId$ - ); - component.transactionType.transaction = component.transactionType.getNewTransaction(); - + component.transaction = testTransaction; const testEntityType = ContactTypes.INDIVIDUAL; const testContact = new Contact(); @@ -671,33 +583,8 @@ describe('TransactionTypeBaseComponent', () => { }); it('positive contribution_amount values should be overriden when the schema requires a negative value', () => { - component.transactionType = { - transaction: testTransaction, - scheduleId: 'TEST', - componentGroupId: 'TEST', - isDependentChild: false, - title: 'Title goes here', - getNewTransaction: () => { - return testTransaction; - }, - schema: { - $id: '10101', - $schema: 'string', - type: 'string', - required: [], - properties: { - contribution_amount: { - type: 'number', - exclusiveMaximum: 0, - }, - }, - }, - updateParentOnSave: false, - getSchemaName: () => 'foo', - generatePurposeDescriptionWrapper: () => 'bar', - }; - - component.parentOnInit(); + component.transaction = getTestTransactionByType(ScheduleATransactionTypes.RETURNED_BOUNCED_RECEIPT_INDIVIDUAL); + component.ngOnInit(); component.form.patchValue({ contribution_amount: 2 }); expect(component.form.value.contribution_amount).toBe(-2); }); diff --git a/front-end/src/app/shared/components/transaction-type-base/transaction-type-base.component.ts b/front-end/src/app/shared/components/transaction-type-base/transaction-type-base.component.ts index b4fc5303b1..0915af9896 100644 --- a/front-end/src/app/shared/components/transaction-type-base/transaction-type-base.component.ts +++ b/front-end/src/app/shared/components/transaction-type-base/transaction-type-base.component.ts @@ -1,22 +1,20 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { Router } from '@angular/router'; -import { SchATransaction, ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; import { NavigationAction, NavigationControl, NavigationDestination, NavigationEvent, - TransactionNavigationControls, + TransactionNavigationControls } from 'app/shared/models/transaction-navigation-controls.model'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; -import { Transaction } from 'app/shared/models/transaction.model'; +import { TransactionTemplateMapType, TransactionType } from 'app/shared/models/transaction-type.model'; +import { ScheduleTransaction, Transaction, TransactionTypes } from 'app/shared/models/transaction.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; import { ContactService } from 'app/shared/services/contact.service'; import { TransactionService } from 'app/shared/services/transaction.service'; -import { ValidateService } from 'app/shared/services/validate.service'; import { LabelUtils, PrimeOptions } from 'app/shared/utils/label.utils'; -import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; +import { ValidateUtils } from 'app/shared/utils/validate.utils'; import { ConfirmationService, MessageService, SelectItem } from 'primeng/api'; import { BehaviorSubject, Subject, takeUntil } from 'rxjs'; import { Contact, ContactTypeLabels, ContactTypes } from '../../models/contact.model'; @@ -27,7 +25,7 @@ import { TransactionFormUtils } from './transaction-form.utils'; template: '', }) export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy { - @Input() transactionType: TransactionType | undefined; + @Input() transaction: Transaction | undefined; abstract formProperties: string[]; ContactTypes = ContactTypes; @@ -36,10 +34,8 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy destroy$: Subject = new Subject(); contactId$: Subject = new BehaviorSubject(''); formSubmitted = false; - memoItemHelpText = 'The dollar amount in a memo item is not incorporated into the total figure for the schedule.'; - contributionPurposeDescriptionLabel = ''; - childTransactionOptions: { [key: string]: string | ScheduleATransactionTypes }[] = []; - negativeAmountValueOnly = false; + purposeDescriptionLabel = ''; + templateMap: TransactionTemplateMapType = {} as TransactionTemplateMapType; form: FormGroup = this.fb.group({}); @@ -47,40 +43,46 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy protected messageService: MessageService, public transactionService: TransactionService, protected contactService: ContactService, - protected validateService: ValidateService, protected confirmationService: ConfirmationService, protected fb: FormBuilder, protected router: Router, protected fecDatePipe: FecDatePipe - ) {} + ) { } ngOnInit(): void { - this.form = this.fb.group(this.validateService.getFormGroupFields(this.formProperties)); - TransactionFormUtils.onInit(this, this.form, this.validateService, this.transactionType, this.contactId$); + this.form = this.fb.group(ValidateUtils.getFormGroupFields(this.formProperties)); + if (this.transaction?.transactionType?.templateMap) { + this.templateMap = this.transaction.transactionType.templateMap; + } else { + throw new Error('Fecfile: Template map not found for transaction component'); + } + TransactionFormUtils.onInit(this, this.form, this.transaction, this.contactId$); this.parentOnInit(); } parentOnInit() { // Override contact type options if present in transactionType - if (this.transactionType && this.transactionType.contactTypeOptions) { - this.contactTypeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, this.transactionType.contactTypeOptions); + if (this.transaction?.transactionType && this.transaction?.transactionType.contactTypeOptions) { + this.contactTypeOptions = LabelUtils.getPrimeOptions( + ContactTypeLabels, + this.transaction.transactionType.contactTypeOptions + ); } - const contribution_amount_schema = this.transactionType?.schema.properties['contribution_amount']; - if (contribution_amount_schema?.exclusiveMaximum === 0) { - this.negativeAmountValueOnly = true; + // Determine if amount should always be negative and then force it to be so if needed + if (this.transaction?.transactionType?.negativeAmountValueOnly && this.templateMap?.amount) { this.form - .get('contribution_amount') + .get(this.templateMap.amount) ?.valueChanges.pipe(takeUntil(this.destroy$)) - .subscribe((contribution_amount) => { - if (typeof contribution_amount === 'number' && contribution_amount > 0) { - this.form.patchValue({ contribution_amount: -1 * contribution_amount }); + .subscribe((amount) => { + if (+amount > 0) { + this.form.patchValue({ [this.templateMap.amount]: -1 * amount }); } }); } - if (this.transactionType?.generatePurposeDescriptionLabel) { - this.contributionPurposeDescriptionLabel = this.transactionType.generatePurposeDescriptionLabel(); + if (this.transaction?.transactionType?.generatePurposeDescriptionLabel) { + this.purposeDescriptionLabel = this.transaction?.transactionType.generatePurposeDescriptionLabel(); } } @@ -98,8 +100,7 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy } const payload: Transaction = TransactionFormUtils.getPayloadTransaction( - this.transactionType, - this.validateService, + this.transaction, this.form, this.formProperties ); @@ -115,17 +116,23 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy payload: Transaction, targetDialog: 'dialog' | 'childDialog' = 'dialog' ) { - if (confirmTransaction.contact_id && confirmTransaction.contact) { + if ( + confirmTransaction.contact_id && + confirmTransaction.contact && + confirmTransaction?.transactionType?.templateMap + ) { const transactionContactChanges = TransactionContactUtils.setTransactionContactFormChanges( form, - confirmTransaction.contact + confirmTransaction.contact, + confirmTransaction.transactionType.templateMap ); if (transactionContactChanges?.length) { const confirmationMessage = TransactionContactUtils.getEditTransactionContactConfirmationMessage( transactionContactChanges, confirmTransaction.contact, form, - this.fecDatePipe + this.fecDatePipe, + confirmTransaction.transactionType.templateMap ); this.confirmationService.confirm({ key: targetDialog, @@ -145,24 +152,29 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy acceptCallback.call(this, navigationEvent, payload); } } else { - const confirmationMessage = TransactionContactUtils.getCreateTransactionContactConfirmationMessage( - (confirmTransaction as SchATransaction).entity_type as ContactTypes, - form - ); - this.confirmationService.confirm({ - key: targetDialog, - header: 'Confirm', - icon: 'pi pi-info-circle', - message: confirmationMessage, - acceptLabel: 'Continue', - rejectLabel: 'Cancel', - accept: () => { - acceptCallback.call(this, navigationEvent, payload); - }, - reject: () => { - return; - }, - }); + if (confirmTransaction?.transactionType?.templateMap) { + const confirmationMessage = TransactionContactUtils.getCreateTransactionContactConfirmationMessage( + (confirmTransaction as ScheduleTransaction).entity_type as ContactTypes, + form, + confirmTransaction.transactionType.templateMap + ); + this.confirmationService.confirm({ + key: targetDialog, + header: 'Confirm', + icon: 'pi pi-info-circle', + message: confirmationMessage, + acceptLabel: 'Continue', + rejectLabel: 'Cancel', + accept: () => { + acceptCallback.call(this, navigationEvent, payload); + }, + reject: () => { + return; + }, + }); + } else { + throw new Error('Fecfile: Cannot find template map when confirming transaction'); + } } } @@ -174,19 +186,22 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy // to create a grandchild transaction because we won't know which child transaction of the grandparent // was the original transaction it's id was generated on the api. the middle child's // id is necessary to generate the url for creating the grandchild. - const transactionType = TransactionTypeUtils.factory(payload.transaction_type_identifier); - if (transactionType.updateParentOnSave) { + if (payload.transactionType?.updateParentOnSave) { payload = payload.getUpdatedParent(); } if (payload.id) { this.transactionService.update(payload).subscribe((transaction) => { - navigationEvent.transaction = !transactionType.updateParentOnSave ? transaction : originalTransaction; + navigationEvent.transaction = originalTransaction.transactionType?.updateParentOnSave + ? originalTransaction + : transaction; this.navigateTo(navigationEvent); }); } else { this.transactionService.create(payload).subscribe((transaction) => { - navigationEvent.transaction = !transactionType.updateParentOnSave ? transaction : originalTransaction; + navigationEvent.transaction = originalTransaction.transactionType?.updateParentOnSave + ? originalTransaction + : transaction; this.navigateTo(navigationEvent); }); } @@ -194,11 +209,11 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy } getNavigationControls(): TransactionNavigationControls { - return this.transactionType?.navigationControls || new TransactionNavigationControls([], [], []); + return this.transaction?.transactionType?.navigationControls || new TransactionNavigationControls([], [], []); } getInlineControls(): NavigationControl[] { - return this.getNavigationControls().getNavigationControls('inline', this.transactionType?.transaction); + return this.getNavigationControls().getNavigationControls('inline', this.transaction); } handleNavigate(navigationEvent: NavigationEvent): void { @@ -238,7 +253,7 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy resetForm() { this.formSubmitted = false; - TransactionFormUtils.resetForm(this.form, this.transactionType, this.contactTypeOptions); + TransactionFormUtils.resetForm(this.form, this.transaction, this.contactTypeOptions); } isMemoCodeReadOnly(transactionType?: TransactionType): boolean { @@ -252,22 +267,15 @@ export abstract class TransactionTypeBaseComponent implements OnInit, OnDestroy } onContactLookupSelect(selectItem: SelectItem) { - TransactionContactUtils.onContactLookupSelect(selectItem, this.form, this.transactionType, this.contactId$); + TransactionContactUtils.onContactLookupSelect(selectItem, this.form, this.transaction, this.contactId$); } getEntityType(): string { return this.form.get('entity_type')?.value || ''; } - createSubTransaction(event: { value: ScheduleATransactionTypes }) { - this.save( - new NavigationEvent( - NavigationAction.SAVE, - NavigationDestination.CHILD, - this.transactionType?.transaction, - event.value - ) - ); + createSubTransaction(event: { value: TransactionTypes }) { + this.save(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.CHILD, this.transaction, event.value)); this.form.get('subTransaction')?.reset(); // If the save fails, this clears the dropdown } } diff --git a/front-end/src/app/shared/models/candidate.model.spec.ts b/front-end/src/app/shared/models/candidate.model.spec.ts new file mode 100644 index 0000000000..40ec8d805f --- /dev/null +++ b/front-end/src/app/shared/models/candidate.model.spec.ts @@ -0,0 +1,7 @@ +import { Candidate } from './candidate.model'; + +describe('Candidate', () => { + it('should create an instance', () => { + expect(new Candidate()).toBeTruthy(); + }); +}); diff --git a/front-end/src/app/shared/models/candidate.model.ts b/front-end/src/app/shared/models/candidate.model.ts new file mode 100644 index 0000000000..59e9b5d9e0 --- /dev/null +++ b/front-end/src/app/shared/models/candidate.model.ts @@ -0,0 +1,39 @@ +import { plainToClass } from 'class-transformer'; +import { BaseModel } from './base.model'; + +export class Candidate extends BaseModel { + active_through: number | undefined; + address_city: string | undefined; + address_state: string | undefined; + address_street_1: string | undefined; + address_street_2: string | undefined; + address_zip: string | undefined; + candidate_id: string | undefined; + candidate_inactive: boolean | undefined; + candidate_status: string | undefined; + cycles: number[] = []; + district: string | undefined; + district_number: number | undefined; + election_districts: string[] = []; + election_years: number[] = []; + federal_funds_flag: boolean | undefined; + first_file_date: string | undefined; + flags: string | undefined; + has_raised_funds: boolean | undefined; + incumbent_challenge: string | undefined; + incumbent_challenge_full: string | undefined; + last_f2_date: string | undefined; + last_file_date: string | undefined; + load_date: string | undefined; + name: string | undefined; + office: string | undefined; + office_full: string | undefined; + party: string | undefined; + party_full: string | undefined; + state: string | undefined; + + // prettier-ignore + static fromJSON(json: any): Candidate { // eslint-disable-line @typescript-eslint/no-explicit-any + return plainToClass(Candidate, json); + } +} diff --git a/front-end/src/app/shared/models/contact.model.ts b/front-end/src/app/shared/models/contact.model.ts index 92425a3982..8c560aeb45 100644 --- a/front-end/src/app/shared/models/contact.model.ts +++ b/front-end/src/app/shared/models/contact.model.ts @@ -98,7 +98,69 @@ export class Contact extends BaseModel { } } -export class FecApiLookupData {} +export class FecApiLookupData { } + +export class FecApiCandidateLookupData extends FecApiLookupData { + id: string | undefined; + office_sought: string | undefined; + name: string | undefined; + + constructor(data: FecApiCandidateLookupData) { + super(); + Object.assign(this, data); + } + + toSelectItem(): SelectItem { + return { + label: `${this.name} (${this.id})`, + value: this, + }; + } +} + +export class FecfileCandidateLookupData extends Contact { + constructor(data: FecfileCandidateLookupData) { + super(); + Object.assign(this, data); + } + + toSelectItem(): SelectItem { + return { + label: `${this.name} (${this.candidate_id})`, + value: this, + }; + } +} + +export class CandidateLookupResponse { + fec_api_candidates?: FecApiCandidateLookupData[]; + fecfile_candidates?: FecfileCandidateLookupData[]; + + // prettier-ignore + static fromJSON(json: any): CandidateLookupResponse { // eslint-disable-line @typescript-eslint/no-explicit-any + return plainToClass(CandidateLookupResponse, json); + } + + toSelectItemGroups(includeFecfileResults: boolean): SelectItemGroup[] { + const fecApiSelectItems = + this.fec_api_candidates?.map((data) => new FecApiCandidateLookupData(data).toSelectItem()) || []; + const fecfileSelectItems = + this.fecfile_candidates?.map((data) => new FecfileCandidateLookupData(data).toSelectItem()) || []; + return [ + ...includeFecfileResults ? [{ + label: fecfileSelectItems.length ? 'Select an existing candidate contact:' : 'There are no matching candidates', + items: fecfileSelectItems, + }] : [], + { + label: fecApiSelectItems.length + ? 'Create a new contact from list of registered candidates:' + : 'There are no matching registered candidates', + items: fecApiSelectItems, + }, + ]; + } +} + export class FecApiCommitteeLookupData extends FecApiLookupData { id: string | undefined; is_active: boolean | undefined; @@ -140,16 +202,16 @@ export class CommitteeLookupResponse { return plainToClass(CommitteeLookupResponse, json); } - toSelectItemGroups(): SelectItemGroup[] { + toSelectItemGroups(includeFecfileResults: boolean): SelectItemGroup[] { const fecApiSelectItems = this.fec_api_committees?.map((data) => new FecApiCommitteeLookupData(data).toSelectItem()) || []; const fecfileSelectItems = this.fecfile_committees?.map((data) => new FecfileCommitteeLookupData(data).toSelectItem()) || []; return [ - { + ...includeFecfileResults ? [{ label: fecfileSelectItems.length ? 'Select an existing committee contact:' : 'There are no matching committees', items: fecfileSelectItems, - }, + }] : [], { label: fecApiSelectItems.length ? 'Create a new contact from list of registered committees:' diff --git a/front-end/src/app/shared/models/fec-api.model.ts b/front-end/src/app/shared/models/fec-api.model.ts index 904ac1cc8c..cc411935c8 100644 --- a/front-end/src/app/shared/models/fec-api.model.ts +++ b/front-end/src/app/shared/models/fec-api.model.ts @@ -1,3 +1,4 @@ +import { Candidate } from './candidate.model'; import { CommitteeAccount } from './committee-account.model'; import { FecFiling } from './fec-filing.model'; @@ -11,5 +12,5 @@ export type FecApiPagination = { export type FecApiPaginatedResponse = { api_version: string; pagination: FecApiPagination; - results: CommitteeAccount[] | FecFiling[]; + results: Candidate[] | CommitteeAccount[] | FecFiling[]; }; diff --git a/front-end/src/app/shared/models/scha-transaction-type.model.ts b/front-end/src/app/shared/models/scha-transaction-type.model.ts new file mode 100644 index 0000000000..78b0c331a5 --- /dev/null +++ b/front-end/src/app/shared/models/scha-transaction-type.model.ts @@ -0,0 +1,32 @@ +import { TransactionType, TransactionTemplateMapType } from './transaction-type.model'; + +export abstract class SchATransactionType extends TransactionType { + scheduleId = 'A'; + + // Mapping of schedule fields to the group input component form templates + templateMap: TransactionTemplateMapType = { + last_name: 'contributor_last_name', + first_name: 'contributor_first_name', + middle_name: 'contributor_middle_name', + prefix: 'contributor_prefix', + suffix: 'contributor_suffix', + street_1: 'contributor_street_1', + street_2: 'contributor_street_2', + city: 'contributor_city', + state: 'contributor_state', + zip: 'contributor_zip', + employer: 'contributor_employer', + occupation: 'contributor_occupation', + organization_name: 'contributor_organization_name', + committee_fec_id: 'donor_committee_fec_id', + date: 'contribution_date', + dateLabel: 'DATE RECEIVED', + memo_code: 'memo_code', + amount: 'contribution_amount', + aggregate: 'contribution_aggregate', + purpose_description: 'contribution_purpose_descrip', + purposeDescripLabel: 'PURPOSE OF RECEIPT', + memo_text_input: 'memo_text_input', + category_code: '', + }; +} diff --git a/front-end/src/app/shared/models/scha-transaction.model.spec.ts b/front-end/src/app/shared/models/scha-transaction.model.spec.ts index 927261b064..61530d6a36 100644 --- a/front-end/src/app/shared/models/scha-transaction.model.spec.ts +++ b/front-end/src/app/shared/models/scha-transaction.model.spec.ts @@ -1,25 +1,6 @@ -import { MemoText } from './memo-text.model'; +import { getTestTransactionByType } from '../utils/unit-test.utils'; import { SchATransaction, ScheduleATransactionTypes } from './scha-transaction.model'; -const initTransactionData = { - id: undefined, - report_id: undefined, - contact: undefined, - contact_id: undefined, - form_type: undefined, - filer_committee_id_number: undefined, - transaction_id: null, - transaction_type_identifier: 'INDIVIDUAL_RECEIPT', - contribution_purpose_descrip: undefined, - parent_transaction_id: undefined, - children: undefined, - parent_transaction: undefined, - fields_to_validate: undefined, - itemized: false, - memo_text: MemoText.fromJSON({ text4000: 'Memo!' }), - memo_text_id: 'ID Goes Here', -}; - describe('SchATransaction', () => { it('should create an instance', () => { expect(new SchATransaction()).toBeTruthy(); @@ -42,17 +23,15 @@ describe('SchATransaction', () => { }); it('Updates the purpose description of a child transaction', () => { - const testTransaction1 = SchATransaction.fromJSON(initTransactionData); - const testTransaction2 = SchATransaction.fromJSON(initTransactionData); - - testTransaction2.transaction_type_identifier = - ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO; - testTransaction2.parent_transaction = testTransaction1; - testTransaction1.contributor_organization_name = 'Test Committee'; - testTransaction1.children = [testTransaction2]; - - const updatedChildren = testTransaction1.updateChildren(); - const child = updatedChildren[0] as SchATransaction; - expect(child.contribution_purpose_descrip).toContain(testTransaction1.contributor_organization_name); + const parentTransaction = getTestTransactionByType( + ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT + ) as SchATransaction; + const childTransaction = getTestTransactionByType( + ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO + ) as SchATransaction; + parentTransaction.children = [childTransaction]; + parentTransaction.contributor_organization_name = 'Test Committee'; + parentTransaction.updateChildren(); + expect(childTransaction.contribution_purpose_descrip).toContain('Test Committee'); }); }); diff --git a/front-end/src/app/shared/models/scha-transaction.model.ts b/front-end/src/app/shared/models/scha-transaction.model.ts index 10ea5efafa..12def39b59 100644 --- a/front-end/src/app/shared/models/scha-transaction.model.ts +++ b/front-end/src/app/shared/models/scha-transaction.model.ts @@ -1,5 +1,5 @@ import { plainToClass, Transform } from 'class-transformer'; -import { Transaction } from './transaction.model'; +import { Transaction, AggregationGroups } from './transaction.model'; import { LabelList } from '../utils/label.utils'; import { BaseModel } from './base.model'; import { TransactionTypeUtils } from '../utils/transaction-type.utils'; @@ -67,9 +67,6 @@ export class SchATransaction extends Transaction { const transactionType = TransactionTypeUtils.factory(transaction.transaction_type_identifier); transaction.setMetaProperties(transactionType); } - // else { - // throw new Error("Can't find transaction_type_identifier when creating class from JSON"); - // } if (depth > 0 && transaction.parent_transaction) { transaction.parent_transaction = SchATransaction.fromJSON(transaction.parent_transaction, depth-1); } @@ -78,84 +75,6 @@ export class SchATransaction extends Transaction { } return transaction; } - - /** - * updateChildren() - * @returns - * An array of Transaction objects whose contribution_purpose_descriptions - * have been re-generated to account for changes to their parent - * - */ - updateChildren(): Transaction[] { - const outChildren: Transaction[] = []; - - if (this.children) { - /* We treat the parent's children as SchATransaction objects in order - to access fields exclusive to the SchATransaction model */ - for (const child of this.children as SchATransaction[]) { - if (child.transaction_type_identifier) { - // Instantiate a TransactionType object in order to access the purpose description generator - const transactionType = TransactionTypeUtils.factory(child.transaction_type_identifier); - - // Prep the TransactionType by setting fields it will need when generating a purpose description - transactionType.transaction = child; - - /* Make a new object to represent the parent within the TransactionType - because setting the parent equal to the this causes an infinite loop */ - if (transactionType.transaction.parent_transaction) - transactionType.transaction.parent_transaction = { - id: this.id, - contributor_organization_name: this.contributor_organization_name, - } as SchATransaction; - - // Modify the purpose description this to reflect the changes to child transactions - if (transactionType.generatePurposeDescription) { - const newDescrip = transactionType.generatePurposeDescriptionWrapper(); - child.contribution_purpose_descrip = newDescrip; - } - } - - // Always add the child into the array or else it will be lost - outChildren.push(child); - } - } - - return outChildren; - } - - /** - * Returns a transaction payload with the parent of the original payload - * swapped in as the main payload and the original main payload is a child - * @returns - */ - getUpdatedParent(childDeleted = false): SchATransaction { - if (!this.parent_transaction?.transaction_type_identifier) { - throw new Error( - `Child transaction '${this.transaction_type_identifier}' is missing its parent when saving to API` - ); - } - - // The parent is the new payload - const payload = this.parent_transaction as SchATransaction; - - // Attach the original payload to the parent as a child, replacing an - // existing version if needed - if (this.id && this.parent_transaction) { - payload.children = this.parent_transaction.children?.filter((c) => c.id !== this.id); - } - if (!childDeleted) { - payload.children?.push(this); - } - payload.children = payload.updateChildren(); - - // Update the CPD - if (payload?.transactionType?.generatePurposeDescription) { - payload.transactionType.transaction = payload; - payload.contribution_purpose_descrip = payload.transactionType.generatePurposeDescriptionWrapper(); - } - - return payload; - } } export enum ScheduleATransactionGroups { @@ -233,6 +152,9 @@ export enum ScheduleATransactionTypes { EARMARK_RECEIPT_FOR_CONVENTION_ACCOUNT_CONTRIBUTION = 'EARMARK_RECEIPT_CONVENTION_ACCOUNT', EARMARK_RECEIPT_FOR_HEADQUARTERS_ACCOUNT_CONTRIBUTION = 'EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT', PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT = 'PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT', + PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT = 'PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT', + PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT = 'PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT', + PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT = 'PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT', // Child transactiion types PAC_EARMARK_MEMO = 'PAC_EARMARK_MEMO', EARMARK_MEMO = 'EARMARK_MEMO', @@ -252,7 +174,10 @@ export enum ScheduleATransactionTypes { TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO = 'TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO', PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO = 'PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO', PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO = 'PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO', + PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO = 'PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO', + PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO = 'PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO', PARTNERSHIP_MEMO = 'PARTNERSHIP_MEMO', + PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO = 'PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO', EARMARK_MEMO_HEADQUARTERS_ACCOUNT = 'EARMARK_MEMO_HEADQUARTERS_ACCOUNT', EARMARK_MEMO_CONVENTION_ACCOUNT = 'EARMARK_MEMO_CONVENTION_ACCOUNT', EARMARK_MEMO_RECOUNT_ACCOUNT = 'EARMARK_MEMO_RECOUNT_ACCOUNT', @@ -427,6 +352,15 @@ export const ScheduleATransactionTypeLabels: LabelList = [ ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT, 'Partnership National Party Recount/Legal Proceedings Account', ], + [ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT, 'Partnership Recount Account Receipt'], + [ + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT, + 'Partnership National Party Headquarters Buildings Account', + ], + [ + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT, + 'Partnership National Party Pres. Nominating Convention Account', + ], [ ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO, 'Individual National Party Recount/Legal Proceedings Account JF Transfer Memo', @@ -459,17 +393,14 @@ export const ScheduleATransactionTypeLabels: LabelList = [ ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO, 'Partnership National Party Recount/Legal Proceedings Account Memo', ], + [ + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO, + 'Partnership National Party Pres. Nominating Convention Account Memo', + ], + [ + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO, + 'Partnership National Party Headquarters Buildings Account Memo', + ], [ScheduleATransactionTypes.PARTNERSHIP_MEMO, 'Partnership Memo'], + [ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO, 'Partnership Recount Account Receipt Memo'], ]; - -export enum AggregationGroups { - GENERAL = 'GENERAL', - LINE_15 = 'LINE_15', - LINE_16 = 'LINE_16', - NATIONAL_PARTY_CONVENTION_ACCOUNT = 'NATIONAL_PARTY_CONVENTION_ACCOUNT', - NATIONAL_PARTY_HEADQUARTERS_ACCOUNT = 'NATIONAL_PARTY_HEADQUARTERS_ACCOUNT', - NATIONAL_PARTY_RECOUNT_ACCOUNT = 'NATIONAL_PARTY_RECOUNT_ACCOUNT', - NON_CONTRIBUTION_ACCOUNT = 'NON_CONTRIBUTION_ACCOUNT', - OTHER_RECEIPTS = 'OTHER_RECEIPTS', - RECOUNT_ACCOUNT = 'RECOUNT_ACCOUNT', -} diff --git a/front-end/src/app/shared/models/schb-transaction-type.model.ts b/front-end/src/app/shared/models/schb-transaction-type.model.ts new file mode 100644 index 0000000000..c254c2f742 --- /dev/null +++ b/front-end/src/app/shared/models/schb-transaction-type.model.ts @@ -0,0 +1,32 @@ +import { TransactionType, TransactionTemplateMapType } from './transaction-type.model'; + +export abstract class SchBTransactionType extends TransactionType { + scheduleId = 'B'; + + // Mapping of schedule fields to the group input component form templates + templateMap: TransactionTemplateMapType = { + last_name: 'payee_last_name', + first_name: 'payee_first_name', + middle_name: 'payee_middle_name', + prefix: 'payee_prefix', + suffix: 'payee_suffix', + street_1: 'payee_street_1', + street_2: 'payee_street_2', + city: 'payee_city', + state: 'payee_state', + zip: 'payee_zip', + employer: '', + occupation: '', + organization_name: 'payee_organization_name', + committee_fec_id: 'beneficiary_committee_fec_id', + date: 'expenditure_date', + dateLabel: 'DATE', + memo_code: 'memo_code', + amount: 'expenditure_amount', + aggregate: 'aggregate_amount', + purpose_description: 'expenditure_purpose_descrip', + purposeDescripLabel: 'PURPOSE OF DISBURSEMENT', + memo_text_input: 'memo_text_input', + category_code: 'category_code', + }; +} diff --git a/front-end/src/app/shared/models/schb-transaction.model.ts b/front-end/src/app/shared/models/schb-transaction.model.ts index 09e9c05eec..bbd3c32b6c 100644 --- a/front-end/src/app/shared/models/schb-transaction.model.ts +++ b/front-end/src/app/shared/models/schb-transaction.model.ts @@ -1,5 +1,5 @@ import { plainToClass, Transform } from 'class-transformer'; -import { Transaction } from './transaction.model'; +import { Transaction, AggregationGroups } from './transaction.model'; import { LabelList } from '../utils/label.utils'; import { BaseModel } from './base.model'; import { TransactionTypeUtils } from '../utils/transaction-type.utils'; @@ -23,6 +23,8 @@ export class SchBTransaction extends Transaction { election_other_description: string | undefined; @Transform(BaseModel.dateTransform) expenditure_date: Date | undefined; expenditure_amount: number | undefined; + aggregate_amount: number | undefined; + aggregation_group: AggregationGroups | undefined; semi_annual_refunded_bundled_amt: number | undefined; expenditure_purpose_descrip: string | undefined; category_code: string | undefined; @@ -47,7 +49,7 @@ export class SchBTransaction extends Transaction { memo_text_description: string | undefined; reference_to_si_or_sl_system_code_that_identifies_the_account: string | undefined; - override apiEndpoint = '/sch-b-transactions'; + override apiEndpoint = '/transactions/schedule-b'; // prettier-ignore static fromJSON(json: any, depth = 2): SchBTransaction { // eslint-disable-line @typescript-eslint/no-explicit-any @@ -56,9 +58,6 @@ export class SchBTransaction extends Transaction { const transactionType = TransactionTypeUtils.factory(transaction.transaction_type_identifier); transaction.setMetaProperties(transactionType); } - else { - throw new Error("Can't find transaction_type_identifier when creating class from JSON"); - } if (depth > 0 && transaction.parent_transaction) { transaction.parent_transaction = SchBTransaction.fromJSON(transaction.parent_transaction, depth-1); } @@ -67,10 +66,6 @@ export class SchBTransaction extends Transaction { } return transaction; } - - getUpdatedParent(): Transaction { - throw new Error('Tried to call updateParent on SchBTransaction and there is no update code'); - } } export enum ScheduleBTransactionGroups { @@ -168,14 +163,14 @@ export const ScheduleBTransactionTypeLabels: LabelList = [ [ScheduleBTransactionTypes.CONTRIBUTION_TO_CANDIDATE_VOID, 'Void of Contribution to Candidate'], [ScheduleBTransactionTypes.CONTRIBUTION_TO_OTHER_COMMITTEE, 'Contribution to Other Committee'], [ScheduleBTransactionTypes.CONTRIBUTION_TO_OTHER_COMMITTEE_VOID, 'Void of Contribution to Other Committee'], - [ScheduleBTransactionTypes.OTHER_DISBURSEMENT, 'Other Disbursements'], + [ScheduleBTransactionTypes.OTHER_DISBURSEMENT, 'Other Disbursement'], [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_CREDIT_CARD_PAYMENT, 'Credit Card Payment for Other Disbursements'], [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_CREDIT_CARD_PAYMENT_MEMO, 'Credit Card Corresponding Memo'], [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_STAFF_REIMBURSEMENT, 'Staff Reimbursements for Other Disbursements'], [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_STAFF_REIMBURSEMENT_MEMO, 'Reimbursement Corresponding Memo'], [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_PAYMENT_TO_PAYROLL, 'Payment to Payroll for Other Disbursements'], [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_PAYMENT_TO_PAYROLL_MEMO, 'Payroll Memo'], - [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_VOID, 'Void of Other Disbursements'], + [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_VOID, 'Void of Other Disbursement'], [ScheduleBTransactionTypes.OTHER_DISBURSEMENT_NON_CONTRIBUTION_ACCOUNT, 'Non-Contribution Account Disbursements'], [ ScheduleBTransactionTypes.OTHER_DISBURSEMENT_NON_CONTRIBUTION_ACCOUNT_CREDIT_CARD_PAYMENT, diff --git a/front-end/src/app/shared/models/transaction-navigation-controls.model.ts b/front-end/src/app/shared/models/transaction-navigation-controls.model.ts index a859baee04..a0ac5fffad 100644 --- a/front-end/src/app/shared/models/transaction-navigation-controls.model.ts +++ b/front-end/src/app/shared/models/transaction-navigation-controls.model.ts @@ -1,5 +1,5 @@ import { hasNoContact, isNewTransaction, Transaction } from './transaction.model'; -import { ScheduleTransactionTypes } from 'app/shared/models/transaction.model'; +import { TransactionTypes } from 'app/shared/models/transaction.model'; export enum NavigationAction { CANCEL, @@ -17,13 +17,13 @@ export class NavigationEvent { action: NavigationAction; destination: NavigationDestination; transaction?: Transaction; - destinationTransactionType?: ScheduleTransactionTypes; + destinationTransactionType?: TransactionTypes; constructor( action?: NavigationAction, destination?: NavigationDestination, transaction?: Transaction, - destinationTransactionType?: ScheduleTransactionTypes + destinationTransactionType?: TransactionTypes ) { this.action = action || NavigationAction.CANCEL; this.destination = destination || NavigationDestination.LIST; diff --git a/front-end/src/app/shared/models/transaction-type.model.spec.ts b/front-end/src/app/shared/models/transaction-type.model.spec.ts new file mode 100644 index 0000000000..95d33b010d --- /dev/null +++ b/front-end/src/app/shared/models/transaction-type.model.spec.ts @@ -0,0 +1,35 @@ +import { ScheduleATransactionTypes } from './scha-transaction.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; +import { TransactionType } from './transaction-type.model'; + +describe('Transaction Type Model', () => { + it('#generatePurposeDescriptionWrapper() should not truncate short purpose descriptions', () => { + const transaction = getTestTransactionByType(ScheduleATransactionTypes.PAC_RECOUNT_RECEIPT); + if (!transaction.transactionType) throw new Error('Fecfile: transactionType method does not exist'); + // prettier-ignore + const spy = spyOn(transaction.transactionType, 'generatePurposeDescription'); // eslint-disable-line @typescript-eslint/no-explicit-any + spy.and.returnValue('A short response'); + + const originalDescrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + const modifiedDescrip = transaction.transactionType?.generatePurposeDescriptionWrapper(transaction); + expect(originalDescrip).toEqual(modifiedDescrip); + }); + + it('#generatePurposeDescriptionWrapper() should not truncate short purpose descriptions', () => { + const transaction = getTestTransactionByType(ScheduleATransactionTypes.PAC_RECOUNT_RECEIPT); + if (!transaction.transactionType) throw new Error('Fecfile: transactionType method does not exist'); + // prettier-ignore + const spy = spyOn(transaction.transactionType, 'generatePurposeDescription'); // eslint-disable-line @typescript-eslint/no-explicit-any + spy.and.returnValue( + 'An absurdly long response' + + 'Just the biggest; no corners cut.' + + 'It needs to be at least 100 chars.' + + 'This should probably get it done.' + ); + + const originalDescrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + const modifiedDescrip = transaction.transactionType?.generatePurposeDescriptionWrapper(transaction); + expect(originalDescrip).not.toEqual(modifiedDescrip); + expect(modifiedDescrip?.length).toEqual(100); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-type.model.ts b/front-end/src/app/shared/models/transaction-type.model.ts new file mode 100644 index 0000000000..e6bb3f78bc --- /dev/null +++ b/front-end/src/app/shared/models/transaction-type.model.ts @@ -0,0 +1,81 @@ +import { TransactionNavigationControls } from './transaction-navigation-controls.model'; +import { JsonSchema } from '../interfaces/json-schema.interface'; +import { ContactType } from './contact.model'; +import { Transaction, TransactionTypes } from './transaction.model'; + +/** + * Class that defines the meta data associated with a transaction type. + * Populated and used by the transaction resovler for use in the transaction components. + */ +export abstract class TransactionType { + abstract scheduleId: string; + abstract componentGroupId: string; // Identifier of transaction component use to render UI form entry page + abstract title: string; + abstract schema: JsonSchema; // FEC validation JSON schema + negativeAmountValueOnly = false; // Set to true if the amount for the transaction can only have a negative value + isDependentChild = false; // When set to true, the parent transaction of the transaction is used to generate UI form entry page + dependentChildTransactionType?: TransactionType; // For double-entry transaction forms, this property defines the transaction type of the dependent child transaction + updateParentOnSave = false; // Set to true when the parent transaction may be affected by a change in the transaction + contactTypeOptions?: ContactType[]; // Override the default list of contact types in the transaction component + defaultContactTypeOption?: ContactType; // Set this to the default contact type (entity type) of the form select box if it is other than the first contact type in the contactTypeOptions list + subTransactionTypes?: TransactionTypes[]; // TransactionTypes displayed in dropdown to choose from when creating a child transaction + navigationControls?: TransactionNavigationControls; + generatePurposeDescription?(transaction: Transaction): string; // Dynamically generates the text in the CPD or EPD field + purposeDescriptionLabelNotice?: string; // Additional italicized text that appears beneath the form input label + abstract templateMap: TransactionTemplateMapType; // Mapping of values between the schedule (A,B,C...) and the common identifiers in the HTML templates + abstract getNewTransaction(): Transaction; // Factory method to create a new Transaction object with default property values for this transaction type + + getSchemaName(): string { + const schema_name = this?.schema?.$id?.split('/').pop()?.split('.')[0]; + if (!schema_name) { + throw new Error('Fecfile: Schema name for transaction type not found.'); + } + return schema_name; + } + + generatePurposeDescriptionLabel(): string { + if (this.generatePurposeDescription !== undefined) { + return '(SYSTEM-GENERATED)'; + } else if (this.schema.required.includes(this.templateMap.purpose_description)) { + return '(REQUIRED)'; + } + return '(OPTIONAL)'; + } + + public generatePurposeDescriptionWrapper(transaction: Transaction): string { + const purpose = this.generatePurposeDescription?.(transaction); + if (purpose) { + if (purpose.length > 100) { + return purpose.slice(0, 97) + '...'; + } + return purpose; + } + return ''; + } +} + +export type TransactionTemplateMapType = { + last_name: string; + first_name: string; + middle_name: string; + prefix: string; + suffix: string; + street_1: string; + street_2: string; + city: string; + state: string; + zip: string; + employer: string; + occupation: string; + organization_name: string; + committee_fec_id: string; + date: string; + dateLabel: string; + memo_code: string; + amount: string; + aggregate: string; + purpose_description: string; + purposeDescripLabel: string; + memo_text_input: string; + category_code: string; +}; diff --git a/front-end/src/app/shared/models/transaction-types/BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT.model.ts index a2b5645ba0..ec5c63df78 100644 --- a/front-end/src/app/shared/models/transaction-types/BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT extends SchaTransactionType { +export class BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO.model.ts index 06aa2becf1..7bba10138e 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO.model.ts @@ -1,8 +1,9 @@ import { schema } from 'fecfile-validate/fecfile_validate_js/dist/EARMARK_MEMO'; -import { AggregationGroups, SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class EARMARK_MEMO extends SchaTransactionType { +export class EARMARK_MEMO extends SchATransactionType { componentGroupId = 'AG'; override isDependentChild = true; title = ''; diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model.spec.ts index d37934cce0..ce4b3052bb 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('EARMARK_MEMO_CONVENTION_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('GG'); + expect(transactionType.componentGroupId).toBe('AG'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model.ts index 3b83cf4424..77e9678e0a 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model.ts @@ -1,9 +1,10 @@ import { schema } from 'fecfile-validate/fecfile_validate_js/dist/NATIONAL_PARTY_EARMARK_MEMOS'; -import { AggregationGroups, SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class EARMARK_MEMO_CONVENTION_ACCOUNT extends SchaTransactionType { - componentGroupId = 'GG'; +export class EARMARK_MEMO_CONVENTION_ACCOUNT extends SchATransactionType { + componentGroupId = 'AG'; override isDependentChild = true; title = ''; schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model.spec.ts index 32d6eec5a1..20c6dc4aeb 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('EARMARK_MEMO_HEADQUARTERS_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('GG'); + expect(transactionType.componentGroupId).toBe('AG'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model.ts index b65a543b78..35b17c1bd8 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model.ts @@ -1,9 +1,10 @@ import { schema } from 'fecfile-validate/fecfile_validate_js/dist/NATIONAL_PARTY_EARMARK_MEMOS'; -import { AggregationGroups, SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class EARMARK_MEMO_HEADQUARTERS_ACCOUNT extends SchaTransactionType { - componentGroupId = 'GG'; +export class EARMARK_MEMO_HEADQUARTERS_ACCOUNT extends SchATransactionType { + componentGroupId = 'AG'; override isDependentChild = true; title = ''; schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model.spec.ts index 195bd8c5f6..338174e5a7 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('EARMARK_MEMO_RECOUNT_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('GG'); + expect(transactionType.componentGroupId).toBe('AG'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model.ts index c774e37003..64bbc9ed0e 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model.ts @@ -1,9 +1,10 @@ import { schema } from 'fecfile-validate/fecfile_validate_js/dist/NATIONAL_PARTY_EARMARK_MEMOS'; -import { AggregationGroups, SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class EARMARK_MEMO_RECOUNT_ACCOUNT extends SchaTransactionType { - componentGroupId = 'GG'; +export class EARMARK_MEMO_RECOUNT_ACCOUNT extends SchATransactionType { + componentGroupId = 'AG'; override isDependentChild = true; title = ''; schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT.model.spec.ts index b4dc54d778..1160be4cdd 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT.model.spec.ts @@ -1,43 +1,38 @@ -import { ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { ContactTypes } from '../contact.model'; -import { EARMARK_MEMO } from './EARMARK_MEMO.model'; -import { EARMARK_RECEIPT } from './EARMARK_RECEIPT.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('EARMARK_RECEIPT', () => { - let transactionType: EARMARK_RECEIPT; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new EARMARK_RECEIPT(); + transaction = getTestTransactionByType(ScheduleATransactionTypes.EARMARK_RECEIPT) as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - if (transactionType) { - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('AG'); - } + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('AG'); }); it('#factory() should return a SchATransaction', () => { - const txn = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA11AI'); - expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.EARMARK_RECEIPT); + expect(transaction.form_type).toBe('SA11AI'); + expect(transaction.transaction_type_identifier).toBe(ScheduleATransactionTypes.EARMARK_RECEIPT); }); it('#generatePurposeDescription() should generate empty string', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(''); }); it('#generatePurposeDescription() should reflect child', () => { - const childTransactionType: EARMARK_MEMO = new EARMARK_MEMO(); - childTransactionType.transaction = childTransactionType.getNewTransaction(); - childTransactionType.transaction.entity_type = ContactTypes.INDIVIDUAL; - childTransactionType.transaction.contributor_first_name = 'Joe'; - childTransactionType.transaction.contributor_last_name = 'Smith'; + const childTransaction = getTestTransactionByType(ScheduleATransactionTypes.EARMARK_MEMO) as SchATransaction; + childTransaction.entity_type = ContactTypes.INDIVIDUAL; + childTransaction.contributor_first_name = 'Joe'; + childTransaction.contributor_last_name = 'Smith'; + transaction.children = [childTransaction]; - transactionType.childTransactionType = childTransactionType; - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe('Earmarked through Joe Smith'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT.model.ts index 127c24168f..8e22547439 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT.model.ts @@ -2,29 +2,26 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/EARMARK_RECEIPT'; import { ContactTypes } from '../contact.model'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { STANDARD_CONTROLS_MINIMAL, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class EARMARK_RECEIPT extends SchaTransactionType { +export class EARMARK_RECEIPT extends SchATransactionType { componentGroupId = 'AG'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.EARMARK_RECEIPT); schema = schema; - override childTransactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.EARMARK_MEMO); + override dependentChildTransactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.EARMARK_MEMO); override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS_MINIMAL; - override generatePurposeDescription(): string { - const earmarkMemo: SchATransaction = this.childTransactionType?.transaction as SchATransaction; - let conduit = earmarkMemo?.contributor_organization_name || ''; + override generatePurposeDescription(transaction: SchATransaction): string { + if (!transaction.children) return ''; + const earmarkMemo: SchATransaction = transaction.children[0] as SchATransaction; + let conduit = earmarkMemo.contributor_organization_name || ''; if ( - earmarkMemo?.entity_type === ContactTypes.INDIVIDUAL && - earmarkMemo?.contributor_first_name && - earmarkMemo?.contributor_last_name + earmarkMemo.entity_type === ContactTypes.INDIVIDUAL && + earmarkMemo.contributor_first_name && + earmarkMemo.contributor_last_name ) { conduit = `${earmarkMemo.contributor_first_name || ''} ${earmarkMemo.contributor_last_name || ''}`; } diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_CONVENTION_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_CONVENTION_ACCOUNT.model.spec.ts index e0d884d518..5217806280 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_CONVENTION_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_CONVENTION_ACCOUNT.model.spec.ts @@ -1,45 +1,44 @@ -import { ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { ContactTypes } from '../contact.model'; -import { EARMARK_MEMO_CONVENTION_ACCOUNT } from './EARMARK_MEMO_CONVENTION_ACCOUNT.model'; -import { EARMARK_RECEIPT_CONVENTION_ACCOUNT } from './EARMARK_RECEIPT_CONVENTION_ACCOUNT.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('EARMARK_RECEIPT_CONVENTION_ACCOUNT', () => { - let transactionType: EARMARK_RECEIPT_CONVENTION_ACCOUNT; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new EARMARK_RECEIPT_CONVENTION_ACCOUNT(); + transaction = getTestTransactionByType( + ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_CONVENTION_ACCOUNT_CONTRIBUTION + ) as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - if (transactionType) { - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('AG'); - } + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('AG'); }); it('#factory() should return a SchATransaction', () => { - const txn = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_CONVENTION_ACCOUNT_CONTRIBUTION ); }); it('#generatePurposeDescription() should generate empty string', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(''); }); it('#generatePurposeDescription() should reflect child', () => { - const childTransactionType: EARMARK_MEMO_CONVENTION_ACCOUNT = new EARMARK_MEMO_CONVENTION_ACCOUNT(); - childTransactionType.transaction = childTransactionType.getNewTransaction(); - childTransactionType.transaction.entity_type = ContactTypes.INDIVIDUAL; - childTransactionType.transaction.contributor_first_name = 'Joe'; - childTransactionType.transaction.contributor_last_name = 'Smith'; + const childTransaction = getTestTransactionByType( + ScheduleATransactionTypes.EARMARK_MEMO_CONVENTION_ACCOUNT + ) as SchATransaction; + childTransaction.entity_type = ContactTypes.INDIVIDUAL; + childTransaction.contributor_first_name = 'Joe'; + childTransaction.contributor_last_name = 'Smith'; + transaction.children = [childTransaction]; - transactionType.childTransactionType = childTransactionType; - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe('Pres. Nominating Convention Account - Earmarked Through Joe Smith'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_CONVENTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_CONVENTION_ACCOUNT.model.ts index 5b45e778b9..180e36015b 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_CONVENTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_CONVENTION_ACCOUNT.model.ts @@ -2,27 +2,23 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/NATIONAL_PARTY_EARMARK_RECEIPTS'; import { ContactTypes } from '../contact.model'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { CANCEL_CONTROL, SAVE_LIST_CONTROL, TransactionNavigationControls, } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class EARMARK_RECEIPT_CONVENTION_ACCOUNT extends SchaTransactionType { +export class EARMARK_RECEIPT_CONVENTION_ACCOUNT extends SchATransactionType { componentGroupId = 'AG'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_CONVENTION_ACCOUNT_CONTRIBUTION ); schema = schema; - override childTransactionType = TransactionTypeUtils.factory( + override dependentChildTransactionType = TransactionTypeUtils.factory( ScheduleATransactionTypes.EARMARK_MEMO_CONVENTION_ACCOUNT ); override navigationControls: TransactionNavigationControls = new TransactionNavigationControls( @@ -31,13 +27,14 @@ export class EARMARK_RECEIPT_CONVENTION_ACCOUNT extends SchaTransactionType { [SAVE_LIST_CONTROL] ); - override generatePurposeDescription(): string { - const subTransaction: SchATransaction = this.childTransactionType?.transaction as SchATransaction; - let conduit = subTransaction?.contributor_organization_name || ''; + override generatePurposeDescription(transaction: SchATransaction): string { + if (!transaction.children) return ''; + const subTransaction: SchATransaction = transaction.children[0] as SchATransaction; + let conduit = subTransaction.contributor_organization_name || ''; if ( - subTransaction?.entity_type === ContactTypes.INDIVIDUAL && - subTransaction?.contributor_first_name && - subTransaction?.contributor_last_name + subTransaction.entity_type === ContactTypes.INDIVIDUAL && + subTransaction.contributor_first_name && + subTransaction.contributor_last_name ) { conduit = `${subTransaction.contributor_first_name || ''} ${subTransaction.contributor_last_name || ''}`; } diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model.spec.ts index f4665d54e2..a92d7b67ed 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model.spec.ts @@ -1,45 +1,44 @@ -import { ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { ContactTypes } from '../contact.model'; -import { EARMARK_MEMO_HEADQUARTERS_ACCOUNT } from './EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model'; -import { EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT } from './EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT', () => { - let transactionType: EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT(); + transaction = getTestTransactionByType( + ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_HEADQUARTERS_ACCOUNT_CONTRIBUTION + ) as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - if (transactionType) { - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('AG'); - } + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('AG'); }); it('#factory() should return a SchATransaction', () => { - const txn = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_HEADQUARTERS_ACCOUNT_CONTRIBUTION ); }); it('#generatePurposeDescription() should generate empty string', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(''); }); it('#generatePurposeDescription() should reflect child', () => { - const childTransactionType: EARMARK_MEMO_HEADQUARTERS_ACCOUNT = new EARMARK_MEMO_HEADQUARTERS_ACCOUNT(); - childTransactionType.transaction = childTransactionType.getNewTransaction(); - childTransactionType.transaction.entity_type = ContactTypes.INDIVIDUAL; - childTransactionType.transaction.contributor_first_name = 'Joe'; - childTransactionType.transaction.contributor_last_name = 'Smith'; + const childTransaction = getTestTransactionByType( + ScheduleATransactionTypes.EARMARK_MEMO_HEADQUARTERS_ACCOUNT + ) as SchATransaction; + childTransaction.entity_type = ContactTypes.INDIVIDUAL; + childTransaction.contributor_first_name = 'Joe'; + childTransaction.contributor_last_name = 'Smith'; + transaction.children = [childTransaction]; - transactionType.childTransactionType = childTransactionType; - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe('Headquarters Buildings Account - Earmarked Through Joe Smith'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model.ts index bf56e07a30..f216a7e403 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT.model.ts @@ -2,27 +2,23 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/NATIONAL_PARTY_EARMARK_RECEIPTS'; import { ContactTypes } from '../contact.model'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { CANCEL_CONTROL, SAVE_LIST_CONTROL, TransactionNavigationControls, } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT extends SchaTransactionType { +export class EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT extends SchATransactionType { componentGroupId = 'AG'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_HEADQUARTERS_ACCOUNT_CONTRIBUTION ); schema = schema; - override childTransactionType = TransactionTypeUtils.factory( + override dependentChildTransactionType = TransactionTypeUtils.factory( ScheduleATransactionTypes.EARMARK_MEMO_HEADQUARTERS_ACCOUNT ); override navigationControls: TransactionNavigationControls = new TransactionNavigationControls( @@ -31,13 +27,14 @@ export class EARMARK_RECEIPT_HEADQUARTERS_ACCOUNT extends SchaTransactionType { [SAVE_LIST_CONTROL] ); - override generatePurposeDescription(): string { - const subTransaction: SchATransaction = this.childTransactionType?.transaction as SchATransaction; - let conduit = subTransaction?.contributor_organization_name || ''; + override generatePurposeDescription(transaction: SchATransaction): string { + if (!transaction.children) return ''; + const subTransaction: SchATransaction = transaction.children[0] as SchATransaction; + let conduit = subTransaction.contributor_organization_name || ''; if ( - subTransaction?.entity_type === ContactTypes.INDIVIDUAL && - subTransaction?.contributor_first_name && - subTransaction?.contributor_last_name + subTransaction.entity_type === ContactTypes.INDIVIDUAL && + subTransaction.contributor_first_name && + subTransaction.contributor_last_name ) { conduit = `${subTransaction.contributor_first_name || ''} ${subTransaction.contributor_last_name || ''}`; } diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_RECOUNT_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_RECOUNT_ACCOUNT.model.spec.ts index 048aedccd6..8035e15c2f 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_RECOUNT_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_RECOUNT_ACCOUNT.model.spec.ts @@ -1,45 +1,44 @@ -import { ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { ContactTypes } from '../contact.model'; -import { EARMARK_MEMO_RECOUNT_ACCOUNT } from './EARMARK_MEMO_RECOUNT_ACCOUNT.model'; -import { EARMARK_RECEIPT_RECOUNT_ACCOUNT } from './EARMARK_RECEIPT_RECOUNT_ACCOUNT.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('EARMARK_RECEIPT_RECOUNT_ACCOUNT', () => { - let transactionType: EARMARK_RECEIPT_RECOUNT_ACCOUNT; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new EARMARK_RECEIPT_RECOUNT_ACCOUNT(); + transaction = getTestTransactionByType( + ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_RECOUNT_ACCOUNT_CONTRIBUTION + ) as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - if (transactionType) { - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('AG'); - } + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('AG'); }); it('#factory() should return a SchATransaction', () => { - const txn = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_RECOUNT_ACCOUNT_CONTRIBUTION ); }); it('#generatePurposeDescription() should generate empty string', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(''); }); it('#generatePurposeDescription() should reflect child', () => { - const childTransactionType: EARMARK_MEMO_RECOUNT_ACCOUNT = new EARMARK_MEMO_RECOUNT_ACCOUNT(); - childTransactionType.transaction = childTransactionType.getNewTransaction(); - childTransactionType.transaction.entity_type = ContactTypes.INDIVIDUAL; - childTransactionType.transaction.contributor_first_name = 'Joe'; - childTransactionType.transaction.contributor_last_name = 'Smith'; + const childTransaction = getTestTransactionByType( + ScheduleATransactionTypes.EARMARK_MEMO_RECOUNT_ACCOUNT + ) as SchATransaction; + childTransaction.entity_type = ContactTypes.INDIVIDUAL; + childTransaction.contributor_first_name = 'Joe'; + childTransaction.contributor_last_name = 'Smith'; + transaction.children = [childTransaction]; - transactionType.childTransactionType = childTransactionType; - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe('Recount/Legal Proceedings Account - Earmarked Through Joe Smith'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_RECOUNT_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_RECOUNT_ACCOUNT.model.ts index ceb310ef49..7ddbd5e864 100644 --- a/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_RECOUNT_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/EARMARK_RECEIPT_RECOUNT_ACCOUNT.model.ts @@ -2,40 +2,39 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/NATIONAL_PARTY_EARMARK_RECEIPTS'; import { ContactTypes } from '../contact.model'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { CANCEL_CONTROL, SAVE_LIST_CONTROL, TransactionNavigationControls, } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class EARMARK_RECEIPT_RECOUNT_ACCOUNT extends SchaTransactionType { +export class EARMARK_RECEIPT_RECOUNT_ACCOUNT extends SchATransactionType { componentGroupId = 'AG'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_RECOUNT_ACCOUNT_CONTRIBUTION ); schema = schema; - override childTransactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.EARMARK_MEMO_RECOUNT_ACCOUNT); + override dependentChildTransactionType = TransactionTypeUtils.factory( + ScheduleATransactionTypes.EARMARK_MEMO_RECOUNT_ACCOUNT + ); override navigationControls: TransactionNavigationControls = new TransactionNavigationControls( [], [CANCEL_CONTROL], [SAVE_LIST_CONTROL] ); - override generatePurposeDescription(): string { - const subTransaction: SchATransaction = this.childTransactionType?.transaction as SchATransaction; - let conduit = subTransaction?.contributor_organization_name || ''; + override generatePurposeDescription(transaction: SchATransaction): string { + if (!transaction.children) return ''; + const subTransaction: SchATransaction = transaction.children[0] as SchATransaction; + let conduit = subTransaction.contributor_organization_name || ''; if ( - subTransaction?.entity_type === ContactTypes.INDIVIDUAL && - subTransaction?.contributor_first_name && - subTransaction?.contributor_last_name + subTransaction.entity_type === ContactTypes.INDIVIDUAL && + subTransaction.contributor_first_name && + subTransaction.contributor_last_name ) { conduit = `${subTransaction.contributor_first_name || ''} ${subTransaction.contributor_last_name || ''}`; } diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_JF_TRANSFER_MEMO.model.spec.ts index 245a5b6580..b21673938e 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_JF_TRANSFER_MEMO.model.spec.ts @@ -1,31 +1,30 @@ +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { INDIVIDUAL_JF_TRANSFER_MEMO } from './INDIVIDUAL_JF_TRANSFER_MEMO.model'; -import { JOINT_FUNDRAISING_TRANSFER } from './JOINT_FUNDRAISING_TRANSFER.model'; describe('INDIVIDUAL_JF_TRANSFER_MEMO', () => { - let transactionType: INDIVIDUAL_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new INDIVIDUAL_JF_TRANSFER_MEMO(); - transactionType.transaction = transactionType.getNewTransaction(); - transactionType.transaction.parent_transaction = new JOINT_FUNDRAISING_TRANSFER().getNewTransaction(); - (transactionType.transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; + transaction = getTestTransactionByType( + ScheduleATransactionTypes.INDIVIDUAL_JF_TRANSFER_MEMO, + ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER + ) as SchATransaction; + (transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('A'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('A'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA12'); - expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.INDIVIDUAL_JF_TRANSFER_MEMO); + expect(transaction.form_type).toBe('SA12'); + expect(transaction.transaction_type_identifier).toBe(ScheduleATransactionTypes.INDIVIDUAL_JF_TRANSFER_MEMO); }); it('#generatePurposeDescription() should return appropriate retval', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(`JF Memo: Test Org`); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_JF_TRANSFER_MEMO.model.ts index 5ef1ce58f0..b4b8547b69 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_JF_TRANSFER_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class INDIVIDUAL_JF_TRANSFER_MEMO extends SchaTransactionType { +export class INDIVIDUAL_JF_TRANSFER_MEMO extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.INDIVIDUAL_JF_TRANSFER_MEMO); schema = schema; @@ -17,8 +13,8 @@ export class INDIVIDUAL_JF_TRANSFER_MEMO extends SchaTransactionType { LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { - return `JF Memo: ${(this.transaction?.parent_transaction as SchATransaction).contributor_organization_name}`; + override generatePurposeDescription(transaction: SchATransaction): string { + return `JF Memo: ${(transaction.parent_transaction as SchATransaction).contributor_organization_name}`; } getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts index 942eb67db8..ee162bf09e 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchaTransactionType { +export class INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts index 4ce85f1f2f..e228f22105 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts @@ -1,33 +1,31 @@ +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO } from './INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model'; describe('INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO', () => { - let transactionType: INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO(); + transaction = getTestTransactionByType( + ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO + ) as SchATransaction; + transaction.parent_transaction = { contributor_organization_name: 'Test Org' } as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('A'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('A'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe( - `Pres. Nominating Convention Account JF Memo: ${ - (transactionType.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name - }` - ); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Pres. Nominating Convention Account JF Memo: Test Org'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts index bb393ec11b..b3db028288 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchaTransactionType { +export class INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get( ScheduleATransactionTypeLabels, @@ -20,9 +16,9 @@ export class INDIVIDUAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchaT LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Pres. Nominating Convention Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name + (transaction.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts index 6fb4ac3a62..f19b899069 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchaTransactionType { +export class INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts index 7ed578bb97..5e73cd87f2 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts @@ -1,33 +1,32 @@ -import { INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO } from './INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { JOINT_FUNDRAISING_TRANSFER } from './JOINT_FUNDRAISING_TRANSFER.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO', () => { - let transactionType: INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO(); - transactionType.transaction = transactionType.getNewTransaction(); - transactionType.transaction.parent_transaction = new JOINT_FUNDRAISING_TRANSFER().getNewTransaction(); - (transactionType.transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; + transaction = getTestTransactionByType( + ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO, + ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER + ) as SchATransaction; + (transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('A'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('A'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe(`Headquarters Buildings Account JF Memo: Test Org`); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Headquarters Buildings Account JF Memo: Test Org'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts index eec1e2c45c..6d8324d665 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends SchaTransactionType { +export class INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get( ScheduleATransactionTypeLabels, @@ -20,9 +16,9 @@ export class INDIVIDUAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends Sch LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Headquarters Buildings Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction).contributor_organization_name + (transaction.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts index 7dd292f7c3..d3ba68dd33 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchaTransactionType { +export class INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts index 029383d6de..b492fc4b27 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts @@ -1,33 +1,31 @@ +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO } from './INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model'; describe('INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO', () => { - let transactionType: INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO(); + transaction = getTestTransactionByType( + ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO + ) as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('A'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('A'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe( - `Recount/Legal Proceedings Account JF Memo: ${ - (transactionType.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name - }` - ); + transaction.parent_transaction = { contributor_organization_name: 'Test Org' } as SchATransaction; + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Recount/Legal Proceedings Account JF Memo: Test Org'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts index fbc97bd097..ea66179073 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchaTransactionType { +export class INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get( ScheduleATransactionTypeLabels, @@ -20,9 +16,9 @@ export class INDIVIDUAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchaTran LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Recount/Legal Proceedings Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name + (transaction?.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT.model.spec.ts index d172f57815..78a4cc3f0f 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT.model.spec.ts @@ -1,6 +1,6 @@ import { INDIVIDUAL_RECEIPT } from './INDIVIDUAL_RECEIPT.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; describe('INDIVIDUAL_RECEIPT', () => { let transactionType: INDIVIDUAL_RECEIPT; diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT.model.ts index fae6f5f9e2..e1459cccf7 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_RECEIPT extends SchaTransactionType { +export class INDIVIDUAL_RECEIPT extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.INDIVIDUAL_RECEIPT); schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT_NON_CONTRIBUTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT_NON_CONTRIBUTION_ACCOUNT.model.ts index 8a72af720a..14b0748006 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT_NON_CONTRIBUTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECEIPT_NON_CONTRIBUTION_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_RECEIPT_NON_CONTRIBUTION_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_RECEIPT_NON_CONTRIBUTION_ACCOUNT extends SchaTransactionType { +export class INDIVIDUAL_RECEIPT_NON_CONTRIBUTION_ACCOUNT extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECOUNT_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECOUNT_RECEIPT.model.ts index 8cabf385bb..4fe200c218 100644 --- a/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECOUNT_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/INDIVIDUAL_RECOUNT_RECEIPT.model.ts @@ -1,22 +1,18 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_RECOUNT_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class INDIVIDUAL_RECOUNT_RECEIPT extends SchaTransactionType { +export class INDIVIDUAL_RECOUNT_RECEIPT extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.INDIVIDUAL_RECOUNT_RECEIPT); schema = schema; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; override generatePurposeDescription(): string { - return `Recount Account`; + return 'Recount Account'; } getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts index 486d352791..ede6d0bbb2 100644 --- a/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/JF_TRANSFER_NATIONAL_PARTY_CONVENTION_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class JF_TRANSFER_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchaTransactionType { +export class JF_TRANSFER_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchATransactionType { componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, @@ -25,7 +21,7 @@ export class JF_TRANSFER_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchaTransacti override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; override generatePurposeDescription(): string { - return `Pres. Nominating Convention Account Transfer of JF Proceeds`; + return 'Pres. Nominating Convention Account Transfer of JF Proceeds'; } getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts index 5c7389cc68..1e21aeb45a 100644 --- a/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/JF_TRANSFER_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class JF_TRANSFER_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchaTransactionType { +export class JF_TRANSFER_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchATransactionType { componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts index d08850a970..0635b7e192 100644 --- a/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/JF_TRANSFER_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/JF_TRANSFER_NATIONAL_PARTY_RECOUNT_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class JF_TRANSFER_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchaTransactionType { +export class JF_TRANSFER_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchATransactionType { componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/JOINT_FUNDRAISING_TRANSFER.model.ts b/front-end/src/app/shared/models/transaction-types/JOINT_FUNDRAISING_TRANSFER.model.ts index 7028392bd1..05610b782a 100644 --- a/front-end/src/app/shared/models/transaction-types/JOINT_FUNDRAISING_TRANSFER.model.ts +++ b/front-end/src/app/shared/models/transaction-types/JOINT_FUNDRAISING_TRANSFER.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/JOINT_FUNDRAISING_TRANSFER'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class JOINT_FUNDRAISING_TRANSFER extends SchaTransactionType { +export class JOINT_FUNDRAISING_TRANSFER extends SchATransactionType { componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER); schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/OFFSET_TO_OPERATING_EXPENDITURES.model.spec.ts b/front-end/src/app/shared/models/transaction-types/OFFSET_TO_OPERATING_EXPENDITURES.model.spec.ts index f0ec64a58e..0430fdc542 100644 --- a/front-end/src/app/shared/models/transaction-types/OFFSET_TO_OPERATING_EXPENDITURES.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/OFFSET_TO_OPERATING_EXPENDITURES.model.spec.ts @@ -1,6 +1,6 @@ import { OFFSET_TO_OPERATING_EXPENDITURES } from './OFFSET_TO_OPERATING_EXPENDITURES.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; describe('OFFSET_TO_OPERATING_EXPENDITURES', () => { let transactionType: OFFSET_TO_OPERATING_EXPENDITURES; diff --git a/front-end/src/app/shared/models/transaction-types/OFFSET_TO_OPERATING_EXPENDITURES.model.ts b/front-end/src/app/shared/models/transaction-types/OFFSET_TO_OPERATING_EXPENDITURES.model.ts index 1bf229cb01..a071814d22 100644 --- a/front-end/src/app/shared/models/transaction-types/OFFSET_TO_OPERATING_EXPENDITURES.model.ts +++ b/front-end/src/app/shared/models/transaction-types/OFFSET_TO_OPERATING_EXPENDITURES.model.ts @@ -1,20 +1,16 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/OFFSET_TO_OPERATING_EXPENDITURES'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; import { ContactTypes } from '../contact.model'; -export class OFFSET_TO_OPERATING_EXPENDITURES extends SchaTransactionType { +export class OFFSET_TO_OPERATING_EXPENDITURES extends SchATransactionType { componentGroupId = 'B'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.OFFSET_TO_OPERATING_EXPENDITURES); schema = schema; - override contactTypeOptions = [ContactTypes.ORGANIZATION, ContactTypes.COMMITTEE, ContactTypes.INDIVIDUAL]; + override defaultContactTypeOption = ContactTypes.ORGANIZATION; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE.model.spec.ts b/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE.model.spec.ts new file mode 100644 index 0000000000..bc92893525 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE.model.spec.ts @@ -0,0 +1,27 @@ +import { OPERATING_EXPENDITURE } from './OPERATING_EXPENDITURE.model'; +import { SchBTransaction, ScheduleBTransactionTypes } from '../schb-transaction.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; + +describe('OPERATING_EXPENDITURE', () => { + let transactionType: OPERATING_EXPENDITURE; + + beforeEach(() => { + transactionType = new OPERATING_EXPENDITURE(); + }); + + it('should create an instance', () => { + expect(transactionType).toBeTruthy(); + expect(transactionType.scheduleId).toBe('B'); + expect(transactionType.componentGroupId).toBe('B'); + }); + + it('#factory() should return a SchBTransaction', () => { + const txn: SchBTransaction = transactionType.getNewTransaction(); + expect(txn.form_type).toBe('SB21b'); + expect(txn.transaction_type_identifier).toBe(ScheduleBTransactionTypes.OPERATING_EXPENDITURE); + }); + + it('#generatePurposeDescription() should not be defined', () => { + expect((transactionType as TransactionType).generatePurposeDescription).toBe(undefined); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE.model.ts b/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE.model.ts new file mode 100644 index 0000000000..870d87f135 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE.model.ts @@ -0,0 +1,23 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/DISBURSEMENTS'; +import { AggregationGroups } from '../transaction.model'; +import { SchBTransaction, ScheduleBTransactionTypeLabels, ScheduleBTransactionTypes } from '../schb-transaction.model'; +import { SchBTransactionType } from '../schb-transaction-type.model'; +import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { ContactTypes } from '../contact.model'; + +export class OPERATING_EXPENDITURE extends SchBTransactionType { + componentGroupId = 'B'; + title = LabelUtils.get(ScheduleBTransactionTypeLabels, ScheduleBTransactionTypes.OPERATING_EXPENDITURE); + schema = schema; + override defaultContactTypeOption = ContactTypes.ORGANIZATION; + override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; + + getNewTransaction() { + return SchBTransaction.fromJSON({ + form_type: 'SB21b', + transaction_type_identifier: ScheduleBTransactionTypes.OPERATING_EXPENDITURE, + aggregation_group: AggregationGroups.GENERAL_DISBURSEMENT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE_VOID.model.spec.ts b/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE_VOID.model.spec.ts new file mode 100644 index 0000000000..ba175c151f --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE_VOID.model.spec.ts @@ -0,0 +1,27 @@ +import { OPERATING_EXPENDITURE_VOID } from './OPERATING_EXPENDITURE_VOID.model'; +import { SchBTransaction, ScheduleBTransactionTypes } from '../schb-transaction.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; + +describe('OPERATING_EXPENDITURE_VOID', () => { + let transactionType: OPERATING_EXPENDITURE_VOID; + + beforeEach(() => { + transactionType = new OPERATING_EXPENDITURE_VOID(); + }); + + it('should create an instance', () => { + expect(transactionType).toBeTruthy(); + expect(transactionType.scheduleId).toBe('B'); + expect(transactionType.componentGroupId).toBe('B'); + }); + + it('#factory() should return a SchBTransaction', () => { + const txn: SchBTransaction = transactionType.getNewTransaction(); + expect(txn.form_type).toBe('SB21b'); + expect(txn.transaction_type_identifier).toBe(ScheduleBTransactionTypes.OPERATING_EXPENDITURE_VOID); + }); + + it('#generatePurposeDescription() should not be defined', () => { + expect((transactionType as TransactionType).generatePurposeDescription).toBe(undefined); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE_VOID.model.ts b/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE_VOID.model.ts new file mode 100644 index 0000000000..334eb800bf --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/OPERATING_EXPENDITURE_VOID.model.ts @@ -0,0 +1,24 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/DISBURSEMENTS'; +import { AggregationGroups } from '../transaction.model'; +import { SchBTransaction, ScheduleBTransactionTypeLabels, ScheduleBTransactionTypes } from '../schb-transaction.model'; +import { SchBTransactionType } from '../schb-transaction-type.model'; +import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { ContactTypes } from '../contact.model'; + +export class OPERATING_EXPENDITURE_VOID extends SchBTransactionType { + componentGroupId = 'B'; + title = LabelUtils.get(ScheduleBTransactionTypeLabels, ScheduleBTransactionTypes.OPERATING_EXPENDITURE_VOID); + schema = schema; + override negativeAmountValueOnly = true; + override defaultContactTypeOption = ContactTypes.ORGANIZATION; + override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; + + getNewTransaction() { + return SchBTransaction.fromJSON({ + form_type: 'SB21b', + transaction_type_identifier: ScheduleBTransactionTypes.OPERATING_EXPENDITURE_VOID, + aggregation_group: AggregationGroups.GENERAL_DISBURSEMENT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT.model.spec.ts index dccb92d1ad..f2a701efca 100644 --- a/front-end/src/app/shared/models/transaction-types/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT.model.ts index 1c3bb2ffa3..5fe5dccd42 100644 --- a/front-end/src/app/shared/models/transaction-types/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT extends SchaTransactionType { - componentGroupId = 'F'; +export class OTHER_COMMITTEE_NON_CONTRIBUTION_ACCOUNT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.OTHER_COMMITTEE_RECEIPT_NON_CONTRIBUTION_ACCOUNT diff --git a/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT.model.spec.ts new file mode 100644 index 0000000000..3d00adad2b --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT.model.spec.ts @@ -0,0 +1,27 @@ +import { OTHER_DISBURSEMENT } from './OTHER_DISBURSEMENT.model'; +import { SchBTransaction, ScheduleBTransactionTypes } from '../schb-transaction.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; + +describe('OTHER_DISBURSEMENT', () => { + let transactionType: OTHER_DISBURSEMENT; + + beforeEach(() => { + transactionType = new OTHER_DISBURSEMENT(); + }); + + it('should create an instance', () => { + expect(transactionType).toBeTruthy(); + expect(transactionType.scheduleId).toBe('B'); + expect(transactionType.componentGroupId).toBe('B'); + }); + + it('#factory() should return a SchBTransaction', () => { + const txn: SchBTransaction = transactionType.getNewTransaction(); + expect(txn.form_type).toBe('SB29'); + expect(txn.transaction_type_identifier).toBe(ScheduleBTransactionTypes.OTHER_DISBURSEMENT); + }); + + it('#generatePurposeDescription() should not be defined', () => { + expect((transactionType as TransactionType).generatePurposeDescription).toBe(undefined); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT.model.ts b/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT.model.ts new file mode 100644 index 0000000000..9784d3fcb7 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT.model.ts @@ -0,0 +1,23 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/DISBURSEMENTS'; +import { AggregationGroups } from '../transaction.model'; +import { SchBTransaction, ScheduleBTransactionTypeLabels, ScheduleBTransactionTypes } from '../schb-transaction.model'; +import { SchBTransactionType } from '../schb-transaction-type.model'; +import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { ContactTypes } from '../contact.model'; + +export class OTHER_DISBURSEMENT extends SchBTransactionType { + componentGroupId = 'B'; + title = LabelUtils.get(ScheduleBTransactionTypeLabels, ScheduleBTransactionTypes.OTHER_DISBURSEMENT); + schema = schema; + override defaultContactTypeOption = ContactTypes.ORGANIZATION; + override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; + + getNewTransaction() { + return SchBTransaction.fromJSON({ + form_type: 'SB29', + transaction_type_identifier: ScheduleBTransactionTypes.OTHER_DISBURSEMENT, + aggregation_group: AggregationGroups.GENERAL_DISBURSEMENT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT_VOID.model.spec.ts b/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT_VOID.model.spec.ts new file mode 100644 index 0000000000..2b1d68041e --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT_VOID.model.spec.ts @@ -0,0 +1,27 @@ +import { OTHER_DISBURSEMENT_VOID } from './OTHER_DISBURSEMENT_VOID.model'; +import { SchBTransaction, ScheduleBTransactionTypes } from '../schb-transaction.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; + +describe('OTHER_DISBURSEMENT_VOID', () => { + let transactionType: OTHER_DISBURSEMENT_VOID; + + beforeEach(() => { + transactionType = new OTHER_DISBURSEMENT_VOID(); + }); + + it('should create an instance', () => { + expect(transactionType).toBeTruthy(); + expect(transactionType.scheduleId).toBe('B'); + expect(transactionType.componentGroupId).toBe('B'); + }); + + it('#factory() should return a SchBTransaction', () => { + const txn: SchBTransaction = transactionType.getNewTransaction(); + expect(txn.form_type).toBe('SB29'); + expect(txn.transaction_type_identifier).toBe(ScheduleBTransactionTypes.OTHER_DISBURSEMENT_VOID); + }); + + it('#generatePurposeDescription() should not be defined', () => { + expect((transactionType as TransactionType).generatePurposeDescription).toBe(undefined); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT_VOID.model.ts b/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT_VOID.model.ts new file mode 100644 index 0000000000..d14a0bcf71 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/OTHER_DISBURSEMENT_VOID.model.ts @@ -0,0 +1,24 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/DISBURSEMENTS'; +import { AggregationGroups } from '../transaction.model'; +import { SchBTransaction, ScheduleBTransactionTypeLabels, ScheduleBTransactionTypes } from '../schb-transaction.model'; +import { SchBTransactionType } from '../schb-transaction-type.model'; +import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { ContactTypes } from '../contact.model'; + +export class OTHER_DISBURSEMENT_VOID extends SchBTransactionType { + componentGroupId = 'B'; + title = LabelUtils.get(ScheduleBTransactionTypeLabels, ScheduleBTransactionTypes.OTHER_DISBURSEMENT_VOID); + schema = schema; + override negativeAmountValueOnly = true; + override defaultContactTypeOption = ContactTypes.ORGANIZATION; + override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; + + getNewTransaction() { + return SchBTransaction.fromJSON({ + form_type: 'SB29', + transaction_type_identifier: ScheduleBTransactionTypes.OTHER_DISBURSEMENT_VOID, + aggregation_group: AggregationGroups.GENERAL_DISBURSEMENT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/OTHER_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/OTHER_RECEIPT.model.spec.ts index 321ba0763e..8d7078e438 100644 --- a/front-end/src/app/shared/models/transaction-types/OTHER_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/OTHER_RECEIPT.model.spec.ts @@ -1,6 +1,6 @@ import { OTHER_RECEIPT } from './OTHER_RECEIPT.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; describe('OTHER_RECEIPT', () => { let transactionType: OTHER_RECEIPT; diff --git a/front-end/src/app/shared/models/transaction-types/OTHER_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/OTHER_RECEIPT.model.ts index 382e393ad1..3af835f0dd 100644 --- a/front-end/src/app/shared/models/transaction-types/OTHER_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/OTHER_RECEIPT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/OTHER_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class OTHER_RECEIPT extends SchaTransactionType { +export class OTHER_RECEIPT extends SchATransactionType { componentGroupId = 'C'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.OTHER_RECEIPTS); schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_MEMO.model.ts index 42a7742abb..0e8c09ee55 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_MEMO.model.ts @@ -1,8 +1,9 @@ import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_EARMARK_MEMO'; -import { AggregationGroups, SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class PAC_EARMARK_MEMO extends SchaTransactionType { +export class PAC_EARMARK_MEMO extends SchATransactionType { componentGroupId = 'FG'; override isDependentChild = true; title = ''; diff --git a/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model.spec.ts index 0ef57a53ab..3166aebb8b 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model.spec.ts @@ -1,43 +1,38 @@ -import { ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { ContactTypes } from '../contact.model'; -import { PAC_EARMARK_MEMO } from './PAC_EARMARK_MEMO.model'; -import { PAC_EARMARK_RECEIPT } from './PAC_EARMARK_RECEIPT.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('PAC_EARMARK_RECEIPT', () => { - let transactionType: PAC_EARMARK_RECEIPT; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new PAC_EARMARK_RECEIPT(); + transaction = getTestTransactionByType(ScheduleATransactionTypes.PAC_EARMARK_RECEIPT) as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - if (transactionType) { - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('FG'); - } + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('FG'); }); it('#factory() should return a SchATransaction', () => { - const txn = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA11C'); - expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.PAC_EARMARK_RECEIPT); + expect(transaction.form_type).toBe('SA11C'); + expect(transaction.transaction_type_identifier).toBe(ScheduleATransactionTypes.PAC_EARMARK_RECEIPT); }); it('#generatePurposeDescription() should generate empty string', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(''); }); it('#generatePurposeDescription() should reflect child', () => { - const childTransactionType: PAC_EARMARK_MEMO = new PAC_EARMARK_MEMO(); - childTransactionType.transaction = childTransactionType.getNewTransaction(); - childTransactionType.transaction.entity_type = ContactTypes.INDIVIDUAL; - childTransactionType.transaction.contributor_first_name = 'Joe'; - childTransactionType.transaction.contributor_last_name = 'Smith'; + const childTransaction = getTestTransactionByType(ScheduleATransactionTypes.PAC_EARMARK_MEMO) as SchATransaction; + childTransaction.entity_type = ContactTypes.INDIVIDUAL; + childTransaction.contributor_first_name = 'Joe'; + childTransaction.contributor_last_name = 'Smith'; + transaction.children = [childTransaction]; - transactionType.childTransactionType = childTransactionType; - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe('Earmarked through Joe Smith'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model.ts index 90584c716f..81c1a7c18f 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model.ts @@ -2,29 +2,26 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_EARMARK_RECEIPT'; import { ContactTypes } from '../contact.model'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { STANDARD_CONTROLS_MINIMAL, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class PAC_EARMARK_RECEIPT extends SchaTransactionType { +export class PAC_EARMARK_RECEIPT extends SchATransactionType { componentGroupId = 'FG'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_EARMARK_RECEIPT); schema = schema; - override childTransactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.PAC_EARMARK_MEMO); + override dependentChildTransactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.PAC_EARMARK_MEMO); override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS_MINIMAL; - override generatePurposeDescription(): string { - const earmarkMemo: SchATransaction = this.childTransactionType?.transaction as SchATransaction; - let conduit = earmarkMemo?.contributor_organization_name || ''; + override generatePurposeDescription(transaction: SchATransaction): string { + if (!transaction.children) return ''; + const earmarkMemo: SchATransaction = transaction.children[0] as SchATransaction; + let conduit = earmarkMemo.contributor_organization_name || ''; if ( - earmarkMemo?.entity_type === ContactTypes.INDIVIDUAL && - earmarkMemo?.contributor_first_name && - earmarkMemo?.contributor_last_name + earmarkMemo.entity_type === ContactTypes.INDIVIDUAL && + earmarkMemo.contributor_first_name && + earmarkMemo.contributor_last_name ) { conduit = `${earmarkMemo.contributor_first_name || ''} ${earmarkMemo.contributor_last_name || ''}`; } diff --git a/front-end/src/app/shared/models/transaction-types/PAC_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_JF_TRANSFER_MEMO.model.spec.ts index 880e58b39f..8e3d679a82 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_JF_TRANSFER_MEMO.model.spec.ts @@ -1,31 +1,30 @@ +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { JOINT_FUNDRAISING_TRANSFER } from './JOINT_FUNDRAISING_TRANSFER.model'; -import { PAC_JF_TRANSFER_MEMO } from './PAC_JF_TRANSFER_MEMO.model'; describe('PAC_JF_TRANSFER_MEMO', () => { - let transactionType: PAC_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new PAC_JF_TRANSFER_MEMO(); - transactionType.transaction = transactionType.getNewTransaction(); - transactionType.transaction.parent_transaction = new JOINT_FUNDRAISING_TRANSFER().getNewTransaction(); - (transactionType.transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; + transaction = getTestTransactionByType( + ScheduleATransactionTypes.PAC_JF_TRANSFER_MEMO, + ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER + ) as SchATransaction; + (transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA12'); - expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.PAC_JF_TRANSFER_MEMO); + expect(transaction.form_type).toBe('SA12'); + expect(transaction.transaction_type_identifier).toBe(ScheduleATransactionTypes.PAC_JF_TRANSFER_MEMO); }); - it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe(`Joint Fundraising Memo: Test Org`); + it('#generatePurpotransaction.seDescription() should generate a string', () => { + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Joint Fundraising Memo: Test Org'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PAC_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_JF_TRANSFER_MEMO.model.ts index ac2dd28a49..1b7c0a91ce 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_JF_TRANSFER_MEMO.model.ts @@ -1,25 +1,21 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class PAC_JF_TRANSFER_MEMO extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_JF_TRANSFER_MEMO extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_JF_TRANSFER_MEMO); schema = schema; override navigationControls: TransactionNavigationControls = getChildNavigationControls( LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Joint Fundraising Memo: ${ - (this.transaction?.parent_transaction as SchATransaction).contributor_organization_name + (transaction?.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts index 5ad5eb31d6..b564af39c7 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts index c0bc66e08e..2aef58c586 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_NATIONAL_PARTY_CONVENTION_ACCOUNT diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts index 3fa7fd31bb..3aa93318c9 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts @@ -1,33 +1,31 @@ +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO } from './PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model'; describe('PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO', () => { - let transactionType: PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO(); + transaction = getTestTransactionByType( + ScheduleATransactionTypes.PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO + ) as SchATransaction; + transaction.parent_transaction = { contributor_organization_name: 'Test Org' } as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe( - `Pres. Nominating Convention Account JF Memo: ${ - (transactionType.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name - }` - ); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Pres. Nominating Convention Account JF Memo: Test Org'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts index bc781cbd2e..3c777040b9 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO @@ -20,9 +16,9 @@ export class PAC_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchaTransact LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Pres. Nominating Convention Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name + (transaction.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts index 6fa6dc0f74..e095f82fe7 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('PAC_NATIONAL_PARTY_HEADQUARTERS_BUILDINGS_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts index 02e6af8f85..0b0d121b32 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts index fd57fb0a4f..18c227a1fc 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts @@ -1,33 +1,32 @@ -import { PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO } from './PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { JOINT_FUNDRAISING_TRANSFER } from './JOINT_FUNDRAISING_TRANSFER.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO', () => { - let transactionType: PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO(); - transactionType.transaction = transactionType.getNewTransaction(); - transactionType.transaction.parent_transaction = new JOINT_FUNDRAISING_TRANSFER().getNewTransaction(); - (transactionType.transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; + transaction = getTestTransactionByType( + ScheduleATransactionTypes.PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO, + ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER + ) as SchATransaction; + (transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(`Headquarters Buildings Account JF Memo: Test Org`); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts index 8b31f1c773..79bab0157d 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO @@ -20,9 +16,9 @@ export class PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends SchaTransa LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Headquarters Buildings Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction).contributor_organization_name + (transaction.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts index d6a0f75cd8..d9846431ff 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts index 03fb74db38..0327ade975 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT); schema = schema; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts index ce5f67db32..0195ad4db6 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts @@ -11,21 +11,21 @@ describe('PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO); + const transaction: SchATransaction = transactionType.getNewTransaction(); + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( + ScheduleATransactionTypes.PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO + ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe( - `Recount/Legal Proceedings Account JF Memo: ${ - (transactionType.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name - }` - ); + const transaction: SchATransaction = transactionType.getNewTransaction(); + transaction.parent_transaction = { contributor_organization_name: 'Test Org' } as SchATransaction; + const descrip = transactionType.generatePurposeDescription(transaction); + expect(descrip).toBe('Recount/Legal Proceedings Account JF Memo: Test Org'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts index 6e4d9fb046..03b544f4dc 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO @@ -20,9 +16,9 @@ export class PAC_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchaTransaction LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Recount/Legal Proceedings Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name + (transaction.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/PAC_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_RECEIPT.model.spec.ts index 0518ac72f1..3d1e8771d8 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_RECEIPT.model.spec.ts @@ -1,4 +1,4 @@ -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { PAC_RECEIPT } from './PAC_RECEIPT.model'; @@ -12,7 +12,7 @@ describe('PAC_RECEIPT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PAC_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_RECEIPT.model.ts index 0a9072b7a8..8f6b4538cc 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_RECEIPT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PAC_RECEIPT extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_RECEIPT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_RECEIPT); schema = schema; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; diff --git a/front-end/src/app/shared/models/transaction-types/PAC_RECOUNT_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_RECOUNT_RECEIPT.model.spec.ts index cf765db0d4..d2f5c10cf0 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_RECOUNT_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_RECOUNT_RECEIPT.model.spec.ts @@ -11,7 +11,7 @@ describe('PAC_RECOUNT_RECEIPT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PAC_RECOUNT_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_RECOUNT_RECEIPT.model.ts index f3362e8b84..bbde47e9f1 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_RECOUNT_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_RECOUNT_RECEIPT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_RECOUNT_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PAC_RECOUNT_RECEIPT extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_RECOUNT_RECEIPT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_RECOUNT_RECEIPT); schema = schema; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; diff --git a/front-end/src/app/shared/models/transaction-types/PAC_RETURN.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PAC_RETURN.model.spec.ts index e2a0cbdf63..f2d92cef95 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_RETURN.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_RETURN.model.spec.ts @@ -1,4 +1,4 @@ -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { PAC_RETURN } from './PAC_RETURN.model'; @@ -12,7 +12,7 @@ describe('PAC_RETURN', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { @@ -21,6 +21,6 @@ describe('PAC_RETURN', () => { expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.PAC_RETURN); }); it('#generatePurposeDescription() should not be defined', () => { - expect((transactionType as SchaTransactionType).generatePurposeDescription).toBe(undefined); + expect((transactionType as SchATransactionType).generatePurposeDescription).toBe(undefined); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PAC_RETURN.model.ts b/front-end/src/app/shared/models/transaction-types/PAC_RETURN.model.ts index dc91113305..72ad3deb6c 100644 --- a/front-end/src/app/shared/models/transaction-types/PAC_RETURN.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PAC_RETURN.model.ts @@ -1,18 +1,15 @@ -import { SchaTransactionType } from './SchaTransactionType.model'; -import { - SchATransaction, - ScheduleATransactionTypes, - ScheduleATransactionTypeLabels, - AggregationGroups, -} from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; +import { SchATransaction, ScheduleATransactionTypes, ScheduleATransactionTypeLabels } from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PAC_RETURN'; import { TransactionNavigationControls, STANDARD_CONTROLS } from '../transaction-navigation-controls.model'; -export class PAC_RETURN extends SchaTransactionType { - componentGroupId = 'F'; +export class PAC_RETURN extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PAC_RETURN); schema = schema; + override negativeAmountValueOnly = true; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_MEMO.model.spec.ts index 1ce0a7e8b4..301fc106ae 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_MEMO.model.spec.ts @@ -21,7 +21,6 @@ describe('PARTNERSHIP_MEMO', () => { }); it('#generatePurposeDescription() should generate a string', () => { - transactionType.transaction = transactionType.getNewTransaction(); const descrip = transactionType.generatePurposeDescription(); expect(descrip).toBe('Partnership Attribution'); }); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_MEMO.model.ts index 2791cb4fb6..f196c3e9fc 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_MEMO.model.ts @@ -1,20 +1,15 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { getChildNavigationControls, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class PARTNERSHIP_MEMO extends SchaTransactionType { +export class PARTNERSHIP_MEMO extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTNERSHIP_MEMO); schema = schema; override updateParentOnSave = true; - override subTransactionTypes = [ScheduleATransactionTypes.PARTNERSHIP_MEMO]; override navigationControls: TransactionNavigationControls = getChildNavigationControls( LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTNERSHIP_RECEIPT) ); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts new file mode 100644 index 0000000000..2fc8bcb252 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts @@ -0,0 +1,36 @@ +import { ScheduleATransactionTypes } from '../scha-transaction.model'; +import { PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT } from './PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT.model'; + +describe('PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT', () => { + let transactionType: PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT; + + beforeEach(() => { + transactionType = new PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT(); + }); + + it('should create an instance', () => { + expect(transactionType).toBeTruthy(); + if (transactionType) { + expect(transactionType.scheduleId).toBe('A'); + expect(transactionType.componentGroupId).toBe('D'); + } + }); + + it('#factory() should return a SchATransaction', () => { + const txn = transactionType.getNewTransaction(); + expect(txn.form_type).toBe('SA17'); + expect(txn.transaction_type_identifier).toBe( + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT + ); + }); + + it('#generatePurposeDescription() should generate a string', () => { + const transaction = transactionType.getNewTransaction(); + let descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Pres. Nominating Convention Account (Partnership attributions do not require itemization)'); + + transaction.children = [transactionType.getNewTransaction()]; + descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Pres. Nominating Convention Account (See Partnership Attribution(s) below)'); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts new file mode 100644 index 0000000000..794edac1f9 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts @@ -0,0 +1,35 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_NATIONAL_PARTY_RECEIPTS'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; +import { AggregationGroups } from '../transaction.model'; + +export class PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchATransactionType { + componentGroupId = 'D'; + title = LabelUtils.get( + ScheduleATransactionTypeLabels, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT + ); + schema = schema; + override subTransactionTypes = [ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO]; + + override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; + override purposeDescriptionLabelNotice = + 'If Partnership Receipt is saved without a Partnership Memo, this will read "Partnership attributions do not require itemization". If a Partnership Memo is added, it will read "See Partnership Attribution(s) below".'; + + override generatePurposeDescription(transaction: SchATransaction): string { + if (transaction.children && transaction.children.length > 0) { + return 'Pres. Nominating Convention Account (See Partnership Attribution(s) below)'; + } + return 'Pres. Nominating Convention Account (Partnership attributions do not require itemization)'; + } + + getNewTransaction() { + return SchATransaction.fromJSON({ + form_type: 'SA17', + transaction_type_identifier: ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT, + aggregation_group: AggregationGroups.NATIONAL_PARTY_CONVENTION_ACCOUNT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO.model.spec.ts new file mode 100644 index 0000000000..a992917d9a --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO.model.spec.ts @@ -0,0 +1,29 @@ +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO } from './PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO.model'; + +describe('PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO', () => { + let transactionType: PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO; + + beforeEach(() => { + transactionType = new PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO(); + }); + + it('should create an instance', () => { + expect(transactionType).toBeTruthy(); + expect(transactionType.scheduleId).toBe('A'); + expect(transactionType.componentGroupId).toBe('A'); + }); + + it('#factory() should return a SchATransaction', () => { + const txn: SchATransaction = transactionType.getNewTransaction(); + expect(txn.form_type).toBe('SA17'); + expect(txn.transaction_type_identifier).toBe( + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO + ); + }); + + it('#generatePurposeDescription() should generate a string', () => { + const descrip = transactionType.generatePurposeDescription(); + expect(descrip).toBe('Pres. Nominating Convention Account Partnership Attribution'); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO.model.ts new file mode 100644 index 0000000000..a82879865f --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO.model.ts @@ -0,0 +1,35 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_NATIONAL_PARTY_MEMOS'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { getChildNavigationControls, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; +import { AggregationGroups } from '../transaction.model'; + +export class PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO extends SchATransactionType { + componentGroupId = 'A'; + title = LabelUtils.get( + ScheduleATransactionTypeLabels, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO + ); + schema = schema; + override updateParentOnSave = true; + override navigationControls: TransactionNavigationControls = getChildNavigationControls( + LabelUtils.get( + ScheduleATransactionTypeLabels, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT + ) + ); + + override generatePurposeDescription(): string { + return 'Pres. Nominating Convention Account Partnership Attribution'; + } + + getNewTransaction() { + return SchATransaction.fromJSON({ + form_type: 'SA17', + transaction_type_identifier: ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO, + back_reference_sched_name: 'SA17', + aggregation_group: AggregationGroups.NATIONAL_PARTY_CONVENTION_ACCOUNT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts new file mode 100644 index 0000000000..fc680e834b --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts @@ -0,0 +1,34 @@ +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; + +describe('PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT', () => { + let transaction: SchATransaction; + + beforeEach(() => { + transaction = getTestTransactionByType( + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT + ) as SchATransaction; + }); + + it('should create an instance', () => { + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('D'); + }); + + it('#factory() should return a SchATransaction', () => { + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT + ); + }); + + it('#generatePurposeDescription() should generate a string', () => { + let descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Headquarters Buildings Account (Partnership attributions do not require itemization)'); + + transaction.children = [transaction.transactionType?.getNewTransaction() as SchATransaction]; + descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Headquarters Buildings Account (See Partnership Attribution(s) below)'); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts new file mode 100644 index 0000000000..595c68fcd1 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts @@ -0,0 +1,34 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_NATIONAL_PARTY_RECEIPTS'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; +import { AggregationGroups } from '../transaction.model'; + +export class PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchATransactionType { + componentGroupId = 'D'; + title = LabelUtils.get( + ScheduleATransactionTypeLabels, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT + ); + schema = schema; + override subTransactionTypes = [ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO]; + override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; + override purposeDescriptionLabelNotice = + 'If Partnership Receipt is saved without a Partnership Memo, this will read "Partnership attributions do not require itemization". If a Partnership Memo is added, it will read "See Partnership Attribution(s) below".'; + + override generatePurposeDescription(transaction: SchATransaction): string { + if (transaction.children && transaction.children.length > 0) { + return 'Headquarters Buildings Account (See Partnership Attribution(s) below)'; + } + return 'Headquarters Buildings Account (Partnership attributions do not require itemization)'; + } + + getNewTransaction() { + return SchATransaction.fromJSON({ + form_type: 'SA17', + transaction_type_identifier: ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT, + aggregation_group: AggregationGroups.NATIONAL_PARTY_HEADQUARTERS_ACCOUNT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO.model.spec.ts new file mode 100644 index 0000000000..e973205ff9 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO.model.spec.ts @@ -0,0 +1,29 @@ +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO } from './PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO.model'; + +describe('PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO', () => { + let transactionType: PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO; + + beforeEach(() => { + transactionType = new PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO(); + }); + + it('should create an instance', () => { + expect(transactionType).toBeTruthy(); + expect(transactionType.scheduleId).toBe('A'); + expect(transactionType.componentGroupId).toBe('A'); + }); + + it('#factory() should return a SchATransaction', () => { + const txn: SchATransaction = transactionType.getNewTransaction(); + expect(txn.form_type).toBe('SA17'); + expect(txn.transaction_type_identifier).toBe( + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO + ); + }); + + it('#generatePurposeDescription() should generate a string', () => { + const descrip = transactionType.generatePurposeDescription(); + expect(descrip).toBe(`Headquarters Buildings Account Partnership Attribution`); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO.model.ts new file mode 100644 index 0000000000..de722b306c --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO.model.ts @@ -0,0 +1,35 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_NATIONAL_PARTY_MEMOS'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { getChildNavigationControls, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; +import { AggregationGroups } from '../transaction.model'; + +export class PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO extends SchATransactionType { + componentGroupId = 'A'; + title = LabelUtils.get( + ScheduleATransactionTypeLabels, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO + ); + schema = schema; + override updateParentOnSave = true; + override navigationControls: TransactionNavigationControls = getChildNavigationControls( + LabelUtils.get( + ScheduleATransactionTypeLabels, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT + ) + ); + + override generatePurposeDescription(): string { + return 'Headquarters Buildings Account Partnership Attribution'; + } + + getNewTransaction() { + return SchATransaction.fromJSON({ + form_type: 'SA17', + transaction_type_identifier: ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO, + back_reference_sched_name: 'SA17', + aggregation_group: AggregationGroups.NATIONAL_PARTY_HEADQUARTERS_ACCOUNT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts index b3c4c7f0b8..92fae36530 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts @@ -23,17 +23,18 @@ describe('PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT', () => { }); it('#generatePurposeDescription() should generate expected retval', () => { - const descrip = transactionType.generatePurposeDescription(); + const txn = transactionType.getNewTransaction(); + const descrip = transactionType.generatePurposeDescription(txn); expect(descrip).toBe('Recount/Legal Proceedings Account (Partnership attributions do not require itemization)'); }); it('#generatePurposeDescription() should generate a string', () => { - transactionType.transaction = transactionType.getNewTransaction(); - let descrip = transactionType.generatePurposeDescription(); + const txn = transactionType.getNewTransaction(); + let descrip = transactionType.generatePurposeDescription(txn); expect(descrip).toBe('Recount/Legal Proceedings Account (Partnership attributions do not require itemization)'); - transactionType.transaction.children = [transactionType.getNewTransaction()]; - descrip = transactionType.generatePurposeDescription(); + txn.children = [transactionType.getNewTransaction()]; + descrip = transactionType.generatePurposeDescription(txn); expect(descrip).toBe('Recount/Legal Proceedings Account (See Partnership Attribution(s) below)'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts index 87c6cf2dd5..602e22fc74 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts @@ -1,16 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; -import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_NATIONAL_PARTY_RECEIPTS'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchaTransactionType { +export class PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, @@ -18,15 +13,12 @@ export class PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchaTransactionT ); schema = schema; override subTransactionTypes = [ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO]; - override childTransactionType = TransactionTypeUtils.factory( - ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO - ); override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; override purposeDescriptionLabelNotice = 'If Partnership Receipt is saved without a Partnership Memo, this will read "Partnership attributions do not require itemization". If a Partnership Memo is added, it will read "See Partnership Attribution(s) below".'; - override generatePurposeDescription(): string { - if (this.transaction?.children && this.transaction?.children.length > 0) { + override generatePurposeDescription(transaction: SchATransaction): string { + if (transaction?.children && transaction?.children.length > 0) { return 'Recount/Legal Proceedings Account (See Partnership Attribution(s) below)'; } return 'Recount/Legal Proceedings Account (Partnership attributions do not require itemization)'; diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model.spec.ts index df4facf74c..5a9922bcf1 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model.spec.ts @@ -1,34 +1,32 @@ import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT } from './PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model'; -import { PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO } from './PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO', () => { - let transactionType: PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO(); - transactionType.transaction = transactionType.getNewTransaction(); - transactionType.transaction.parent_transaction = - new PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT().getNewTransaction(); - (transactionType.transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; + transaction = getTestTransactionByType( + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT + ) as SchATransaction; + (transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('A'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('A'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe(`Recount/Legal Proceedings Account Partnership Attribution`); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Recount/Legal Proceedings Account Partnership Attribution'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model.ts index 9226bbcbd2..9c68bebc9a 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_NATIONAL_PARTY_MEMOS'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { getChildNavigationControls, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO extends SchaTransactionType { +export class PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO extends SchATransactionType { componentGroupId = 'A'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECEIPT.model.spec.ts index 62759bba21..d857171986 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECEIPT.model.spec.ts @@ -1,32 +1,32 @@ -import { PARTNERSHIP_RECEIPT } from './PARTNERSHIP_RECEIPT.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; describe('PARTNERSHIP_RECEIPT', () => { - let transactionType: PARTNERSHIP_RECEIPT; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new PARTNERSHIP_RECEIPT(); + transaction = TransactionTypeUtils.factory( + ScheduleATransactionTypes.PARTNERSHIP_RECEIPT + ).getNewTransaction() as SchATransaction; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('D'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('D'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA11AI'); - expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.PARTNERSHIP_RECEIPT); + expect(transaction.form_type).toBe('SA11AI'); + expect(transaction.transaction_type_identifier).toBe(ScheduleATransactionTypes.PARTNERSHIP_RECEIPT); }); it('#generatePurposeDescription() should generate a string', () => { - transactionType.transaction = transactionType.getNewTransaction(); - let descrip = transactionType.generatePurposeDescription(); + let descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe('Partnership attributions do not require itemization'); - transactionType.transaction.children = [transactionType.getNewTransaction()]; - descrip = transactionType.generatePurposeDescription(); + transaction.children = [{ ...transaction } as SchATransaction]; + descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe('See Partnership Attribution(s) below'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECEIPT.model.ts index 99941c3211..95eccee6e2 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECEIPT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class PARTNERSHIP_RECEIPT extends SchaTransactionType { +export class PARTNERSHIP_RECEIPT extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTNERSHIP_RECEIPT); schema = schema; @@ -17,8 +13,8 @@ export class PARTNERSHIP_RECEIPT extends SchaTransactionType { override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; override purposeDescriptionLabelNotice = 'If Partnership Receipt is saved without a Partnership Memo, this will read "Partnership attributions do not require itemization". If a Partnership Memo is added, it will read "See Partnership Attribution(s) below".'; - override generatePurposeDescription(): string { - if (this.transaction?.children && this.transaction?.children.length > 0) { + override generatePurposeDescription(transaction: SchATransaction): string { + if (transaction.children && transaction.children.length > 0) { return 'See Partnership Attribution(s) below'; } return 'Partnership attributions do not require itemization'; diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT.model.spec.ts new file mode 100644 index 0000000000..2cde0af957 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT.model.spec.ts @@ -0,0 +1,34 @@ +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; +import { Transaction } from '../transaction.model'; + +describe('PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT', () => { + let transaction: SchATransaction; + + beforeEach(() => { + transaction = getTestTransactionByType( + ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT + ) as SchATransaction; + }); + + it('should create an instance', () => { + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('D'); + }); + + it('#factory() should return a SchATransaction', () => { + const txn: SchATransaction | undefined = transaction.transactionType?.getNewTransaction() as SchATransaction; + expect(txn?.form_type).toBe('SA17'); + expect(txn?.transaction_type_identifier).toBe(ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT); + }); + + it('#generatePurposeDescription() should generate a string', () => { + let descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Recount Account (Partnership attributions do not require itemization)'); + + transaction.children = [transaction.transactionType?.getNewTransaction() as Transaction]; + descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Recount Account (See Partnership Attribution(s) below)'); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT.model.ts new file mode 100644 index 0000000000..3bfc11aa69 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT.model.ts @@ -0,0 +1,30 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; + +export class PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT extends SchATransactionType { + componentGroupId = 'D'; + title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT); + schema = schema; + override subTransactionTypes = [ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO]; + override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; + override purposeDescriptionLabelNotice = + 'If Partnership Receipt is saved without a Partnership Memo, this will read "Recount Account (Partnership attributions do not require itemization)". If a Partnership Memo is added, it will read "Recount Account (See Partnership Attribution(s) below)".'; + override generatePurposeDescription(transaction: SchATransaction): string { + if (transaction.children && transaction.children.length > 0) { + return 'Recount Account (See Partnership Attribution(s) below)'; + } + return 'Recount Account (Partnership attributions do not require itemization)'; + } + + getNewTransaction() { + return SchATransaction.fromJSON({ + form_type: 'SA17', + transaction_type_identifier: ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT, + aggregation_group: AggregationGroups.RECOUNT_ACCOUNT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO.model.spec.ts new file mode 100644 index 0000000000..bb4d3646c1 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO.model.spec.ts @@ -0,0 +1,27 @@ +import { PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO } from './PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; + +describe('PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO', () => { + let transactionType: PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO; + + beforeEach(() => { + transactionType = new PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO(); + }); + + it('should create an instance', () => { + expect(transactionType).toBeTruthy(); + expect(transactionType.scheduleId).toBe('A'); + expect(transactionType.componentGroupId).toBe('A'); + }); + + it('#factory() should return a SchATransaction', () => { + const txn: SchATransaction = transactionType.getNewTransaction(); + expect(txn.form_type).toBe('SA17'); + expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO); + }); + + it('#generatePurposeDescription() should generate a string', () => { + const descrip = transactionType.generatePurposeDescription(); + expect(descrip).toBe('Recount Account Partnership Attribution'); + }); +}); diff --git a/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO.model.ts new file mode 100644 index 0000000000..72a34d9155 --- /dev/null +++ b/front-end/src/app/shared/models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO.model.ts @@ -0,0 +1,31 @@ +import { LabelUtils } from 'app/shared/utils/label.utils'; +import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { getChildNavigationControls, TransactionNavigationControls } from '../transaction-navigation-controls.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; +import { AggregationGroups } from '../transaction.model'; + +export class PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO extends SchATransactionType { + componentGroupId = 'A'; + title = LabelUtils.get( + ScheduleATransactionTypeLabels, + ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO + ); + schema = schema; + override updateParentOnSave = true; + override navigationControls: TransactionNavigationControls = getChildNavigationControls( + LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO) + ); + + override generatePurposeDescription(): string { + return 'Recount Account Partnership Attribution'; + } + + getNewTransaction() { + return SchATransaction.fromJSON({ + form_type: 'SA17', + transaction_type_identifier: ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO, + aggregation_group: AggregationGroups.RECOUNT_ACCOUNT, + }); + } +} diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTY_JF_TRANSFER_MEMO.model.spec.ts index 269199856e..f9227cbb27 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_JF_TRANSFER_MEMO.model.spec.ts @@ -1,31 +1,32 @@ +import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { JOINT_FUNDRAISING_TRANSFER } from './JOINT_FUNDRAISING_TRANSFER.model'; -import { PARTY_JF_TRANSFER_MEMO } from './PARTY_JF_TRANSFER_MEMO.model'; describe('PARTY_JF_TRANSFER_MEMO', () => { - let transactionType: PARTY_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new PARTY_JF_TRANSFER_MEMO(); - transactionType.transaction = transactionType.getNewTransaction(); - transactionType.transaction.parent_transaction = new JOINT_FUNDRAISING_TRANSFER().getNewTransaction(); - (transactionType.transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; + transaction = TransactionTypeUtils.factory( + ScheduleATransactionTypes.PARTY_JF_TRANSFER_MEMO + ).getNewTransaction() as SchATransaction; + transaction.parent_transaction = TransactionTypeUtils.factory( + ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER + ).getNewTransaction() as SchATransaction; + (transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA12'); - expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.PARTY_JF_TRANSFER_MEMO); + expect(transaction.form_type).toBe('SA12'); + expect(transaction.transaction_type_identifier).toBe(ScheduleATransactionTypes.PARTY_JF_TRANSFER_MEMO); }); it('#generatePurposeDescription() should return appropriate retval', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(`JF Memo: Test Org`); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/PARTY_JF_TRANSFER_MEMO.model.ts index 71410ddca4..47011eef86 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_JF_TRANSFER_MEMO.model.ts @@ -1,24 +1,20 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTY_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class PARTY_JF_TRANSFER_MEMO extends SchaTransactionType { - componentGroupId = 'F'; +export class PARTY_JF_TRANSFER_MEMO extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTY_JF_TRANSFER_MEMO); schema = schema; override navigationControls: TransactionNavigationControls = getChildNavigationControls( LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { - return `JF Memo: ${(this.transaction?.parent_transaction as SchATransaction).contributor_organization_name}`; + override generatePurposeDescription(transaction: SchATransaction): string { + return `JF Memo: ${(transaction.parent_transaction as SchATransaction).contributor_organization_name}`; } getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts index 28dd77415b..3e9e7720c9 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('PARTY_NATIONAL_PARTY_CONVENTION_BUILDINGS_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts index 80445c38e7..d96f4191ed 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchaTransactionType { - componentGroupId = 'F'; +export class PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts index d913621883..c7ffacb451 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('PARTY_NATIONAL_PARTY_HEADQUARTERS_BUILDINGS_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts index f3c53960b0..fdba7e8dce 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchaTransactionType { - componentGroupId = 'F'; +export class PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts index 919a996ef9..ce59e3ec78 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.spec.ts @@ -11,7 +11,7 @@ describe('PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts index 51271c2d3b..ed55b5d33b 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchaTransactionType { - componentGroupId = 'F'; +export class PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTY_RECEIPT.model.spec.ts index 7887514070..3c64bd20d6 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_RECEIPT.model.spec.ts @@ -1,4 +1,4 @@ -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { PARTY_RECEIPT } from './PARTY_RECEIPT.model'; @@ -12,7 +12,7 @@ describe('PARTY_RECEIPT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTY_RECEIPT.model.ts index b71d8e347e..6382551dba 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_RECEIPT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTY_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PARTY_RECEIPT extends SchaTransactionType { - componentGroupId = 'F'; +export class PARTY_RECEIPT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTY_RECEIPT); schema = schema; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_RECOUNT_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTY_RECOUNT_RECEIPT.model.spec.ts index 854aa00641..baa82b837c 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_RECOUNT_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_RECOUNT_RECEIPT.model.spec.ts @@ -11,7 +11,7 @@ describe('PARTY_RECOUNT_RECEIPT', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_RECOUNT_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/PARTY_RECOUNT_RECEIPT.model.ts index 64d1a717e7..3af4a68c50 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_RECOUNT_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_RECOUNT_RECEIPT.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTY_RECOUNT_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PARTY_RECOUNT_RECEIPT extends SchaTransactionType { - componentGroupId = 'F'; +export class PARTY_RECOUNT_RECEIPT extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTY_RECOUNT_RECEIPT); schema = schema; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_RETURN.model.spec.ts b/front-end/src/app/shared/models/transaction-types/PARTY_RETURN.model.spec.ts index b356444c85..bcd4220a70 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_RETURN.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_RETURN.model.spec.ts @@ -1,4 +1,4 @@ -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { PARTY_RETURN } from './PARTY_RETURN.model'; @@ -12,7 +12,7 @@ describe('PARTY_RETURN', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/PARTY_RETURN.model.ts b/front-end/src/app/shared/models/transaction-types/PARTY_RETURN.model.ts index 640a794eb4..d70c02f6c8 100644 --- a/front-end/src/app/shared/models/transaction-types/PARTY_RETURN.model.ts +++ b/front-end/src/app/shared/models/transaction-types/PARTY_RETURN.model.ts @@ -1,18 +1,15 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/PARTY_RETURN'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class PARTY_RETURN extends SchaTransactionType { - componentGroupId = 'F'; +export class PARTY_RETURN extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.PARTY_RETURN); schema = schema; + override negativeAmountValueOnly = true; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/RETURN_RECEIPT.model.spec.ts b/front-end/src/app/shared/models/transaction-types/RETURN_RECEIPT.model.spec.ts index 6b9fec5ce5..2d7f3e7d05 100644 --- a/front-end/src/app/shared/models/transaction-types/RETURN_RECEIPT.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/RETURN_RECEIPT.model.spec.ts @@ -1,4 +1,4 @@ -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { RETURN_RECEIPT } from './RETURN_RECEIPT.model'; diff --git a/front-end/src/app/shared/models/transaction-types/RETURN_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/RETURN_RECEIPT.model.ts index dbd6d9c2aa..6ac9dbbef7 100644 --- a/front-end/src/app/shared/models/transaction-types/RETURN_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/RETURN_RECEIPT.model.ts @@ -1,19 +1,16 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/RETURN_RECEIPT'; import { ContactTypes } from '../contact.model'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class RETURN_RECEIPT extends SchaTransactionType { +export class RETURN_RECEIPT extends SchATransactionType { componentGroupId = 'C'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.RETURNED_BOUNCED_RECEIPT_INDIVIDUAL); schema = schema; + override negativeAmountValueOnly = true; override contactTypeOptions = [ContactTypes.INDIVIDUAL, ContactTypes.ORGANIZATION]; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; diff --git a/front-end/src/app/shared/models/transaction-types/SchaTransactionType.model.ts b/front-end/src/app/shared/models/transaction-types/SchaTransactionType.model.ts deleted file mode 100644 index c4dc70d0aa..0000000000 --- a/front-end/src/app/shared/models/transaction-types/SchaTransactionType.model.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TransactionType } from './transaction-type.model'; -import { SchATransaction } from '../scha-transaction.model'; - -export abstract class SchaTransactionType extends TransactionType { - scheduleId = 'A'; - override transaction?: SchATransaction; - - override generatePurposeDescriptionLabel(): string { - if (this.generatePurposeDescription !== undefined) { - return '(SYSTEM-GENERATED)'; - } else if (this.schema.required.includes('contribution_purpose_descrip')) { - return '(REQUIRED)'; - } - return '(OPTIONAL)'; - } -} diff --git a/front-end/src/app/shared/models/transaction-types/TRANSFER.model.spec.ts b/front-end/src/app/shared/models/transaction-types/TRANSFER.model.spec.ts index 93ebf9bc84..4e4202cae1 100644 --- a/front-end/src/app/shared/models/transaction-types/TRANSFER.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/TRANSFER.model.spec.ts @@ -1,4 +1,4 @@ -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; import { TRANSFER } from './TRANSFER.model'; @@ -12,7 +12,7 @@ describe('TRANSFER', () => { it('should create an instance', () => { expect(transactionType).toBeTruthy(); expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('F'); + expect(transactionType.componentGroupId).toBe('E'); }); it('#factory() should return a SchATransaction', () => { diff --git a/front-end/src/app/shared/models/transaction-types/TRANSFER.model.ts b/front-end/src/app/shared/models/transaction-types/TRANSFER.model.ts index 478d5287e3..88a8ab0a5c 100644 --- a/front-end/src/app/shared/models/transaction-types/TRANSFER.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRANSFER.model.ts @@ -1,16 +1,12 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRANSFER'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class TRANSFER extends SchaTransactionType { - componentGroupId = 'F'; +export class TRANSFER extends SchATransactionType { + componentGroupId = 'E'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.TRANSFER); schema = schema; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model.spec.ts index 310dbc6d86..7a57765187 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model.spec.ts @@ -1,31 +1,30 @@ +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { JOINT_FUNDRAISING_TRANSFER } from './JOINT_FUNDRAISING_TRANSFER.model'; -import { TRIBAL_JF_TRANSFER_MEMO } from './TRIBAL_JF_TRANSFER_MEMO.model'; describe('TRIBAL_JF_TRANSFER_MEMO', () => { - let transactionType: TRIBAL_JF_TRANSFER_MEMO; + let transaction: SchATransaction; beforeEach(() => { - transactionType = new TRIBAL_JF_TRANSFER_MEMO(); - transactionType.transaction = transactionType.getNewTransaction(); - transactionType.transaction.parent_transaction = new JOINT_FUNDRAISING_TRANSFER().getNewTransaction(); - (transactionType.transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; + transaction = getTestTransactionByType( + ScheduleATransactionTypes.TRIBAL_JF_TRANSFER_MEMO, + ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER + ) as SchATransaction; + (transaction.parent_transaction as SchATransaction).contributor_organization_name = 'Test Org'; }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('D'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('D'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA12'); - expect(txn.transaction_type_identifier).toBe(ScheduleATransactionTypes.TRIBAL_JF_TRANSFER_MEMO); + expect(transaction.form_type).toBe('SA12'); + expect(transaction.transaction_type_identifier).toBe(ScheduleATransactionTypes.TRIBAL_JF_TRANSFER_MEMO); }); it('#generatePurposeDescription() should return appropriate retval', () => { - const descrip = transactionType.generatePurposeDescription(); + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe(`JF Memo: Test Org`); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model.ts index b171ce64b0..8df4cd0a97 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class TRIBAL_JF_TRANSFER_MEMO extends SchaTransactionType { +export class TRIBAL_JF_TRANSFER_MEMO extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.TRIBAL_JF_TRANSFER_MEMO); schema = schema; @@ -17,8 +13,8 @@ export class TRIBAL_JF_TRANSFER_MEMO extends SchaTransactionType { LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { - return `JF Memo: ${(this.transaction?.parent_transaction as SchATransaction).contributor_organization_name}`; + override generatePurposeDescription(transaction: SchATransaction): string { + return `JF Memo: ${(transaction.parent_transaction as SchATransaction).contributor_organization_name}`; } getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts index 4a6835801c..ef1b017e8c 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchaTransactionType { +export class TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts index 5c14dfe8aa..4ffc6cdffb 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.spec.ts @@ -1,32 +1,37 @@ import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO } from './TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model'; +import { Transaction } from '../transaction.model'; +import { TransactionTypeUtils } from '../../utils/transaction-type.utils'; describe('TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO', () => { - let transactionType: TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO; + let transaction: Transaction; beforeEach(() => { - transactionType = new TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO(); + transaction = TransactionTypeUtils.factory( + ScheduleATransactionTypes.TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO + ).getNewTransaction(); }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('D'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('D'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); + transaction.parent_transaction = { + contributor_organization_name: 'ABC', + } as SchATransaction; + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); expect(descrip).toBe( `Pres. Nominating Convention Account JF Memo: ${ - (transactionType.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name + (transaction.parent_transaction as SchATransaction)?.contributor_organization_name }` ); }); diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts index 45f52a0c0c..24e9590cc4 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchaTransactionType { +export class TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, @@ -20,9 +16,9 @@ export class TRIBAL_NATIONAL_PARTY_CONVENTION_JF_TRANSFER_MEMO extends SchaTrans LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Pres. Nominating Convention Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name + (transaction.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts index b1089870cd..6d1016f16d 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class TRIBAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchaTransactionType { +export class TRIBAL_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts index 37b43ff8c4..c458bccedd 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.spec.ts @@ -1,33 +1,30 @@ import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO } from './TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model'; +import { Transaction } from '../transaction.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO', () => { - let transactionType: TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO; - - beforeEach(() => { - transactionType = new TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO(); - }); + const transaction: Transaction = getTestTransactionByType( + ScheduleATransactionTypes.TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO + ); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('D'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('D'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( + expect(transaction.form_type).toBe('SA17'); + expect(transaction.transaction_type_identifier).toBe( ScheduleATransactionTypes.TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO ); }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe( - `Headquarters Buildings Account JF Memo: ${ - (transactionType.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name - }` - ); + transaction.parent_transaction = { + contributor_organization_name: 'ABC', + } as SchATransaction; + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Headquarters Buildings Account JF Memo: ABC'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts index 1cf58b3fb8..d1f176a300 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends SchaTransactionType { +export class TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, @@ -20,9 +16,9 @@ export class TRIBAL_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO extends SchaTra LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Headquarters Buildings Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name + (transaction.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts index d274967615..cf5085446e 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_ACCOUNT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_NATIONAL_PARTY_RECOUNT_ACCOUNT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class TRIBAL_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchaTransactionType { +export class TRIBAL_NATIONAL_PARTY_RECOUNT_ACCOUNT extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts index 0cf5805476..538a4c81a9 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.spec.ts @@ -1,33 +1,36 @@ import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO } from './TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model'; +import { Transaction } from '../transaction.model'; +import { getTestTransactionByType } from 'app/shared/utils/unit-test.utils'; describe('TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO', () => { - let transactionType: TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO; + let transaction: Transaction; beforeEach(() => { - transactionType = new TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO(); + transaction = getTestTransactionByType(ScheduleATransactionTypes.TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO); }); it('should create an instance', () => { - expect(transactionType).toBeTruthy(); - expect(transactionType.scheduleId).toBe('A'); - expect(transactionType.componentGroupId).toBe('D'); + expect(transaction.transactionType).toBeTruthy(); + expect(transaction.transactionType?.scheduleId).toBe('A'); + expect(transaction.transactionType?.componentGroupId).toBe('D'); }); it('#factory() should return a SchATransaction', () => { - const txn: SchATransaction = transactionType.getNewTransaction(); - expect(txn.form_type).toBe('SA17'); - expect(txn.transaction_type_identifier).toBe( - ScheduleATransactionTypes.TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO - ); + const txn: Transaction | undefined = transaction.transactionType?.getNewTransaction(); + expect(txn).toBeTruthy(); + if (txn) { + expect(txn.form_type).toBe('SA17'); + expect(txn.transaction_type_identifier).toBe( + ScheduleATransactionTypes.TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO + ); + } }); it('#generatePurposeDescription() should generate a string', () => { - const descrip = transactionType.generatePurposeDescription(); - expect(descrip).toBe( - `Recount/Legal Proceedings Account JF Memo: ${ - (transactionType.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name - }` - ); + transaction.parent_transaction = { + contributor_organization_name: 'ABC', + } as SchATransaction; + const descrip = transaction.transactionType?.generatePurposeDescription?.(transaction); + expect(descrip).toBe('Recount/Legal Proceedings Account JF Memo: ABC'); }); }); diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts index 34dec3a603..556b7c8b50 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { TransactionNavigationControls, getChildNavigationControls } from '../transaction-navigation-controls.model'; -export class TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchaTransactionType { +export class TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, @@ -20,9 +16,9 @@ export class TRIBAL_NATIONAL_PARTY_RECOUNT_JF_TRANSFER_MEMO extends SchaTransact LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER) ); - override generatePurposeDescription(): string { + override generatePurposeDescription(transaction: SchATransaction): string { return `Recount/Legal Proceedings Account JF Memo: ${ - (this.transaction?.parent_transaction as SchATransaction)?.contributor_organization_name + (transaction.parent_transaction as SchATransaction).contributor_organization_name }`; } diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_RECEIPT.model.ts index 03e5fdc007..25f749f041 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_RECEIPT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class TRIBAL_RECEIPT extends SchaTransactionType { +export class TRIBAL_RECEIPT extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.TRIBAL_RECEIPT); schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/TRIBAL_RECOUNT_RECEIPT.model.ts b/front-end/src/app/shared/models/transaction-types/TRIBAL_RECOUNT_RECEIPT.model.ts index 8675e16f47..291ddc6ab2 100644 --- a/front-end/src/app/shared/models/transaction-types/TRIBAL_RECOUNT_RECEIPT.model.ts +++ b/front-end/src/app/shared/models/transaction-types/TRIBAL_RECOUNT_RECEIPT.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_RECOUNT_RECEIPT'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -export class TRIBAL_RECOUNT_RECEIPT extends SchaTransactionType { +export class TRIBAL_RECOUNT_RECEIPT extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.TRIBAL_RECOUNT_RECEIPT); schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON.model.spec.ts b/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON.model.spec.ts index 704859c11a..cd7ac16b83 100644 --- a/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON.model.spec.ts @@ -1,6 +1,6 @@ import { UNREGISTERED_RECEIPT_FROM_PERSON } from './UNREGISTERED_RECEIPT_FROM_PERSON.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; describe('UNREGISTERED_RECEIPT_FROM_PERSON', () => { let transactionType: UNREGISTERED_RECEIPT_FROM_PERSON; diff --git a/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON.model.ts b/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON.model.ts index 4a7cfa3c5e..816a1546e3 100644 --- a/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON.model.ts +++ b/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON.model.ts @@ -1,15 +1,11 @@ import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/UNREGISTERED_RECEIPT_FROM_PERSON'; -import { - AggregationGroups, - SchATransaction, - ScheduleATransactionTypeLabels, - ScheduleATransactionTypes, -} from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; +import { SchATransaction, ScheduleATransactionTypeLabels, ScheduleATransactionTypes } from '../scha-transaction.model'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class UNREGISTERED_RECEIPT_FROM_PERSON extends SchaTransactionType { +export class UNREGISTERED_RECEIPT_FROM_PERSON extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get(ScheduleATransactionTypeLabels, ScheduleATransactionTypes.UNREGISTERED_RECEIPT_FROM_PERSON); schema = schema; diff --git a/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model.spec.ts b/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model.spec.ts index e680360210..d312a40f54 100644 --- a/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model.spec.ts +++ b/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model.spec.ts @@ -1,6 +1,6 @@ import { UNREGISTERED_RECEIPT_FROM_PERSON_RETURN } from './UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model'; import { SchATransaction, ScheduleATransactionTypes } from '../scha-transaction.model'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; +import { TransactionType } from 'app/shared/models/transaction-type.model'; describe('UNREGISTERED_RECEIPT_FROM_PERSON_RETURN', () => { let transactionType: UNREGISTERED_RECEIPT_FROM_PERSON_RETURN; diff --git a/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model.ts b/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model.ts index 7793129e57..6375610e3b 100644 --- a/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model.ts +++ b/front-end/src/app/shared/models/transaction-types/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN.model.ts @@ -1,21 +1,18 @@ -import { - SchATransaction, - ScheduleATransactionTypes, - ScheduleATransactionTypeLabels, - AggregationGroups, -} from '../scha-transaction.model'; +import { SchATransaction, ScheduleATransactionTypes, ScheduleATransactionTypeLabels } from '../scha-transaction.model'; +import { AggregationGroups } from '../transaction.model'; import { LabelUtils } from 'app/shared/utils/label.utils'; import { schema } from 'fecfile-validate/fecfile_validate_js/dist/UNREGISTERED_RECEIPT_FROM_PERSON_RETURN'; import { STANDARD_CONTROLS, TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { SchaTransactionType } from './SchaTransactionType.model'; +import { SchATransactionType } from '../scha-transaction-type.model'; -export class UNREGISTERED_RECEIPT_FROM_PERSON_RETURN extends SchaTransactionType { +export class UNREGISTERED_RECEIPT_FROM_PERSON_RETURN extends SchATransactionType { componentGroupId = 'D'; title = LabelUtils.get( ScheduleATransactionTypeLabels, ScheduleATransactionTypes.UNREGISTERED_RECEIPT_FROM_PERSON_RETURN ); schema = schema; + override negativeAmountValueOnly = true; override navigationControls: TransactionNavigationControls = STANDARD_CONTROLS; getNewTransaction() { diff --git a/front-end/src/app/shared/models/transaction-types/transaction-type.model.spec.ts b/front-end/src/app/shared/models/transaction-types/transaction-type.model.spec.ts deleted file mode 100644 index 9020a16370..0000000000 --- a/front-end/src/app/shared/models/transaction-types/transaction-type.model.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { EARMARK_RECEIPT_RECOUNT_ACCOUNT } from './EARMARK_RECEIPT_RECOUNT_ACCOUNT.model'; - -describe('Transaction Type Model', () => { - it('#generatePurposeDescriptionWrapper() should not truncate short purpose descriptions', () => { - const transactionType = new EARMARK_RECEIPT_RECOUNT_ACCOUNT(); - const spy = spyOn(transactionType, 'generatePurposeDescription'); - spy.and.returnValue('A short response'); - - const originalDescrip = transactionType.generatePurposeDescription?.(); - const modifiedDescrip = transactionType.generatePurposeDescriptionWrapper(); - expect(originalDescrip).toEqual(modifiedDescrip); - }); - - it('#generatePurposeDescriptionWrapper() should not truncate short purpose descriptions', () => { - const transactionType = new EARMARK_RECEIPT_RECOUNT_ACCOUNT(); - const spy = spyOn(transactionType, 'generatePurposeDescription'); - spy.and.returnValue( - 'An absurdly long response' + - 'Just the biggest; no corners cut.' + - 'It needs to be at least 100 chars.' + - 'This should probably get it done.' - ); - - const originalDescrip = transactionType.generatePurposeDescription?.(); - const modifiedDescrip = transactionType.generatePurposeDescriptionWrapper(); - expect(originalDescrip).not.toEqual(modifiedDescrip); - expect(modifiedDescrip.length).toEqual(100); - }); -}); diff --git a/front-end/src/app/shared/models/transaction-types/transaction-type.model.ts b/front-end/src/app/shared/models/transaction-types/transaction-type.model.ts deleted file mode 100644 index 657be2e6e7..0000000000 --- a/front-end/src/app/shared/models/transaction-types/transaction-type.model.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { TransactionNavigationControls } from '../transaction-navigation-controls.model'; -import { JsonSchema } from '../../interfaces/json-schema.interface'; -import { ContactType } from '../contact.model'; -import { Transaction, ScheduleTransactionTypes } from '../transaction.model'; - -/** - * Class that defines the meta data associated with a transaction type. - * Populated and used by the transaction resovler for use in the transaction components. - */ -export abstract class TransactionType { - abstract scheduleId: string; - abstract componentGroupId: string; // Identifier of transaction component use to render UI form entry page - abstract title: string; - abstract schema: JsonSchema; // FEC validation JSON schema - isDependentChild = false; // When set to true, the parent transaction of the transaction is used to generate UI form entry page - updateParentOnSave = false; // Set to true when the parent transaction may be affected by a change in the transaction - contactTypeOptions?: ContactType[]; - transaction?: Transaction; - childTransactionType?: TransactionType; - subTransactionTypes?: ScheduleTransactionTypes[]; // TransactionTypes displayed in dropdown to choose from when creating a child transaction - navigationControls?: TransactionNavigationControls; - generatePurposeDescription?(): string; // Dynamically generates the text in the CPD or EPD field - generatePurposeDescriptionLabel?(): string; // Get the CPD or EPD field label - purposeDescriptionLabelNotice?: string; // Additional italicized text that appears beneath the form input label - abstract getNewTransaction(): Transaction; // Factory method to create a new Transaction object with default property values for this transaction type - - getSchemaName(): string { - const schema_name = this?.schema?.$id?.split('/').pop()?.split('.')[0]; - if (!schema_name) { - throw new Error('Schema name for transaction type not found.'); - } - return schema_name; - } - - public generatePurposeDescriptionWrapper(): string { - const purpose = this.generatePurposeDescription?.(); - if (purpose) { - if (purpose.length > 100) { - return purpose.slice(0, 97) + '...'; - } - return purpose; - } - return ''; - } -} diff --git a/front-end/src/app/shared/models/transaction.model.spec.ts b/front-end/src/app/shared/models/transaction.model.spec.ts index 5d98d04559..76cd87dead 100644 --- a/front-end/src/app/shared/models/transaction.model.spec.ts +++ b/front-end/src/app/shared/models/transaction.model.spec.ts @@ -1,4 +1,5 @@ -import { SchATransaction } from './scha-transaction.model'; +import { getTestTransactionByType } from '../utils/unit-test.utils'; +import { SchATransaction, ScheduleATransactionTypes } from './scha-transaction.model'; import { Transaction } from './transaction.model'; describe('Transaction', () => { @@ -6,10 +7,17 @@ describe('Transaction', () => { // Must extend the abstract class to instantiate it class ChildTransaction extends Transaction { apiEndpoint = '/sch-x-transactions'; - getUpdatedParent() { - return new SchATransaction(); - } } expect(new ChildTransaction()).toBeTruthy(); }); + + it('should update child purpose descriptions', () => { + const testTransaction = getTestTransactionByType( + ScheduleATransactionTypes.PARTNERSHIP_MEMO, + ScheduleATransactionTypes.PARTNERSHIP_RECEIPT + ) as SchATransaction; + + const payload = testTransaction.getUpdatedParent(); + expect(payload.transaction_type_identifier).toBe(ScheduleATransactionTypes.PARTNERSHIP_RECEIPT); + }); }); diff --git a/front-end/src/app/shared/models/transaction.model.ts b/front-end/src/app/shared/models/transaction.model.ts index 9955fa5ac0..306d69d150 100644 --- a/front-end/src/app/shared/models/transaction.model.ts +++ b/front-end/src/app/shared/models/transaction.model.ts @@ -1,14 +1,16 @@ import { BaseModel } from './base.model'; import { Contact } from './contact.model'; import { MemoText } from './memo-text.model'; -import { SchATransaction, ScheduleATransactionTypes } from './scha-transaction.model'; -import { SchBTransaction, ScheduleBTransactionTypes } from './schb-transaction.model'; -import { ValidateUtils } from '../utils/validate.utils'; -import { TransactionType } from './transaction-types/transaction-type.model'; +import { SchATransaction, ScheduleATransactionTypes, ScheduleATransactionGroupsType } from './scha-transaction.model'; +import { SchBTransaction, ScheduleBTransactionTypes, ScheduleBTransactionGroupsType } from './schb-transaction.model'; +import { TransactionType } from './transaction-type.model'; import { Type } from 'class-transformer'; +import { ValidateUtils } from '../utils/validate.utils'; export abstract class Transaction extends BaseModel { id: string | undefined; + + @Type(() => TransactionType) transactionType: TransactionType | undefined; // FECFile spec properties @@ -50,8 +52,6 @@ export abstract class Transaction extends BaseModel { abstract apiEndpoint: string; // Root URL for API endpoint - abstract getUpdatedParent(childDeleted?: boolean): Transaction; // Method to handle save when child must update parent properties - /** * Perform bookkeeping updates to the transaction when it is created via fromJSON() * We have to pass the transactionType instead of getting from TransactonTypeUtils @@ -61,15 +61,71 @@ export abstract class Transaction extends BaseModel { */ setMetaProperties(transactionType: TransactionType): void { this.contact_id = this.contact?.id; - if (this?.transaction_type_identifier) { - this.transactionType = transactionType; - this.schema_name = transactionType.getSchemaName(); - const fieldsToValidate: string[] = ValidateUtils.getSchemaProperties(transactionType.schema); - const fieldsNotToValidate: string[] = this.getFieldsNotToValidate(); - this.fields_to_validate = fieldsToValidate.filter((p) => ![...fieldsNotToValidate].includes(p)); - } else { - throw new Error('No TRANSACTION_TYPE_IDENTIFIER found when setting Meta Properties'); + this.transactionType = transactionType; + this.schema_name = transactionType.getSchemaName(); + const fieldsToValidate: string[] = ValidateUtils.getSchemaProperties(transactionType.schema); + const fieldsNotToValidate: string[] = this.getFieldsNotToValidate(); + this.fields_to_validate = fieldsToValidate.filter((p) => ![...fieldsNotToValidate].includes(p)); + } + + /** + * updateChildren() + * @returns + * An array of Transaction objects whose contribution_purpose_descriptions + * have been re-generated to account for changes to their parent + * + */ + updateChildren(): Transaction[] { + const outChildren: Transaction[] = []; + if (this.children) { + for (const child of this.children as SchATransaction[]) { + // Modify the purpose description this to reflect the changes to child transactions + if (child?.transactionType?.generatePurposeDescription) { + child.parent_transaction = this; + const newDescrip = child.transactionType.generatePurposeDescriptionWrapper(child); + const key = child.transactionType.templateMap.purpose_description as keyof ScheduleTransaction; + ((child as ScheduleTransaction)[key] as string) = newDescrip; + } + outChildren.push(child); + } } + return outChildren; + } + + /** + * Returns a transaction payload with the parent of the original payload + * swapped in as the main payload and the original main payload is a child + * @returns + */ + getUpdatedParent(childDeleted = false): Transaction { + if (!this.parent_transaction?.transaction_type_identifier) { + throw new Error( + `Fecfile: Child transaction '${this.transaction_type_identifier}' is missing its parent when saving to API` + ); + } + + // The parent is the new payload + const payload = this.parent_transaction; + if (!payload.children) payload.children = []; + + // Attach the original payload to the parent as a child, replacing an + // existing version if needed + if (this.id) { + payload.children = payload.children.filter((c) => c.id !== this.id); + } + if (!childDeleted) { + payload.children.push(this); + } + payload.children = payload.updateChildren(); + + // Update the purpose description + if (payload.transactionType?.generatePurposeDescription) { + const key = payload.transactionType.templateMap.purpose_description as keyof ScheduleTransaction; + ((payload as ScheduleTransaction)[key] as string) = + payload.transactionType.generatePurposeDescriptionWrapper(payload); + } + + return payload; } } @@ -81,4 +137,18 @@ export function hasNoContact(transaction?: Transaction): boolean { } export type ScheduleTransaction = SchATransaction | SchBTransaction; -export type ScheduleTransactionTypes = ScheduleATransactionTypes | ScheduleBTransactionTypes; +export type TransactionTypes = ScheduleATransactionTypes | ScheduleBTransactionTypes; +export type TransactionGroupTypes = ScheduleATransactionGroupsType | ScheduleBTransactionGroupsType; + +export enum AggregationGroups { + GENERAL = 'GENERAL', + LINE_15 = 'LINE_15', + LINE_16 = 'LINE_16', + NATIONAL_PARTY_CONVENTION_ACCOUNT = 'NATIONAL_PARTY_CONVENTION_ACCOUNT', + NATIONAL_PARTY_HEADQUARTERS_ACCOUNT = 'NATIONAL_PARTY_HEADQUARTERS_ACCOUNT', + NATIONAL_PARTY_RECOUNT_ACCOUNT = 'NATIONAL_PARTY_RECOUNT_ACCOUNT', + NON_CONTRIBUTION_ACCOUNT = 'NON_CONTRIBUTION_ACCOUNT', + OTHER_RECEIPTS = 'OTHER_RECEIPTS', + RECOUNT_ACCOUNT = 'RECOUNT_ACCOUNT', + GENERAL_DISBURSEMENT = 'GENERAL_DISBURSEMENT', +} diff --git a/front-end/src/app/shared/resolvers/transaction-type.resolver.ts b/front-end/src/app/shared/resolvers/transaction-type.resolver.ts deleted file mode 100644 index 68d18394a8..0000000000 --- a/front-end/src/app/shared/resolvers/transaction-type.resolver.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; -import { map, mergeMap, Observable, of } from 'rxjs'; -import { TransactionType } from '../models/transaction-types/transaction-type.model'; -import { Transaction } from '../models/transaction.model'; -import { Contact } from '../models/contact.model'; -import { ContactService } from '../services/contact.service'; -import { TransactionService } from '../services/transaction.service'; -import { TransactionTypeUtils } from '../utils/transaction-type.utils'; - -@Injectable({ - providedIn: 'root', -}) -export class TransactionTypeResolver implements Resolve { - constructor(private transactionService: TransactionService, private contactService: ContactService) {} - - resolve(route: ActivatedRouteSnapshot): Observable { - const reportId = route.paramMap.get('reportId'); - const transactionTypeName = route.paramMap.get('transactionType'); - const transactionId = route.paramMap.get('transactionId'); - const parentTransactionId = route.paramMap.get('parentTransactionId'); - - if (transactionId) { - return this.resolve_existing_transaction(transactionId); - } - if (parentTransactionId && transactionTypeName) { - return this.resolve_new_child_transaction(parentTransactionId, transactionTypeName); - } - if (reportId && transactionTypeName) { - return this.resolve_new_transaction(reportId, transactionTypeName); - } - return of(undefined); - } - - resolve_new_transaction(reportId: string, transactionTypeName: string): Observable { - const transactionType = TransactionTypeUtils.factory(transactionTypeName) as TransactionType; - transactionType.transaction = transactionType.getNewTransaction(); - transactionType.transaction.report_id = String(reportId); - - if (transactionType.childTransactionType) { - transactionType.childTransactionType.transaction = transactionType.childTransactionType.getNewTransaction(); - } - - return of(transactionType); - } - - resolve_new_child_transaction( - parentTransactionId: string, - transactionTypeName: string - ): Observable { - const transactionType = TransactionTypeUtils.factory(transactionTypeName) as TransactionType; - return this.transactionService.get(String(parentTransactionId)).pipe( - map((transaction: Transaction) => { - transactionType.transaction = transactionType.getNewTransaction(); - - transactionType.transaction.parent_transaction = transaction; - transactionType.transaction.parent_transaction_id = String(parentTransactionId); - transactionType.transaction.report_id = String(transaction.report_id); - - return transactionType; - }) - ); - } - - resolve_existing_transaction(transactionId: string): Observable { - function buildTransactionType(transaction: Transaction): TransactionType | undefined { - if (transaction.transaction_type_identifier && transaction.contact) { - const transactionType: TransactionType = TransactionTypeUtils.factory( - transaction.transaction_type_identifier - ) as TransactionType; - transactionType.transaction = transaction; - transactionType.transaction.contact = Contact.fromJSON(transaction.contact); - if (transaction.children?.length) { - transactionType.childTransactionType = buildTransactionType(transaction.children[0]); - } - return transactionType; - } - return undefined; - } - - return this.transactionService.get(String(transactionId)).pipe( - mergeMap((transaction: Transaction) => { - if (transaction.transaction_type_identifier && transaction.contact) { - const transactionType = TransactionTypeUtils.factory( - transaction.transaction_type_identifier - ) as TransactionType; - - // Determine if we need to get the parent transaction as the - // transaction type requested is a dependent transaction and cannot - // be modified directly in a UI form. (e.g. EARMARK_MEMO) - if (transactionType.isDependentChild) { - // Get parent transaction to ensure we have a full list of children - if (transaction?.parent_transaction?.id) { - return this.transactionService.get(transaction.parent_transaction.id).pipe( - map((transaction: Transaction) => { - return buildTransactionType(transaction); - }) - ); - } else { - throw new Error( - `Transaction ${transaction.id} (${transaction.transaction_type_identifier}) is a dependent transaction type but does not have a parent transaction.` - ); - } - } else { - return of(buildTransactionType(transaction)); - } - } - throw new Error( - `Transaction type resolver can't find transaction and/or contact for transaction ID ${transactionId}` - ); - }) - ); - } -} diff --git a/front-end/src/app/shared/resolvers/transaction-type.resolver.spec.ts b/front-end/src/app/shared/resolvers/transaction.resolver.spec.ts similarity index 82% rename from front-end/src/app/shared/resolvers/transaction-type.resolver.spec.ts rename to front-end/src/app/shared/resolvers/transaction.resolver.spec.ts index 8e59348dd5..6e9c7d6b62 100644 --- a/front-end/src/app/shared/resolvers/transaction-type.resolver.spec.ts +++ b/front-end/src/app/shared/resolvers/transaction.resolver.spec.ts @@ -3,16 +3,16 @@ import { TestBed } from '@angular/core/testing'; import { ActivatedRouteSnapshot, convertToParamMap } from '@angular/router'; import { provideMockStore } from '@ngrx/store/testing'; import { of } from 'rxjs'; -import { TransactionType } from '../models/transaction-types/transaction-type.model'; +import { Transaction } from '../models/transaction.model'; import { Contact } from '../models/contact.model'; import { SchATransaction } from '../models/scha-transaction.model'; import { ContactService } from '../services/contact.service'; import { TransactionService } from '../services/transaction.service'; import { testMockStore } from '../utils/unit-test.utils'; -import { TransactionTypeResolver } from './transaction-type.resolver'; +import { TransactionResolver } from './transaction.resolver'; describe('TransactionResolver', () => { - let resolver: TransactionTypeResolver; + let resolver: TransactionResolver; let testContactService: ContactService; beforeEach(() => { @@ -36,7 +36,7 @@ describe('TransactionResolver', () => { }, ], }); - resolver = TestBed.inject(TransactionTypeResolver); + resolver = TestBed.inject(TransactionResolver); testContactService = TestBed.inject(ContactService); }); @@ -53,10 +53,10 @@ describe('TransactionResolver', () => { testContact.id = 'testId'; spyOn(testContactService, 'get').and.returnValue(of(testContact)); - resolver.resolve(route as ActivatedRouteSnapshot).subscribe((response: TransactionType | undefined) => { + resolver.resolve(route as ActivatedRouteSnapshot).subscribe((response: Transaction | undefined) => { expect(of(response)).toBeTruthy(); if (response) { - expect('Offsets to Operating Expenditures').toEqual(response.title); + expect('Offsets to Operating Expenditures').toEqual(response.transactionType?.title || ''); } }); }); @@ -66,7 +66,7 @@ describe('TransactionResolver', () => { paramMap: convertToParamMap({ transactionId: undefined }), }; - resolver.resolve(route as ActivatedRouteSnapshot).subscribe((response: TransactionType | undefined) => { + resolver.resolve(route as ActivatedRouteSnapshot).subscribe((response: Transaction | undefined) => { expect(response).toEqual(undefined); }); }); @@ -76,10 +76,10 @@ describe('TransactionResolver', () => { paramMap: convertToParamMap({ reportId: 1, transactionType: 'OFFSET_TO_OPERATING_EXPENDITURES' }), }; - resolver.resolve(route as ActivatedRouteSnapshot).subscribe((response: TransactionType | undefined) => { + resolver.resolve(route as ActivatedRouteSnapshot).subscribe((response: Transaction | undefined) => { expect(response).toBeTruthy(); if (response) { - expect(response.title).toEqual('Offsets to Operating Expenditures'); + expect(response.transactionType?.title).toEqual('Offsets to Operating Expenditures'); } }); }); @@ -89,10 +89,10 @@ describe('TransactionResolver', () => { paramMap: convertToParamMap({ parentTransactionId: 1, transactionType: 'PAC_JF_TRANSFER_MEMO' }), }; - resolver.resolve(route as ActivatedRouteSnapshot).subscribe((response: TransactionType | undefined) => { + resolver.resolve(route as ActivatedRouteSnapshot).subscribe((response: Transaction | undefined) => { expect(response).toBeTruthy(); if (response) { - expect(response.title).toEqual('PAC Joint Fundraising Transfer Memo'); + expect(response.transactionType?.title).toEqual('PAC Joint Fundraising Transfer Memo'); } }); }); diff --git a/front-end/src/app/shared/resolvers/transaction.resolver.ts b/front-end/src/app/shared/resolvers/transaction.resolver.ts new file mode 100644 index 0000000000..d3d58df943 --- /dev/null +++ b/front-end/src/app/shared/resolvers/transaction.resolver.ts @@ -0,0 +1,87 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { map, Observable, of, mergeMap } from 'rxjs'; +import { Transaction } from '../models/transaction.model'; +import { ContactService } from '../services/contact.service'; +import { TransactionService } from '../services/transaction.service'; +import { TransactionTypeUtils } from '../utils/transaction-type.utils'; + +@Injectable({ + providedIn: 'root', +}) +export class TransactionResolver implements Resolve { + constructor(private transactionService: TransactionService, private contactService: ContactService) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + const reportId = route.paramMap.get('reportId'); + const transactionTypeName = route.paramMap.get('transactionType'); + const transactionId = route.paramMap.get('transactionId'); + const parentTransactionId = route.paramMap.get('parentTransactionId'); + + if (transactionId) { + return this.resolve_existing_transaction(transactionId); + } + if (parentTransactionId && transactionTypeName) { + return this.resolve_new_child_transaction(parentTransactionId, transactionTypeName); + } + if (reportId && transactionTypeName) { + return this.resolve_new_transaction(reportId, transactionTypeName); + } + return of(undefined); + } + + resolve_new_transaction(reportId: string, transactionTypeName: string): Observable { + const transactionType = TransactionTypeUtils.factory(transactionTypeName); + const transaction: Transaction = transactionType.getNewTransaction(); + transaction.report_id = String(reportId); + + if (transactionType.dependentChildTransactionType) { + transaction.children = [transactionType.dependentChildTransactionType.getNewTransaction()]; + } + + return of(transaction); + } + + resolve_new_child_transaction( + parentTransactionId: string, + childTransactionTypeName: string + ): Observable { + return this.transactionService.get(String(parentTransactionId)).pipe( + map((parentTransaction: Transaction) => { + const childTransactionType = TransactionTypeUtils.factory(childTransactionTypeName); + const childTransaction = childTransactionType.getNewTransaction(); + childTransaction.parent_transaction = parentTransaction; + childTransaction.parent_transaction_id = String(parentTransactionId); + childTransaction.report_id = String(parentTransaction.report_id); + return childTransaction; + }) + ); + } + + resolve_existing_transaction(transactionId: string): Observable { + return this.transactionService.get(String(transactionId)).pipe( + mergeMap((transaction: Transaction) => { + if (transaction.transaction_type_identifier && transaction.contact) { + // Determine if we need to get the parent transaction as the + // transaction type requested is a dependent transaction and cannot + // be modified directly in a UI form. (e.g. EARMARK_MEMO) + if (transaction.transactionType && transaction.transactionType.isDependentChild) { + // Get parent transaction to ensure we have a full list of children + if (transaction?.parent_transaction?.id) { + return this.transactionService.get(transaction.parent_transaction.id); + } else { + throw new Error( + `Fecfile: Transaction ${transaction.id} (${transaction.transaction_type_identifier}) is a dependent transaction type but does not have a parent transaction.` + ); + } + } else { + return of(transaction); + } + } + throw new Error( + `Fecfile: Transaction type resolver can't find transaction and/or contact for transaction ID ${transactionId}` + ); + }) + ); + } +} diff --git a/front-end/src/app/shared/services/committee-account.service.ts b/front-end/src/app/shared/services/committee-account.service.ts index 0938a8c961..4e50d0532d 100644 --- a/front-end/src/app/shared/services/committee-account.service.ts +++ b/front-end/src/app/shared/services/committee-account.service.ts @@ -20,7 +20,7 @@ export class CommitteeAccountService { const userLoginData$ = this.store.select(selectUserLoginData); return userLoginData$.pipe( mergeMap((userLoginData) => { - return this.fecApiService.getDetails(userLoginData.committee_id); + return this.fecApiService.getCommitteeDetails(userLoginData.committee_id); }) ); } diff --git a/front-end/src/app/shared/services/contact.service.spec.ts b/front-end/src/app/shared/services/contact.service.spec.ts index 7517f8cde1..5130281c65 100644 --- a/front-end/src/app/shared/services/contact.service.spec.ts +++ b/front-end/src/app/shared/services/contact.service.spec.ts @@ -3,7 +3,8 @@ import { TestBed } from '@angular/core/testing'; import { provideMockStore } from '@ngrx/store/testing'; import { of } from 'rxjs'; import { environment } from '../../../environments/environment'; -import { CommitteeLookupResponse, Contact, IndividualLookupResponse, OrganizationLookupResponse } from '../models/contact.model'; +import { JsonSchema } from '../interfaces/json-schema.interface'; +import { CandidateLookupResponse, CommitteeLookupResponse, Contact, ContactTypes, IndividualLookupResponse, OrganizationLookupResponse } from '../models/contact.model'; import { ListRestResponse } from '../models/rest-api.model'; import { testMockStore } from '../utils/unit-test.utils'; import { ApiService } from './api.service'; @@ -98,6 +99,26 @@ describe('ContactService', () => { httpTestingController.verify(); }); + it('#candidateLookup() happy path', () => { + const expectedRetval = new CandidateLookupResponse(); + const apiServiceGetSpy = spyOn(testApiService, 'get').and.returnValue(of(expectedRetval)); + const testSearch = 'testSearch'; + const testMaxFecResults = 1; + const testMaxFecfileResults = 2; + + const expectedEndpoint = '/contacts/candidate_lookup/'; + const expectedParams = { + q: testSearch, + max_fec_results: testMaxFecResults, + max_fecfile_results: testMaxFecfileResults + } + + service + .candidateLookup(testSearch, testMaxFecResults, testMaxFecfileResults) + .subscribe((value) => expect(value).toEqual(expectedRetval)); + expect(apiServiceGetSpy).toHaveBeenCalledOnceWith(expectedEndpoint, expectedParams); + }); + it('#committeeLookup() happy path', () => { const expectedRetval = new CommitteeLookupResponse(); const apiServiceGetSpy = spyOn(testApiService, 'get').and.returnValue(of(expectedRetval)); @@ -154,4 +175,15 @@ describe('ContactService', () => { expect(apiServiceGetSpy).toHaveBeenCalledOnceWith(expectedEndpoint, expectedParams); }); + it('#getSchemaByType should return the correct schema', () => { + let schema: JsonSchema = ContactService.getSchemaByType(ContactTypes.COMMITTEE); + expect(schema.$id).toBe('https://github.com/fecgov/fecfile-validate/blob/main/schema/Contact_Committee.json'); + + schema = ContactService.getSchemaByType(ContactTypes.ORGANIZATION); + expect(schema.$id).toBe('https://github.com/fecgov/fecfile-validate/blob/main/schema/Contact_Organization.json'); + + schema = ContactService.getSchemaByType(ContactTypes.CANDIDATE); + expect(schema.$id).toBe('https://github.com/fecgov/fecfile-validate/blob/main/schema/Contact_Candidate.json'); + }); + }); diff --git a/front-end/src/app/shared/services/contact.service.ts b/front-end/src/app/shared/services/contact.service.ts index 6aa6ee6617..bc463e1792 100644 --- a/front-end/src/app/shared/services/contact.service.ts +++ b/front-end/src/app/shared/services/contact.service.ts @@ -1,11 +1,17 @@ import { Injectable } from '@angular/core'; +import { schema as contactCandidateSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Candidate'; +import { schema as contactCommitteeSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Committee'; +import { schema as contactIndividualSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Individual'; +import { schema as contactOrganizationSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Organization'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { JsonSchema } from '../interfaces/json-schema.interface'; import { TableListService } from '../interfaces/table-list-service.interface'; -import { CommitteeLookupResponse, Contact, IndividualLookupResponse, OrganizationLookupResponse } from '../models/contact.model'; +import { CandidateLookupResponse, CommitteeLookupResponse, Contact, ContactTypes, IndividualLookupResponse, OrganizationLookupResponse } from '../models/contact.model'; import { ListRestResponse } from '../models/rest-api.model'; import { ApiService } from './api.service'; + @Injectable({ providedIn: 'root', }) @@ -44,6 +50,17 @@ export class ContactService implements TableListService { return this.apiService.delete(`/contacts/${contact.id}`); } + public candidateLookup(search: string, maxFecResults: number, + maxFecfileResults: number): Observable { + return this.apiService.get( + '/contacts/candidate_lookup/', { + q: search, + max_fec_results: maxFecResults, + max_fecfile_results: maxFecfileResults + }).pipe( + map((response) => CandidateLookupResponse.fromJSON(response))); + } + public committeeLookup(search: string, maxFecResults: number, maxFecfileResults: number): Observable { return this.apiService.get( @@ -75,4 +92,23 @@ export class ContactService implements TableListService { map((response) => OrganizationLookupResponse.fromJSON(response))); } + /** + * Given the type of contact given, return the appropriate JSON schema doc + * @param {ContactTypes} type + * @returns {JsonSchema} schema + */ + public static getSchemaByType(type: ContactTypes): JsonSchema { + let schema: JsonSchema = contactIndividualSchema; + if (type === ContactTypes.CANDIDATE) { + schema = contactCandidateSchema; + } + if (type === ContactTypes.COMMITTEE) { + schema = contactCommitteeSchema; + } + if (type === ContactTypes.ORGANIZATION) { + schema = contactOrganizationSchema; + } + return schema; + } + } diff --git a/front-end/src/app/shared/services/fec-api.service.spec.ts b/front-end/src/app/shared/services/fec-api.service.spec.ts index e095796aef..18d816ca7a 100644 --- a/front-end/src/app/shared/services/fec-api.service.spec.ts +++ b/front-end/src/app/shared/services/fec-api.service.spec.ts @@ -1,10 +1,13 @@ -import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { FecApiService } from './fec-api.service'; -import { FecApiPaginatedResponse } from 'app/shared/models/fec-api.model'; +import { TestBed } from '@angular/core/testing'; +import { provideMockStore } from '@ngrx/store/testing'; import { CommitteeAccount } from 'app/shared/models/committee-account.model'; -import { environment } from 'environments/environment'; +import { FecApiPaginatedResponse } from 'app/shared/models/fec-api.model'; import { FecFiling } from '../models/fec-filing.model'; +import { testMockStore } from '../utils/unit-test.utils'; +import { FecApiService } from './fec-api.service'; +import { Candidate } from '../models/candidate.model'; +import { environment } from '../../../environments/environment'; describe('FecApiService', () => { let httpTestingController: HttpTestingController; @@ -13,6 +16,7 @@ describe('FecApiService', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], + providers: [FecApiService, provideMockStore(testMockStore)], }); httpTestingController = TestBed.inject(HttpTestingController); service = TestBed.inject(FecApiService); @@ -22,9 +26,9 @@ describe('FecApiService', () => { expect(service).toBeTruthy(); }); - describe('#getDetails()', () => { - it('should return committee details', () => { - const committeeAccount: CommitteeAccount = new CommitteeAccount(); + describe('#getCandidateDetails()', () => { + it('should return candidate details', () => { + const candidate: Candidate = new Candidate(); const response: FecApiPaginatedResponse = { api_version: '1.0', pagination: { @@ -33,15 +37,15 @@ describe('FecApiService', () => { count: 1, pages: 1, }, - results: [committeeAccount], + results: [candidate], }; - service.getDetails('C00601211').subscribe((committeeAccountData) => { - expect(committeeAccountData).toEqual(committeeAccount); + service.getCandidateDetails('P12345678').subscribe((candidateData) => { + expect(candidateData).toEqual(candidate); }); const req = httpTestingController.expectOne( - 'https://api.open.fec.gov/v1/committee/C00601211/?api_key=' + environment.fecApiKey + 'https://api.open.fec.gov/v1/candidate/P12345678/?api_key=' + environment.fecApiKey ); expect(req.request.method).toEqual('GET'); @@ -49,10 +53,9 @@ describe('FecApiService', () => { }); }); - describe('#getCommitteeRecentFiling()', () => { - it('should return most recent filing from realtime endpoint', () => { - const f1Filing: FecFiling = FecFiling.fromJSON({ form_type: 'F1N', pdf_url: 'go here' }); - const f2Filing: FecFiling = FecFiling.fromJSON({ form_type: 'F2', pdf_url: 'dont go here' }); + describe('#getCommitteeDetails()', () => { + it('should return committee details', () => { + const committeeAccount: CommitteeAccount = new CommitteeAccount(); const response: FecApiPaginatedResponse = { api_version: '1.0', pagination: { @@ -61,60 +64,44 @@ describe('FecApiService', () => { count: 1, pages: 1, }, - results: [f2Filing, f1Filing], + results: [committeeAccount], }; + service.getCommitteeDetails('C00601211').subscribe((committeeAccountData) => { + expect(committeeAccountData).toEqual(committeeAccount); + }); + + const req = httpTestingController.expectOne(`https://localhost/api/v1/openfec/C00601211/committee/`); + + expect(req.request.method).toEqual('GET'); + req.flush(response); + }); + }); + + describe('#getCommitteeRecentFiling()', () => { + it('should return most recent filing from realtime endpoint', () => { + const f1Filing: FecFiling = FecFiling.fromJSON({ form_type: 'F1N', pdf_url: 'go here' }); + service.getCommitteeRecentFiling('C00601211').subscribe((mostRecentFiling) => { expect(mostRecentFiling).toEqual(f1Filing); }); - const req = httpTestingController.expectOne( - `https://api.open.fec.gov/v1/efile/filings/?api_key=${environment.fecApiKey}&committee_id=C00601211&sort=-receipt_date` - ); + const req = httpTestingController.expectOne(`https://localhost/api/v1/openfec/C00601211/filings/`); expect(req.request.method).toEqual('GET'); - req.flush(response); + req.flush(f1Filing); }); }); it('should return most recent filing from nightly endpoint', () => { - const f1Filing: FecFiling = FecFiling.fromJSON({ form_type: 'F1N', pdf_url: 'go here' }); const f2Filing: FecFiling = FecFiling.fromJSON({ form_type: 'F2', pdf_url: 'dont go here' }); - const realtimeResponse: FecApiPaginatedResponse = { - api_version: '1.0', - pagination: { - page: 1, - per_page: 20, - count: 1, - pages: 1, - }, - results: [f2Filing], - }; - - const nightlyResponse: FecApiPaginatedResponse = { - api_version: '1.0', - pagination: { - page: 1, - per_page: 20, - count: 1, - pages: 1, - }, - results: [f1Filing], - }; service.getCommitteeRecentFiling('C00601211').subscribe((mostRecentFiling) => { - expect(mostRecentFiling).toEqual(f1Filing); + expect(mostRecentFiling).toEqual(f2Filing); }); - const realtimeReq = httpTestingController.expectOne( - `https://api.open.fec.gov/v1/efile/filings/?api_key=${environment.fecApiKey}&committee_id=C00601211&sort=-receipt_date` - ); + const realtimeReq = httpTestingController.expectOne(`https://localhost/api/v1/openfec/C00601211/filings/`); expect(realtimeReq.request.method).toEqual('GET'); - realtimeReq.flush(realtimeResponse); - const nightlyReq = httpTestingController.expectOne( - `https://api.open.fec.gov/v1/filings/?api_key=${environment.fecApiKey}&sort=-receipt_date&per_page=1&page=1&committee_id=C00601211&form_type=F1` - ); - expect(nightlyReq.request.method).toEqual('GET'); - nightlyReq.flush(nightlyResponse); + realtimeReq.flush(f2Filing); }); }); diff --git a/front-end/src/app/shared/services/fec-api.service.ts b/front-end/src/app/shared/services/fec-api.service.ts index d87137c2a4..3ebc556e50 100644 --- a/front-end/src/app/shared/services/fec-api.service.ts +++ b/front-end/src/app/shared/services/fec-api.service.ts @@ -1,10 +1,12 @@ import { HttpClient, HttpParams } from '@angular/common/http'; -import { environment } from '../../../environments/environment'; import { Injectable } from '@angular/core'; -import { map, Observable, of, switchMap } from 'rxjs'; +import { map, Observable } from 'rxjs'; +import { environment } from '../../../environments/environment'; import { CommitteeAccount } from '../models/committee-account.model'; -import { FecApiPaginatedResponse } from 'app/shared/models/fec-api.model'; +import { FecApiPaginatedResponse } from '../models/fec-api.model'; +import { Candidate } from '../models/candidate.model'; import { FecFiling } from '../models/fec-filing.model'; +import { ApiService } from './api.service'; export type QueryParamsType = { [param: string]: string | number | boolean | readonly (string | number | boolean)[] }; @@ -14,7 +16,7 @@ export type QueryParamsType = { [param: string]: string | number | boolean | rea export class FecApiService { private apiKey: string | null = environment.fecApiKey; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient, private apiService: ApiService) {} getHeaders() { return { @@ -28,22 +30,37 @@ export class FecApiService { } /** - * Gets the commitee account details. + * Gets the candidate details. * - * @return {Observable} The commitee details. + * @return {Observable} The candidate details. */ - public getDetails(committeeId: string | null): Observable { - if (!committeeId) { - throw new Error('No Committee Id provided.'); + public getCandidateDetails(candidateId: string | null): Observable { + if (!candidateId) { + throw new Error('Fecfile: No Candidate Id provided in getCandidateDetails()'); } const headers = this.getHeaders(); const params = this.getQueryParams(); return this.http - .get(`${environment.fecApiUrl}committee/${committeeId}/`, { + .get(`${environment.fecApiUrl}candidate/${candidateId}/`, { headers: headers, params: params, }) - .pipe(map((response: FecApiPaginatedResponse) => (response.results[0] as CommitteeAccount) || undefined)); + .pipe(map((response: FecApiPaginatedResponse) => (response.results[0] as Candidate) || undefined)); + } + + /** + * Gets the commitee account details. + * + * @return {Observable} The commitee details. + */ + public getCommitteeDetails(committeeId: string | null): Observable { + if (!committeeId) { + throw new Error('Fecfile: No Committee Id provided in getCommitteeDetails()'); + } + + return this.apiService + .get(`/openfec/${committeeId}/committee/`) + .pipe(map((response) => response.results[0] as CommitteeAccount)); } /** @@ -58,50 +75,11 @@ export class FecApiService { */ public getCommitteeRecentFiling(committeeId: string | undefined): Observable { if (!committeeId) { - throw new Error('No Committee Id provided.'); + throw new Error('Fecfile: No Committee Id provided in getCommitteeRecentFiling()'); } - const headers = this.getHeaders(); - const nightlyEndpointParams = this.getQueryParams({ - sort: '-receipt_date', - per_page: 1, - page: 1, - committee_id: committeeId, - form_type: 'F1', - }); - const nightlyEndpoint = this.http - .get(`${environment.fecApiUrl}filings/`, { - headers: headers, - params: nightlyEndpointParams, - }) - .pipe( - map((response: FecApiPaginatedResponse) => { - return (response.results as FecFiling[]).find((filing: FecFiling) => filing.form_type?.startsWith('F1')); - }) - ); - - const realTimeEndpoint = this.http - .get(`${environment.fecApiUrl}efile/filings/`, { - headers: headers, - params: this.getQueryParams({ - committee_id: committeeId, - sort: '-receipt_date', - }), - }) - .pipe( - map((response: FecApiPaginatedResponse) => { - return (response.results as FecFiling[]).find((filing: FecFiling) => filing.form_type?.startsWith('F1')); - }) - ); - - // Check real time endpoint for result. If it doesn't have one, check the nightly endpoint - return realTimeEndpoint.pipe( - switchMap((realTimeResult) => { - if (realTimeResult) { - return of(realTimeResult); - } - return nightlyEndpoint; - }) - ); + return this.apiService + .get(`/openfec/${committeeId}/filings/`) + .pipe(map((response) => FecFiling.fromJSON(response))); } } diff --git a/front-end/src/app/shared/services/report.service.ts b/front-end/src/app/shared/services/report.service.ts index b15f103e73..92f9e1f690 100644 --- a/front-end/src/app/shared/services/report.service.ts +++ b/front-end/src/app/shared/services/report.service.ts @@ -69,7 +69,7 @@ export class ReportService implements TableListService { * @returns Observable */ setActiveReportById(reportId: string | undefined): Observable { - if (!reportId) throw new Error('No Report Id Provided.'); + if (!reportId) throw new Error('Fecfile: No Report Id Provided.'); return this.get(reportId).pipe( tap((report) => { return this.store.dispatch(setActiveReportAction({ payload: report || new F3xSummary() })); diff --git a/front-end/src/app/shared/services/transaction.service.spec.ts b/front-end/src/app/shared/services/transaction.service.spec.ts index 4dfb6e3dfa..2713605240 100644 --- a/front-end/src/app/shared/services/transaction.service.spec.ts +++ b/front-end/src/app/shared/services/transaction.service.spec.ts @@ -3,9 +3,9 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { TestBed } from '@angular/core/testing'; import { provideMockStore } from '@ngrx/store/testing'; import { environment } from '../../../environments/environment'; -import { TransactionType } from '../models/transaction-types/transaction-type.model'; +import { Transaction, AggregationGroups } from '../models/transaction.model'; import { ListRestResponse } from '../models/rest-api.model'; -import { SchATransaction, ScheduleATransactionTypes, AggregationGroups } from '../models/scha-transaction.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../models/scha-transaction.model'; import { testMockStore } from '../utils/unit-test.utils'; import { TransactionService } from './transaction.service'; import { TransactionTypeUtils } from '../utils/transaction-type.utils'; @@ -48,9 +48,7 @@ describe('TransactionService', () => { expect(response).toEqual(mockResponse); }); - const req = httpTestingController.expectOne( - `${environment.apiUrl}/transactions/schedule-a/?page=1&ordering=form_type` - ); + const req = httpTestingController.expectOne(`${environment.apiUrl}/transactions/?page=1&ordering=form_type`); expect(req.request.method).toEqual('GET'); req.flush(mockResponse); httpTestingController.verify(); @@ -66,7 +64,7 @@ describe('TransactionService', () => { expect(response).toEqual(mockResponse); }); - const req = httpTestingController.expectOne(`${environment.apiUrl}/transactions/schedule-a/1/`); + const req = httpTestingController.expectOne(`${environment.apiUrl}/transactions/1/`); expect(req.request.method).toEqual('GET'); req.flush(mockResponse); httpTestingController.verify(); @@ -78,22 +76,16 @@ describe('TransactionService', () => { transaction_type_identifier: ScheduleATransactionTypes.OFFSET_TO_OPERATING_EXPENDITURES, aggregation_group: AggregationGroups.GENERAL, }); - const mockTransactionType: TransactionType | undefined = TransactionTypeUtils.factory( + const mockTransaction: Transaction = TransactionTypeUtils.factory( ScheduleATransactionTypes.INDIVIDUAL_RECEIPT - ); - if (mockTransactionType) { - mockTransactionType.transaction = mockResponse; - mockTransactionType.transaction = SchATransaction.fromJSON({ - id: 'abc', - transaction_type_identifier: ScheduleATransactionTypes.OFFSET_TO_OPERATING_EXPENDITURES, - }); - service.getPreviousTransaction(mockTransactionType, '1', new Date()).subscribe((response) => { - expect(response).toEqual(mockResponse); - }); - } + ).getNewTransaction(); + mockTransaction.id = 'abc'; + service.getPreviousTransaction(mockTransaction, '1', new Date()).subscribe((response) => { + expect(response).toEqual(mockResponse); + }); const formattedDate = formatDate(new Date(), 'yyyy-MM-dd', 'en-US'); const req = httpTestingController.expectOne( - `${environment.apiUrl}/transactions/schedule-a/previous/?transaction_id=abc&contact_id=1&action_date=${formattedDate}&aggregation_group=${AggregationGroups.GENERAL}` + `${environment.apiUrl}/transactions/previous/?transaction_id=abc&contact_id=1&date=${formattedDate}&aggregation_group=${AggregationGroups.GENERAL}` ); expect(req.request.method).toEqual('GET'); req.flush(mockResponse); diff --git a/front-end/src/app/shared/services/transaction.service.ts b/front-end/src/app/shared/services/transaction.service.ts index 4f87060617..ef990b2f34 100644 --- a/front-end/src/app/shared/services/transaction.service.ts +++ b/front-end/src/app/shared/services/transaction.service.ts @@ -3,27 +3,10 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { TableListService } from '../interfaces/table-list-service.interface'; -import { Transaction } from '../models/transaction.model'; +import { Transaction, AggregationGroups, ScheduleTransaction } from '../models/transaction.model'; import { ListRestResponse } from '../models/rest-api.model'; import { ApiService } from './api.service'; -import { SchATransaction, AggregationGroups } from '../models/scha-transaction.model'; -import { SchBTransaction } from '../models/schb-transaction.model'; -import { TransactionType } from '../models/transaction-types/transaction-type.model'; - -/** - * Given the API endpoint, return the class of the relevent schedule. - * @param key URL of root API endpoint - * @returns Transaction subclass - */ -function getScheduleClass(apiEndpoint: string) { - switch (apiEndpoint) { - case '/transactions/schedule-a': - return SchATransaction; - case '/transactions/schedule-b': - return SchBTransaction; - } - throw new Error(`Class transaction for API endpoint '${apiEndpoint}' not found`); -} +import { getFromJSON } from '../utils/transaction-type.utils'; @Injectable({ providedIn: 'root', @@ -39,71 +22,58 @@ export class TransactionService implements TableListService { if (!ordering) { ordering = 'form_type'; } - // Pull list from Sch A Transactions until we have an endpoint that pulls transactions from the different schedule types - return this.apiService - .get(`/transactions/schedule-a/?page=${pageNumber}&ordering=${ordering}`, params) - .pipe( - map((response: ListRestResponse) => { - response.results = response.results.map((item) => SchATransaction.fromJSON(item)); - return response; - }) - ); + return this.apiService.get(`/transactions/?page=${pageNumber}&ordering=${ordering}`, params).pipe( + map((response: ListRestResponse) => { + response.results = response.results.map((item) => getFromJSON(item)); + return response; + }) + ); } - public get(id: string): Observable { - return this.apiService.get(`/transactions/schedule-a/${id}/`).pipe( + public get(id: string): Observable { + return this.apiService.get(`/transactions/${id}/`).pipe( map((response) => { - const txn = SchATransaction.fromJSON(response); - - // Convert child transactions into SchATransaction objects - if (txn.children) { - txn.children = txn.children.map((child) => SchATransaction.fromJSON(child)); - } - - return txn; + return getFromJSON(response); }) ); } public getPreviousTransaction( - transactionType: TransactionType | undefined, + transaction: Transaction | undefined, contact_id: string, action_date: Date ): Observable { const actionDateString: string = this.datePipe.transform(action_date, 'yyyy-MM-dd') || ''; - const transaction_id: string = transactionType?.transaction?.id || ''; + const transaction_id: string = transaction?.id || ''; const aggregation_group: AggregationGroups | undefined = - (transactionType?.transaction as SchATransaction)?.aggregation_group || AggregationGroups.GENERAL; - const apiEndpoint: string = transactionType?.transaction?.apiEndpoint || ''; - const scheduleClass = getScheduleClass(apiEndpoint); + (transaction as ScheduleTransaction)?.aggregation_group || AggregationGroups.GENERAL; - if (transactionType && action_date && contact_id && aggregation_group) { + if (transaction && action_date && contact_id && aggregation_group) { + // Need 404 handler return this.apiService - .get(`${apiEndpoint}/previous/`, { + .get('/transactions/previous/', { transaction_id, contact_id, - action_date: actionDateString, + date: actionDateString, aggregation_group, }) - .pipe(map((response) => scheduleClass.fromJSON(response))); + .pipe(map((response) => getFromJSON(response))); } return of(undefined); } public create(transaction: Transaction): Observable { const payload = transaction.toJson(); - const scheduleClass = getScheduleClass(transaction.apiEndpoint); return this.apiService .post(`${transaction.apiEndpoint}/`, payload) - .pipe(map((response) => scheduleClass.fromJSON(response))); + .pipe(map((response) => getFromJSON(response))); } public update(transaction: Transaction): Observable { const payload = transaction.toJson(); - const scheduleClass = getScheduleClass(transaction.apiEndpoint); return this.apiService .put(`${transaction.apiEndpoint}/${transaction.id}/`, payload) - .pipe(map((response) => scheduleClass.fromJSON(response))); + .pipe(map((response) => getFromJSON(response))); } public delete(transaction: Transaction): Observable { diff --git a/front-end/src/app/shared/services/validate.service.spec.ts b/front-end/src/app/shared/services/validate.service.spec.ts deleted file mode 100644 index a91f38e11e..0000000000 --- a/front-end/src/app/shared/services/validate.service.spec.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { FormBuilder, ValidationErrors, ValidatorFn, FormControl } from '@angular/forms'; -import { schema as contactCandidateSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Candidate'; -import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; -import { ValidateService } from './validate.service'; - -describe('ValidateService', () => { - let service: ValidateService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(ValidateService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - it('#formValidator should validate properties correctly', () => { - const fb: FormBuilder = new FormBuilder(); - service.formValidatorSchema = contactCandidateSchema; - service.formValidatorForm = fb.group( - service.getFormGroupFields(service.getSchemaProperties(contactCandidateSchema)) - ); - service.formValidatorForm.patchValue({ - telephone: '12345678910', - candidate_office: 'S', - last_name: 'Valid Name', - }); - - let validator: ValidatorFn = service.formValidator('telephone'); - let result: ValidationErrors | null = validator(service.formValidatorForm.get('telephone') as FormControl); - expect(result).not.toBe(null); - if (result) { - expect(result['pattern'].requiredPattern).toBe('^\\+\\d{1,3} \\d{10}$'); - } - - validator = service.formValidator('candidate_state'); - result = validator(service.formValidatorForm.get('candidate_state') as FormControl); - expect(result).not.toBe(null); - if (result) { - expect(result['required']).toBe(true); - } - - validator = service.formValidator('last_name'); - result = validator(service.formValidatorForm.get('last_name') as FormControl); - expect(result).toBe(null); - }); - - it('#formValidator should validate boolean properties correctly', () => { - const fb: FormBuilder = new FormBuilder(); - service.formValidatorSchema = f3xSchema; - service.formValidatorForm = fb.group(service.getFormGroupFields(service.getSchemaProperties(f3xSchema))); - service.formValidatorForm.patchValue({ - change_of_address: 'A', - }); - - let validator: ValidatorFn = service.formValidator('change_of_address'); - let result: ValidationErrors | null = validator(service.formValidatorForm.get('change_of_address') as FormControl); - expect(result).not.toBe(null); - if (result) { - expect(result['pattern'].requiredPattern).toBe('must be boolean,null'); - } - - service.formValidatorForm.patchValue({ - change_of_address: true, - }); - validator = service.formValidator('change_of_address'); - result = validator(service.formValidatorForm.get('change_of_address') as FormControl); - expect(result).toBe(null); - }); - - it('#formValidator should validate min/max numeric properties correctly', () => { - const fb: FormBuilder = new FormBuilder(); - service.formValidatorSchema = f3xSchema; - service.formValidatorForm = fb.group(service.getFormGroupFields(service.getSchemaProperties(f3xSchema))); - service.formValidatorForm.patchValue({ - L6a_cash_on_hand_jan_1_ytd: 1000000000.0, - }); - - let validator: ValidatorFn = service.formValidator('L6a_cash_on_hand_jan_1_ytd'); - let result: ValidationErrors | null = validator( - service.formValidatorForm.get('L6a_cash_on_hand_jan_1_ytd') as FormControl - ); - expect(result).not.toEqual(null); - if (result) { - expect(result['max'].max).toBe(999999999.99); - } - - service.formValidatorForm.patchValue({ - L6a_cash_on_hand_jan_1_ytd: -200.0, - }); - validator = service.formValidator('L6a_cash_on_hand_jan_1_ytd'); - result = validator(service.formValidatorForm.get('L6a_cash_on_hand_jan_1_ytd') as FormControl); - if (result) { - expect(result['min'].min).toBe(0); - } - }); - - it('#getSchemaProperties() should return empty array when no schema', () => { - const properties: string[] = service.getSchemaProperties(undefined); - expect(properties.length).toBe(0); - }); -}); diff --git a/front-end/src/app/shared/services/validate.service.ts b/front-end/src/app/shared/services/validate.service.ts deleted file mode 100644 index d58c2eb747..0000000000 --- a/front-end/src/app/shared/services/validate.service.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { Injectable } from '@angular/core'; -import { FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; -import { validate, ValidationError } from 'fecfile-validate'; -import { JsonSchema } from '../interfaces/json-schema.interface'; -import { DateUtils } from '../utils/date.utils'; - -@Injectable({ - providedIn: 'root', -}) -export class ValidateService { - /** - * @property - This property is a placeholder for the schema to use in the form element - * custom validator located in the method formValidator() below. - */ - formValidatorSchema: JsonSchema | undefined; - - /** - * @property - This property is a placeholder for the ng reactive form to use in the form - * element custom validator located in the method formValidator() below. - */ - formValidatorForm: FormGroup | undefined; - - /** - * Validate a data object against a JSON Schema document - * @param {JsonSchema} schema - * @param data - * @param {string[]} fieldsToValidate - * @returns {ValidationError[]} array of validation errors if any - */ - // prettier-ignore - validate(schema: JsonSchema, data: any, fieldsToValidate: string[] = []): ValidationError[] { // eslint-disable-line @typescript-eslint/no-explicit-any - return validate(schema, data, fieldsToValidate); - } - - /** - * Returns an array of the property fields for a given JSON schema. - * @param {JsonSchema} schema - * @returns {string[]} list of property names - */ - getSchemaProperties(schema: JsonSchema | undefined): string[] { - if (schema) { - return Object.keys(schema.properties); - } - return []; - } - - /** - * Returns an object to pass ot the FormBuilder group() method when creating - * a reactive Angular form whose validation will be managed by this service. - * @param {string[]} properties - * @returns data structure to pass to the FormBuilder group() method - */ - getFormGroupFields(properties: string[]) { - const group: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - properties.forEach((property) => (group[property] = ['', [this.formValidator(property)]])); - return group; - } - - /** - * - * @param {FormGroup} form - * @param {string[]} propertiesSubset - Only get values for the listed subset of schema parameters. - * @returns object containing the form property values limited to the current validation schema - * This method will 'null' any schema values that do not have a form value and, more importantly, - * set those form fields with an empty '' value to null for the backend. It will also convert - * strings to number types when necessary. - */ - getFormValues(form: FormGroup, propertiesSubset: string[] = []) { - const formValues: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - - if (this.formValidatorSchema) { - this.getSchemaProperties(this.formValidatorSchema).forEach((property: string) => { - if (propertiesSubset.length > 0 && !propertiesSubset.includes(property)) { - return; - } - formValues[property] = this.getPropertyValue(property, form); - }); - } - - return formValues; - } - - /** - * Convert the form input value to the appropriate type. - * @param {string} property - * @param {FromGroup} form - * @returns - */ - private getPropertyValue(property: string, form: FormGroup) { - // Undefined and empty strings are set to null. - if ( - form?.get(property)?.value === undefined || - form?.get(property)?.value === '' || - form?.get(property)?.value === null - ) { - return null; - } - - // Convert a string to number if expected in the schema. - if ( - (Array.isArray(this.formValidatorSchema?.properties[property].type) && - this.formValidatorSchema?.properties[property].type.includes('number')) || - this.formValidatorSchema?.properties[property].type === 'number' - ) { - return Number(form?.get(property)?.value); - } - - // Convert date to string - if (Object.prototype.toString.call(form?.get(property)?.value) === '[object Date]') { - return DateUtils.convertDateToFecFormat(form?.get(property)?.value); - } - - // All else are strings so copy straight into value - return form?.get(property)?.value; - } - - /** - * ng validator function for reactive forms. Provides validation based on the - * JSON schema and form in the formValidationSchema and formValidationForm properties - * @param {string} property - name of form property to validate - * @returns {ValidationErrors | undefined} - */ - formValidator(property: string): ValidatorFn { - return (): ValidationErrors | null => { - if (!this.formValidatorSchema || !this.formValidatorForm) { - return null; - } - - const errors: ValidationError[] = this.validate( - this.formValidatorSchema, - this.getFormValues(this.formValidatorForm), - [property] - ); - - if (errors.length) { - const result: ValidationErrors = {}; - errors.forEach((error) => { - // The keyword === 'type' indicates a conditional check fail as part of an 'anyOf' JSON schema rule - // Basically, we tried to pass a null to a JSON schema type: ["string"] rule rather than a type: ["string", "null"] rule. - if (error.keyword === 'required' || (error.keyword === 'type' && error['params']['type'] === 'string')) { - result['required'] = true; - } - if (error.keyword === 'minLength') { - result['minlength'] = { requiredLength: error.params['limit'] }; - } - if (error.keyword === 'maxLength' || error.keyword === 'maximum') { - result['maxlength'] = { requiredLength: error.params['limit'] }; - } - if (error.keyword === 'minimum') { - result['min'] = { min: error.params['limit'] }; - } - if (error.keyword === 'exclusiveMinimum') { - result['exclusiveMin'] = { exclusiveMin: error.params['limit'] }; - } - if (error.keyword === 'maximum') { - result['max'] = { max: error.params['limit'] }; - } - if (error.keyword === 'exclusiveMaximum') { - result['exclusiveMax'] = { exclusiveMax: error.params['limit'] }; - } - if (error.keyword === 'pattern') { - result['pattern'] = { requiredPattern: error.params['pattern'] }; - } - if (error.keyword === 'enum') { - result['pattern'] = { requiredPattern: `Allowed values: ${error.params['allowedValues'].join(', ')}` }; - } - if (error.keyword === 'type' && error.params['type'] === 'number') { - if ( - this.formValidatorForm?.get(error.path)?.value === '' || - this.formValidatorForm?.get(error.path)?.value === null || - this.formValidatorForm?.get(error.path)?.value === undefined - ) { - result['required'] = true; - } else { - result['pattern'] = { requiredPattern: 'Value must be a number' }; - } - } - if (error.keyword === 'type' && error.params['type'].includes('boolean')) { - result['pattern'] = { requiredPattern: error.message }; - } - }); - return result; - } - - return null; - }; - } - - passwordValidator(): ValidatorFn | ValidatorFn[] { - const v = Validators.compose([ - Validators.required, - Validators.minLength(8), - Validators.maxLength(16), - Validators.pattern('.*[A-Z].*'), - Validators.pattern('.*[a-z].*'), - Validators.pattern('.*[0-9].*'), - Validators.pattern('.*[!@#$%&*()].*'), - ]); - - return v ? v : []; - } -} diff --git a/front-end/src/app/shared/shared.module.ts b/front-end/src/app/shared/shared.module.ts index c0ea4a0a2e..68f6c56a58 100644 --- a/front-end/src/app/shared/shared.module.ts +++ b/front-end/src/app/shared/shared.module.ts @@ -29,12 +29,15 @@ import { LabelPipe } from './pipes/label.pipe'; import { LongDatePipe } from './pipes/long-date.pipe'; import { NavigationControlComponent } from './components/navigation-control/navigation-control/navigation-control.component'; import { NavigationControlBarComponent } from './components/navigation-control-bar/navigation-control-bar.component'; +import { TransactionContactLookupComponent } from './components/transaction-contact-lookup/transaction-contact-lookup.component'; +import { TooltipModule } from 'primeng/tooltip'; @NgModule({ imports: [ CommonModule, ReactiveFormsModule, ButtonModule, + TooltipModule, DropdownModule, AutoCompleteModule, InputTextModule, @@ -58,6 +61,7 @@ import { NavigationControlBarComponent } from './components/navigation-control-b NavigationControlComponent, NavigationControlBarComponent, ContactLookupComponent, + TransactionContactLookupComponent, ContactFormComponent, AddressInputComponent, NameInputComponent, @@ -80,6 +84,7 @@ import { NavigationControlBarComponent } from './components/navigation-control-b NavigationControlComponent, NavigationControlBarComponent, ContactLookupComponent, + TransactionContactLookupComponent, ContactFormComponent, AddressInputComponent, NameInputComponent, diff --git a/front-end/src/app/shared/utils/form-type.utils.spec.ts b/front-end/src/app/shared/utils/form-type.utils.spec.ts new file mode 100644 index 0000000000..8167a066a0 --- /dev/null +++ b/front-end/src/app/shared/utils/form-type.utils.spec.ts @@ -0,0 +1,20 @@ +import { FormType, FormTypes, FORM_TYPES } from './form-type.utils'; + +describe('FormTypeUtils', () => { + describe('FormType', () => { + it('should carry properties', () => { + const f1FormType = new FormType('F1', 'Form 1', 'Statement of organization', '/'); + expect(f1FormType.label).toBe('Form 1'); + expect(f1FormType.createRoute).toEqual('/'); + }); + }); + + describe('FORM_TYPES', () => { + it('should have F3X', () => { + const F3X = FORM_TYPES.get(FormTypes.F3X); + expect(F3X).toBeTruthy(); + expect(F3X?.description).toEqual('Report of Receipts and Disbursements'); + expect(F3X?.code).toEqual('F3X'); + }); + }); +}); diff --git a/front-end/src/app/shared/utils/form-type.utils.ts b/front-end/src/app/shared/utils/form-type.utils.ts new file mode 100644 index 0000000000..92dfa44651 --- /dev/null +++ b/front-end/src/app/shared/utils/form-type.utils.ts @@ -0,0 +1,26 @@ +export enum FormTypes { + F3X = 'F3X', + F24 = 'F24', +} + +export class FormType { + code: string; + label: string; + description: string; + createRoute: string; + + constructor(code: string, label: string, description: string, createRoute: string) { + this.code = code; + this.label = label; + this.description = description; + this.createRoute = createRoute; + } +} + +export const FORM_TYPES = new Map([ + [FormTypes.F3X, new FormType('F3X', 'Form 3X', 'Report of Receipts and Disbursements', '/reports/f3x/create/step1')], + // [ + // FormTypes.F24, + // new FormType('F24', 'Form 24', '24/48 Hour Notice of Independent Expentiture', '/reports/f24/create/step1'), + // ], +]); diff --git a/front-end/src/app/shared/utils/label.utils.ts b/front-end/src/app/shared/utils/label.utils.ts index f29174fca6..9e312c646e 100644 --- a/front-end/src/app/shared/utils/label.utils.ts +++ b/front-end/src/app/shared/utils/label.utils.ts @@ -208,3 +208,18 @@ const CongressionalDistricts: Record = { WI: 8, WY: 1, }; + +export const CategoryCodeLabels: LabelList = [ + ['001', '001 Administrative/Salary/Overhead Expenses'], + ['002', '002 Travel Expenses - including travel reimbursement expenses'], + ['003', '003 Solicitation and Fundraising Expenses'], + ['004', '004 Advertising Expenses -including general public political advertising'], + ['005', '005 Polling Expenses'], + ['006', '006 Campaign Materials'], + ['007', '007 Campaign Event Expenses'], + ['008', '008 Transfers'], + ['009', '009 Loan Repayments'], + ['010', '010 Refunds of Contributions'], + ['011', '011 Political Contributions'], + ['012', '012 Donations'], +]; diff --git a/front-end/src/app/shared/utils/transaction-type.utils.spec.ts b/front-end/src/app/shared/utils/transaction-type.utils.spec.ts index 1c78f6fcf3..424ea117bc 100644 --- a/front-end/src/app/shared/utils/transaction-type.utils.spec.ts +++ b/front-end/src/app/shared/utils/transaction-type.utils.spec.ts @@ -8,6 +8,6 @@ describe('LabelUtils', () => { it('non-existing transaction type should throw an error', () => { expect(() => { TransactionTypeUtils.factory('DOES_NOT_EXIST'); - }).toThrow(new Error("Class transaction type of 'DOES_NOT_EXIST' is not found")); + }).toThrow(new Error("Fecfile: Class transaction type of 'DOES_NOT_EXIST' is not found")); }); }); diff --git a/front-end/src/app/shared/utils/transaction-type.utils.ts b/front-end/src/app/shared/utils/transaction-type.utils.ts index 127f1aadfe..e60fcfa158 100644 --- a/front-end/src/app/shared/utils/transaction-type.utils.ts +++ b/front-end/src/app/shared/utils/transaction-type.utils.ts @@ -1,3 +1,8 @@ +import { ScheduleTransaction } from '../models/transaction.model'; +import { SchATransaction } from 'app/shared/models/scha-transaction.model'; +import { SchBTransaction } from '../models/schb-transaction.model'; + +// Schedule A ///////////////////////////////////////////////////// import { BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT } from '../models/transaction-types/BUSINESS_LABOR_NON_CONTRIBUTION_ACCOUNT.model'; import { EARMARK_MEMO } from '../models/transaction-types/EARMARK_MEMO.model'; import { EARMARK_RECEIPT } from '../models/transaction-types/EARMARK_RECEIPT.model'; @@ -33,13 +38,15 @@ import { PARTNERSHIP_MEMO } from '../models/transaction-types/PARTNERSHIP_MEMO.m import { PARTNERSHIP_RECEIPT } from '../models/transaction-types/PARTNERSHIP_RECEIPT.model'; import { PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT } from '../models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT.model'; import { PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO } from '../models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO.model'; +import { PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT } from '../models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT.model'; +import { PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO } from '../models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO.model'; import { PARTY_JF_TRANSFER_MEMO } from '../models/transaction-types/PARTY_JF_TRANSFER_MEMO.model'; import { PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT } from '../models/transaction-types/PARTY_NATIONAL_PARTY_CONVENTION_ACCOUNT.model'; import { PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT } from '../models/transaction-types/PARTY_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model'; import { PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT } from '../models/transaction-types/PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT.model'; import { PARTY_RECEIPT } from '../models/transaction-types/PARTY_RECEIPT.model'; import { PARTY_RECOUNT_RECEIPT } from '../models/transaction-types/PARTY_RECOUNT_RECEIPT.model'; -import { TransactionType } from '../models/transaction-types/transaction-type.model'; +import { TransactionType } from '../models/transaction-type.model'; import { TRANSFER } from '../models/transaction-types/TRANSFER.model'; import { TRIBAL_JF_TRANSFER_MEMO } from '../models/transaction-types/TRIBAL_JF_TRANSFER_MEMO.model'; import { TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT } from '../models/transaction-types/TRIBAL_NATIONAL_PARTY_CONVENTION_ACCOUNT.model'; @@ -61,9 +68,20 @@ import { EARMARK_RECEIPT_CONVENTION_ACCOUNT } from '../models/transaction-types/ import { EARMARK_MEMO_HEADQUARTERS_ACCOUNT } from '../models/transaction-types/EARMARK_MEMO_HEADQUARTERS_ACCOUNT.model'; import { EARMARK_MEMO_CONVENTION_ACCOUNT } from '../models/transaction-types/EARMARK_MEMO_CONVENTION_ACCOUNT.model'; import { EARMARK_MEMO_RECOUNT_ACCOUNT } from '../models/transaction-types/EARMARK_MEMO_RECOUNT_ACCOUNT.model'; +import { PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT } from '../models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT.model'; +import { PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO } from '../models/transaction-types/PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO.model'; +import { PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT } from '../models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT.model'; +import { PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO } from '../models/transaction-types/PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO.model'; + +// Schedule B ///////////////////////////////////////////////////// +import { OPERATING_EXPENDITURE } from '../models/transaction-types/OPERATING_EXPENDITURE.model'; +import { OPERATING_EXPENDITURE_VOID } from '../models/transaction-types/OPERATING_EXPENDITURE_VOID.model'; +import { OTHER_DISBURSEMENT } from '../models/transaction-types/OTHER_DISBURSEMENT.model'; +import { OTHER_DISBURSEMENT_VOID } from '../models/transaction-types/OTHER_DISBURSEMENT_VOID.model'; // prettier-ignore const transactionTypeClasses: any = { // eslint-disable-line @typescript-eslint/no-explicit-any + // Schedule A ///////////////////////////////////////////////////// EARMARK_RECEIPT, EARMARK_MEMO, INDIVIDUAL_JF_TRANSFER_MEMO, @@ -111,6 +129,10 @@ const transactionTypeClasses: any = { // eslint-disable-line @typescript-eslint/ PAC_NATIONAL_PARTY_HEADQUARTERS_JF_TRANSFER_MEMO, PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT, PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT_MEMO, + PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT, + PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT_MEMO, + PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT, + PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT_MEMO, PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT, INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT, INDIVIDUAL_NATIONAL_PARTY_CONVENTION_ACCOUNT, @@ -126,13 +148,20 @@ const transactionTypeClasses: any = { // eslint-disable-line @typescript-eslint/ EARMARK_MEMO_HEADQUARTERS_ACCOUNT, EARMARK_MEMO_CONVENTION_ACCOUNT, EARMARK_MEMO_RECOUNT_ACCOUNT, + PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT, + PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT_MEMO, + // Schedule B ///////////////////////////////////////////////////// + OPERATING_EXPENDITURE, + OPERATING_EXPENDITURE_VOID, + OTHER_DISBURSEMENT, + OTHER_DISBURSEMENT_VOID, } export class TransactionTypeUtils { static factory(transactionTypeIdentifier: string): TransactionType { const transactionType = getTransactionTypeClass(transactionTypeIdentifier); if (!transactionType) { - throw new Error(`Class transaction type of '${transactionTypeIdentifier}' is not found`); + throw new Error(`Fecfile: Class transaction type of '${transactionTypeIdentifier}' is not found`); } return new transactionType(); } @@ -142,3 +171,24 @@ export class TransactionTypeUtils { export function getTransactionTypeClass(transactionTypeIdentifier: string): any { // eslint-disable-line @typescript-eslint/no-explicit-any return transactionTypeClasses[transactionTypeIdentifier]; } + +// prettier-ignore +/** + * Returns a schedule object of the correct class as discovered by examining + * the scheduleId of the transaction type. + * + * This function is in this file because there is a REFERENCEERROR when it + * is included in the transaction.model.ts file + * @param json + * @param depth + * @returns + */ +export function getFromJSON(json: any, depth = 2): ScheduleTransaction { // eslint-disable-line @typescript-eslint/no-explicit-any + if (json.transaction_type_identifier) { + const transactionType = TransactionTypeUtils.factory(json.transaction_type_identifier); + if (transactionType.scheduleId === 'A') return SchATransaction.fromJSON(json, depth); + if (transactionType.scheduleId === 'B') return SchBTransaction.fromJSON(json, depth); + } + return SchATransaction.fromJSON(json, depth); // Until 404 resolved + // throw new Error('Fecfile: Missing transaction type identifier when creating a transaction object from a JSON record'); +} diff --git a/front-end/src/app/shared/utils/unit-test.utils.ts b/front-end/src/app/shared/utils/unit-test.utils.ts index f4e5ef8b82..76badbe0b5 100644 --- a/front-end/src/app/shared/utils/unit-test.utils.ts +++ b/front-end/src/app/shared/utils/unit-test.utils.ts @@ -11,6 +11,13 @@ import { CommitteeAccount } from '../models/committee-account.model'; import { F3xSummary } from '../models/f3x-summary.model'; import { UploadSubmission } from '../models/upload-submission.model'; import { CashOnHand } from '../interfaces/report.interface'; +import { Transaction, AggregationGroups, TransactionTypes } from '../models/transaction.model'; +import { SchBTransaction, ScheduleBTransactionTypes } from '../models/schb-transaction.model'; +import { Contact, ContactTypes } from '../models/contact.model'; +import { SchATransaction, ScheduleATransactionTypes } from '../models/scha-transaction.model'; +import { TransactionTypeUtils } from './transaction-type.utils'; +import { TransactionTemplateMapType } from '../models/transaction-type.model'; +import { MemoText } from '../models/memo-text.model'; export const testCommitteeAccount: CommitteeAccount = CommitteeAccount.fromJSON({ affiliated_committee_name: 'NONE', @@ -118,3 +125,111 @@ export const testMockStore = { { selector: selectCashOnHand, value: testCashOnHand }, ], }; + +export const testIndividualReceipt: SchATransaction = SchATransaction.fromJSON({ + id: '123', + transaction_type_identifier: ScheduleATransactionTypes.INDIVIDUAL_RECEIPT, + report_id: '999', + contribution_amount: '202.2', + contribution_date: '2022-02-02', + entity_type: ContactTypes.ORGANIZATION, + contributor_organization_name: 'org name', + contributor_street_1: '123 Main St', + contributor_city: 'city', + contributor_state: 'VA', + contributor_zip: '20001', + memo_text: MemoText.fromJSON({ text4000: 'Memo!' }), + contact_id: '456', + contact: Contact.fromJSON({ + id: 'testId', + type: ContactTypes.INDIVIDUAL, + last_name: 'testLn1', + first_name: 'testFn1', + middle_name: 'testMn1', + prefix: 'testPrefix1', + suffix: 'testSuffix1', + employer: 'testEmployer1', + occupation: 'testOccupation1', + street_1: 'testStreet1', + street_2: 'testStreet2', + city: 'testCity1', + state: 'VA', + zip: '12345', + }), +}); + +export const testScheduleATransaction = SchATransaction.fromJSON({ + form_type: 'SA15', + filer_committee_id_number: 'C00000000', + transaction_type_identifier: 'PAC_JF_TRANSFER_MEMO', + transaction_id: 'AAAAAAAAAAAAAAAAAAA', + back_reference_tran_id_number: 'AAAAAAAAAAAAAAAAAAA', + back_reference_sched_name: 'SA12', + entity_type: ContactTypes.COMMITTEE, + contributor_organization_name: 'org name', + contributor_street_1: '123 Main St', + contributor_city: 'city', + contributor_state: 'VA', + contributor_zip: '20001', + contribution_date: '2022-08-11', + contribution_amount: 1, + contribution_aggregate: 2, + contribution_purpose_descrip: 'Joint Fundraising Memo: test', + aggregation_group: AggregationGroups.GENERAL, + memo_code: true, + donor_committee_fec_id: 'C00000000', +}); + +export const testScheduleBTransaction = SchBTransaction.fromJSON({ + form_type: 'SB21b', + filer_committee_id_number: 'C00000000', + transaction_type_identifier: ScheduleBTransactionTypes.OPERATING_EXPENDITURE, + transaction_id: 'AAAAAAAAAAAAAAAAAAA', + entity_type: ContactTypes.ORGANIZATION, + contributor_organization_name: 'org name', + contributor_street_1: '123 Main St', + contributor_city: 'city', + contributor_state: 'VA', + contributor_zip: '20001', + contribution_date: '2022-08-11', + contribution_amount: 1, + contribution_aggregate: 2, + aggregation_group: AggregationGroups.GENERAL_DISBURSEMENT, +}); + +export function getTestTransactionByType( + transactionType: TransactionTypes, + parentTransactionType?: TransactionTypes +): Transaction { + const transaction = TransactionTypeUtils.factory(transactionType).getNewTransaction(); + if (parentTransactionType) { + transaction.parent_transaction = TransactionTypeUtils.factory(parentTransactionType).getNewTransaction(); + } + return transaction; +} + +export const testTemplateMap: TransactionTemplateMapType = { + last_name: 'contributor_last_name', + first_name: 'contributor_first_name', + middle_name: 'contributor_middle_name', + prefix: 'contributor_prefix', + suffix: 'contributor_suffix', + street_1: 'contributor_street_1', + street_2: 'contributor_street_2', + city: 'contributor_city', + state: 'contributor_state', + zip: 'contributor_zip', + employer: 'contributor_employer', + occupation: 'contributor_occupation', + organization_name: 'contributor_organization_name', + committee_fec_id: 'donor_committee_fec_id', + date: 'contribution_date', + dateLabel: 'DATE RECEIVED', + memo_code: 'memo_code', + amount: 'contribution_amount', + aggregate: 'contribution_aggregate', + purpose_description: 'contribution_purpose_descrip', + purposeDescripLabel: 'CONTRIBUTION PURPOSE DESCRIPTION', + memo_text_input: 'memo_text_input', + category_code: '', +}; diff --git a/front-end/src/app/shared/utils/validate.utils.spec.ts b/front-end/src/app/shared/utils/validate.utils.spec.ts index 792de2322e..331a398d1f 100644 --- a/front-end/src/app/shared/utils/validate.utils.spec.ts +++ b/front-end/src/app/shared/utils/validate.utils.spec.ts @@ -1,7 +1,104 @@ +import { FormBuilder, FormControl, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { schema as contactCandidateSchema } from 'fecfile-validate/fecfile_validate_js/dist/Contact_Candidate'; +import { schema as f3xSchema } from 'fecfile-validate/fecfile_validate_js/dist/F3X'; import { ValidateUtils } from './validate.utils'; -describe('DateUtils', () => { - it('should create an instance', () => { - expect(new ValidateUtils()).toBeTruthy(); +describe('ValidateUtils', () => { + + it('#formValidator should validate properties correctly', () => { + const fb: FormBuilder = new FormBuilder(); + const formValidatorSchema = contactCandidateSchema; + const formValidatorForm = fb.group( + ValidateUtils.getFormGroupFields(ValidateUtils.getSchemaProperties(contactCandidateSchema)) + ); + formValidatorForm.patchValue({ + telephone: '12345678910', + candidate_office: 'S', + last_name: 'Valid Name', + }); + + let validator: ValidatorFn = ValidateUtils.jsonSchemaValidator('telephone', + formValidatorForm, formValidatorSchema); + let result: ValidationErrors | null = + validator(formValidatorForm.get('telephone') as FormControl); + expect(result).not.toBe(null); + if (result) { + expect(result['pattern'].requiredPattern).toBe('^\\+\\d{1,3} \\d{10}$'); + } + + validator = ValidateUtils.jsonSchemaValidator('candidate_state', + formValidatorForm, formValidatorSchema); + result = validator(formValidatorForm.get('candidate_state') as FormControl); + expect(result).not.toBe(null); + if (result) { + expect(result['required']).toBe(true); + } + + validator = ValidateUtils.jsonSchemaValidator('last_name', + formValidatorForm, formValidatorSchema); + result = validator(formValidatorForm.get('last_name') as FormControl); + expect(result).toBe(null); + }); + + it('#formValidator should validate boolean properties correctly', () => { + const fb: FormBuilder = new FormBuilder(); + const formValidatorSchema = f3xSchema; + const formValidatorForm = fb.group(ValidateUtils.getFormGroupFields( + ValidateUtils.getSchemaProperties(f3xSchema))); + formValidatorForm.patchValue({ + change_of_address: 'A', + }); + + let validator: ValidatorFn = ValidateUtils.jsonSchemaValidator('change_of_address', + formValidatorForm, formValidatorSchema); + let result: ValidationErrors | null = validator( + formValidatorForm.get('change_of_address') as FormControl); + expect(result).not.toBe(null); + if (result) { + expect(result['pattern'].requiredPattern).toBe('must be boolean,null'); + } + + formValidatorForm.patchValue({ + change_of_address: true, + }); + validator = ValidateUtils.jsonSchemaValidator('change_of_address', + formValidatorForm, formValidatorSchema); + result = validator(formValidatorForm.get('change_of_address') as FormControl); + expect(result).toBe(null); + }); + + it('#formValidator should validate min/max numeric properties correctly', () => { + const fb: FormBuilder = new FormBuilder(); + const formValidatorSchema = f3xSchema; + const formValidatorForm = fb.group(ValidateUtils.getFormGroupFields( + ValidateUtils.getSchemaProperties(f3xSchema))); + formValidatorForm.patchValue({ + L6a_cash_on_hand_jan_1_ytd: 1000000000.0, + }); + + let validator: ValidatorFn = ValidateUtils.jsonSchemaValidator('L6a_cash_on_hand_jan_1_ytd', + formValidatorForm, formValidatorSchema); + let result: ValidationErrors | null = validator( + formValidatorForm.get('L6a_cash_on_hand_jan_1_ytd') as FormControl + ); + expect(result).not.toEqual(null); + if (result) { + expect(result['max'].max).toBe(999999999.99); + } + + formValidatorForm.patchValue({ + L6a_cash_on_hand_jan_1_ytd: -200.0, + }); + validator = ValidateUtils.jsonSchemaValidator('L6a_cash_on_hand_jan_1_ytd', + formValidatorForm, formValidatorSchema); + result = validator(formValidatorForm.get('L6a_cash_on_hand_jan_1_ytd') as FormControl); + if (result) { + expect(result['min'].min).toBe(0); + } + }); + + it('#getSchemaProperties() should return empty array when no schema', () => { + const properties: string[] = ValidateUtils.getSchemaProperties(undefined); + expect(properties.length).toBe(0); }); }); diff --git a/front-end/src/app/shared/utils/validate.utils.ts b/front-end/src/app/shared/utils/validate.utils.ts index 82294c1cda..95ea3fee91 100644 --- a/front-end/src/app/shared/utils/validate.utils.ts +++ b/front-end/src/app/shared/utils/validate.utils.ts @@ -1,20 +1,10 @@ import { FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { validate, ValidationError } from 'fecfile-validate'; import { JsonSchema } from '../interfaces/json-schema.interface'; +import { Transaction } from '../models/transaction.model'; import { DateUtils } from './date.utils'; export class ValidateUtils { - /** - * Validate a data object against a JSON Schema document - * @param {JsonSchema} schema - * @param data - * @param {string[]} fieldsToValidate - * @returns {ValidationError[]} array of validation errors if any - */ - // prettier-ignore - static validate(schema: JsonSchema, data: any, fieldsToValidate: string[] = []): ValidationError[] { // eslint-disable-line @typescript-eslint/no-explicit-any - return validate(schema, data, fieldsToValidate); - } /** * Returns an array of the property fields for a given JSON schema. @@ -34,43 +24,63 @@ export class ValidateUtils { * @param {string[]} properties * @returns data structure to pass to the FormBuilder group() method */ - static getFormGroupFields(properties: string[], form: FormGroup, schema: JsonSchema) { + static getFormGroupFields(properties: string[]) { const group: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - properties.forEach((property) => (group[property] = ['', [this.formValidator(property, form, schema)]])); + properties.forEach((property) => (group[property] = ['', null])); return group; } /** * * @param {FormGroup} form - * @param {JsonSchema} schema + * @param {JsonSchema} jsonSchema - the schema to use in the form element custom validator. * @param {string[]} propertiesSubset - Only get values for the listed subset of schema parameters. * @returns object containing the form property values limited to the current validation schema * This method will 'null' any schema values that do not have a form value and, more importantly, * set those form fields with an empty '' value to null for the backend. It will also convert * strings to number types when necessary. */ - static getFormValues(form: FormGroup, schema: JsonSchema, propertiesSubset: string[] = []) { + static getFormValues(form: FormGroup, jsonSchema?: JsonSchema, + propertiesSubset: string[] = []) { const formValues: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - this.getSchemaProperties(schema).forEach((property: string) => { - if (propertiesSubset.length > 0 && !propertiesSubset.includes(property)) { - return; - } - formValues[property] = this.getPropertyValue(property, form, schema); - }); + if (jsonSchema) { + ValidateUtils.getSchemaProperties(jsonSchema).forEach((property: string) => { + if (propertiesSubset.length > 0 && !propertiesSubset.includes(property)) { + return; + } + formValues[property] = ValidateUtils.getPropertyValue( + property, form, jsonSchema); + }); + } return formValues; } + /** + * + * @param transaction Transaction object to get values from. + * This method returns "non-form" values that may be required + * for validation of form fields. + */ + static getNonFormValues(transaction?: Transaction) { + const values: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any + if (transaction) { + values['transaction_type_identifier'] = + transaction.transaction_type_identifier; + } + return values; + } + /** * Convert the form input value to the appropriate type. * @param {string} property * @param {FromGroup} form - * @param {JsonSchema} schema + * @param {JsonSchema} jsonSchema - the schema to use in the form element custom validator. * @returns */ - static getPropertyValue(property: string, form: FormGroup, schema: JsonSchema) { + private static getPropertyValue(property: string, form: FormGroup, + jsonSchema?: JsonSchema) { // Undefined and empty strings are set to null. if ( form?.get(property)?.value === undefined || @@ -82,8 +92,9 @@ export class ValidateUtils { // Convert a string to number if expected in the schema. if ( - (Array.isArray(schema?.properties[property].type) && schema?.properties[property].type.includes('number')) || - schema?.properties[property].type === 'number' + (Array.isArray(jsonSchema?.properties[property].type) && + jsonSchema?.properties[property].type.includes('number')) || + jsonSchema?.properties[property].type === 'number' ) { return Number(form?.get(property)?.value); } @@ -97,21 +108,47 @@ export class ValidateUtils { return form?.get(property)?.value; } + /** + * This method adds JSON schema validators to a form + * for the JsonSchema passed in, removing existing + * validators first (if clearExistingValidators === true). + * @param form formGroup to add validators to. + * @param jsonSchema JSON schema to add validators for. + * @param clearExistingValidators flag specifying whether + * @param transaction (if any) to add validators for. + * to remove existing form validators first for each field. + */ + static addJsonSchemaValidators(form: FormGroup, + jsonSchema: JsonSchema, clearExistingValidators: boolean, + transaction?: Transaction) { + for (const key in form.controls) { + if (clearExistingValidators) { + form.get(key)?.clearValidators(); + } + form.get(key)?.addValidators( + ValidateUtils.jsonSchemaValidator(key, form, + jsonSchema, transaction)); + } + form.updateValueAndValidity(); + } + /** * ng validator function for reactive forms. Provides validation based on the - * JSON schema and form in the formValidationSchema and formValidationForm properties + * JSON schema and form in the jsonSchema and formGroup properties * @param {string} property - name of form property to validate - * @param {FormGroup} form - * @param {JsonSchema} schema - * @returns {ValidationErrors | undefined} + * @param {JsonSchema} jsonSchema - the schema to use in the form element custom validator. + * @param {FormGroup} form - the ng reactive form to use in the form element custom validator + * @param {Transaction} transaction - Transaction (if any) associated with the property passed in. + * @returns {ValidationErrors | undefined} generated by the Ajv validation library */ - static formValidator(property: string, form: FormGroup, schema: JsonSchema): ValidatorFn { + static jsonSchemaValidator(property: string, form: FormGroup, + jsonSchema: JsonSchema, transaction?: Transaction): ValidatorFn { return (): ValidationErrors | null => { - if (!schema || !form) { - return null; + const data = { + ...ValidateUtils.getFormValues(form, jsonSchema), + ...ValidateUtils.getNonFormValues(transaction) } - - const errors: ValidationError[] = this.validate(schema, this.getFormValues(form, schema), [property]); + const errors: ValidationError[] = validate(jsonSchema, data, [property]); if (errors.length) { const result: ValidationErrors = {}; diff --git a/front-end/src/app/transactions/transaction-container/transaction-container.component.html b/front-end/src/app/transactions/transaction-container/transaction-container.component.html index 235f3b13ef..6ff3d6a7b6 100644 --- a/front-end/src/app/transactions/transaction-container/transaction-container.component.html +++ b/front-end/src/app/transactions/transaction-container/transaction-container.component.html @@ -1,30 +1,21 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - + + diff --git a/front-end/src/app/transactions/transaction-container/transaction-container.component.spec.ts b/front-end/src/app/transactions/transaction-container/transaction-container.component.spec.ts index 586f660487..feb87f311b 100644 --- a/front-end/src/app/transactions/transaction-container/transaction-container.component.spec.ts +++ b/front-end/src/app/transactions/transaction-container/transaction-container.component.spec.ts @@ -4,8 +4,9 @@ import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; +import { SchATransaction, ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; +import { getTestTransactionByType, testMockStore } from 'app/shared/utils/unit-test.utils'; import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { CalendarModule } from 'primeng/calendar'; @@ -18,7 +19,6 @@ import { InputTextareaModule } from 'primeng/inputtextarea'; import { ToastModule } from 'primeng/toast'; import { of } from 'rxjs'; import { SharedModule } from '../../shared/shared.module'; -import { TransactionTypeUtils } from '../../shared/utils/transaction-type.utils'; import { TransactionGroupBComponent } from '../transaction-group-b/transaction-group-b.component'; import { TransactionContainerComponent } from './transaction-container.component'; @@ -53,7 +53,9 @@ describe('TransactionContainerComponent', () => { provide: ActivatedRoute, useValue: { data: of({ - transactionType: TransactionTypeUtils.factory('OFFSET_TO_OPERATING_EXPENDITURES'), + transaction: getTestTransactionByType( + ScheduleATransactionTypes.OFFSET_TO_OPERATING_EXPENDITURES + ) as SchATransaction, }), }, }, diff --git a/front-end/src/app/transactions/transaction-container/transaction-container.component.ts b/front-end/src/app/transactions/transaction-container/transaction-container.component.ts index 1c9d0d0e21..5e6e630845 100644 --- a/front-end/src/app/transactions/transaction-container/transaction-container.component.ts +++ b/front-end/src/app/transactions/transaction-container/transaction-container.component.ts @@ -1,25 +1,25 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subject, takeUntil } from 'rxjs'; -import { TransactionType } from '../../shared/models/transaction-types/transaction-type.model'; import { Store } from '@ngrx/store'; import { selectCommitteeAccount } from '../../store/committee-account.selectors'; import { CommitteeAccount } from '../../shared/models/committee-account.model'; import { Title } from '@angular/platform-browser'; +import { Transaction } from 'app/shared/models/transaction.model'; @Component({ selector: 'app-transaction-container', templateUrl: './transaction-container.component.html', }) export class TransactionContainerComponent implements OnInit, OnDestroy { - transactionType: TransactionType | undefined; + transaction: Transaction | undefined; destroy$: Subject = new Subject(); constructor(private activatedRoute: ActivatedRoute, private store: Store, private titleService: Title) { activatedRoute.data.pipe(takeUntil(this.destroy$)).subscribe((data) => { - this.transactionType = data['transactionType']; - if (this.transactionType) { - const title = this.transactionType['title']; + this.transaction = data['transaction']; + if (this.transaction) { + const title: string = this.transaction.transactionType?.title || ''; this.titleService.setTitle(title); } }); @@ -30,12 +30,11 @@ export class TransactionContainerComponent implements OnInit, OnDestroy { .select(selectCommitteeAccount) .pipe(takeUntil(this.destroy$)) .subscribe((committeeAccount: CommitteeAccount) => { - if (this.transactionType?.transaction) { - this.transactionType.transaction.filer_committee_id_number = committeeAccount.committee_id; + if (this.transaction) { + this.transaction.filer_committee_id_number = committeeAccount.committee_id; } - if (this.transactionType?.childTransactionType?.transaction) { - this.transactionType.childTransactionType.transaction.filer_committee_id_number = - committeeAccount.committee_id; + if (this.transaction?.transactionType?.dependentChildTransactionType && this.transaction.children) { + this.transaction.children[0].filer_committee_id_number = committeeAccount.committee_id; } }); } diff --git a/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.html b/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.html index 3b50ee16f7..5edb64a9fd 100644 --- a/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.html +++ b/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.html @@ -1,52 +1,57 @@
    -

    {{ transactionType?.title }}

    +

    {{ transaction?.transactionType?.title }}

    Contact

    - - +
    - - + +

    Address

    - +

    Employer

    - +

    Receipt Information

    Additional Information

    @@ -56,7 +61,7 @@

    Additional Information

    diff --git a/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.spec.ts b/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.spec.ts index 6e6b4b32de..c700196e1f 100644 --- a/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.spec.ts +++ b/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.spec.ts @@ -3,19 +3,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; -import { Transaction } from 'app/shared/models/transaction.model'; -import { ContactTypes } from 'app/shared/models/contact.model'; -import { SchATransaction, ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; +import { ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; import { NavigationAction, NavigationDestination, NavigationEvent, } from 'app/shared/models/transaction-navigation-controls.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; +import { getTestTransactionByType, testMockStore, testTemplateMap } from 'app/shared/utils/unit-test.utils'; import { environment } from 'environments/environment'; -import { schema as INDIVIDUAL_JF_TRANSFER_MEMO } from 'fecfile-validate/fecfile_validate_js/dist/INDIVIDUAL_JF_TRANSFER_MEMO'; import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { CalendarModule } from 'primeng/calendar'; @@ -34,22 +30,7 @@ describe('TransactionGroupAComponent', () => { let httpTestingController: HttpTestingController; let component: TransactionGroupAComponent; let fixture: ComponentFixture; - - const transaction = SchATransaction.fromJSON({ - form_type: 'SA11AI', - filer_committee_id_number: 'C00000000', - transaction_type_identifier: ScheduleATransactionTypes.INDIVIDUAL_JF_TRANSFER_MEMO, - transaction_id: 'AAAAAAAAAAAAAAAAAAA', - entity_type: ContactTypes.ORGANIZATION, - contributor_organization_name: 'org name', - contributor_street_1: '123 Main St', - contributor_city: 'city', - contributor_state: 'VA', - contributor_zip: '20001', - contribution_date: '2022-08-11', - contribution_amount: 1, - contribution_aggregate: 2, - }); + const transaction = getTestTransactionByType(ScheduleATransactionTypes.INDIVIDUAL_RECEIPT); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -79,21 +60,8 @@ describe('TransactionGroupAComponent', () => { httpTestingController = TestBed.inject(HttpTestingController); fixture = TestBed.createComponent(TransactionGroupAComponent); component = fixture.componentInstance; - component.transactionType = { - scheduleId: '', - componentGroupId: '', - contact: undefined, - generatePurposeDescriptionWrapper: () => 'test description', - getNewTransaction: () => { - return {} as Transaction; - }, - title: '', - schema: INDIVIDUAL_JF_TRANSFER_MEMO, - transaction: transaction, - isDependentChild: false, - updateParentOnSave: false, - getSchemaName: () => 'foo', - } as TransactionType; + component.templateMap = testTemplateMap; + component.transaction = transaction; fixture.detectChanges(); }); @@ -106,7 +74,7 @@ describe('TransactionGroupAComponent', () => { component.save(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, transaction)); expect(component.form.invalid).toBe(true); httpTestingController.expectNone( - `${environment.apiUrl}/transactions/schedule-a/1/?schema=INDIVIDUAL_JF_TRANSFER_MEMO&fields_to_validate=` + `${environment.apiUrl}/transactions/schedule-a/1/?schema=INDIVIDUAL_RECEIPT&fields_to_validate=` ); httpTestingController.verify(); }); diff --git a/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.ts b/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.ts index 587d7b9daf..5748d03abd 100644 --- a/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.ts +++ b/front-end/src/app/transactions/transaction-group-a/transaction-group-a.component.ts @@ -29,7 +29,5 @@ export class TransactionGroupAComponent extends TransactionTypeBaseComponent imp 'memo_code', 'memo_text_input', ]; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL].includes(option.code as ContactTypes) - ); + override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ContactTypes.INDIVIDUAL]); } diff --git a/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.html b/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.html index 68d1ce5d22..d844bb98b5 100644 --- a/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.html +++ b/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.html @@ -1,5 +1,5 @@ -

    {{ transactionType?.title }}

    +

    {{ transaction?.transactionType?.title }}

    This type of receipt requires a memo transaction. Follow this two-step process to create both an earmark receipt and an earmark memo: @@ -17,39 +17,48 @@

    Contact

    - - +
    - - + +

    Address

    - +

    Employer

    - +

    Receipt Information

    Additional Information

    This type of receipt requires a memo transaction

    @@ -74,55 +83,67 @@

    Conduit

    - - +
    - + - +

    Address

    Employer

    - +

    Receipt Information

    Additional Information

    @@ -132,7 +153,7 @@

    Additional Information

    diff --git a/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.spec.ts b/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.spec.ts index ae2671f7d6..9f6246b0d8 100644 --- a/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.spec.ts +++ b/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.spec.ts @@ -4,7 +4,6 @@ import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; -import { EARMARK_MEMO } from 'app/shared/models/transaction-types/EARMARK_MEMO.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; import { testMockStore } from 'app/shared/utils/unit-test.utils'; import { AccordionModule } from 'primeng/accordion'; @@ -18,20 +17,17 @@ import { InputNumberModule } from 'primeng/inputnumber'; import { InputTextModule } from 'primeng/inputtext'; import { InputTextareaModule } from 'primeng/inputtextarea'; import { ToastModule } from 'primeng/toast'; -import { EARMARK_RECEIPT } from '../../shared/models/transaction-types/EARMARK_RECEIPT.model'; import { SharedModule } from '../../shared/shared.module'; import { TransactionGroupAgComponent } from './transaction-group-ag.component'; import { ConfirmDialogModule } from 'primeng/confirmdialog'; +import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; +import { ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; describe('TransactionGroupAgComponent', () => { let component: TransactionGroupAgComponent; let fixture: ComponentFixture; - const earmarkReceipt = new EARMARK_RECEIPT(); - earmarkReceipt.transaction = earmarkReceipt.getNewTransaction(); - const earmarkMemo = new EARMARK_MEMO(); - earmarkMemo.transaction = earmarkMemo.getNewTransaction(); - earmarkReceipt.childTransactionType = earmarkMemo; + const earmarkReceipt = TransactionTypeUtils.factory(ScheduleATransactionTypes.EARMARK_RECEIPT).getNewTransaction(); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -62,11 +58,12 @@ describe('TransactionGroupAgComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(TransactionGroupAgComponent); component = fixture.componentInstance; + component.transaction = earmarkReceipt; fixture.detectChanges(); }); it('should create', () => { - component.transactionType = earmarkReceipt; + component.transaction = earmarkReceipt; component.ngOnInit(); expect(component).toBeTruthy(); }); diff --git a/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.ts b/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.ts index a09e2e25c7..0b0cb715fc 100644 --- a/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.ts +++ b/front-end/src/app/transactions/transaction-group-ag/transaction-group-ag.component.ts @@ -53,10 +53,9 @@ export class TransactionGroupAgComponent extends DoubleTransactionTypeBaseCompon 'memo_code', 'memo_text_input', ]; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL].includes(option.code as ContactTypes) - ); - override childContactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL, ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); + override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ContactTypes.INDIVIDUAL]); + override childContactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ + ContactTypes.INDIVIDUAL, + ContactTypes.COMMITTEE, + ]); } diff --git a/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.html b/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.html index a2f739a942..bca1f14641 100644 --- a/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.html +++ b/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.html @@ -1,59 +1,62 @@
    -

    {{ transactionType?.title }}

    +

    {{ transaction?.transactionType?.title }}

    Contact

    - - +
    - + - +

    Address

    - +

    Receipt Information

    Additional Information

    diff --git a/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.spec.ts b/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.spec.ts index 5b5108ffab..96e62bb6c5 100644 --- a/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.spec.ts +++ b/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.spec.ts @@ -3,10 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; -import { Transaction } from 'app/shared/models/transaction.model'; +import { AggregationGroups } from 'app/shared/models/transaction.model'; import { Contact, ContactTypes } from 'app/shared/models/contact.model'; -import { AggregationGroups, SchATransaction } from 'app/shared/models/scha-transaction.model'; +import { SchATransaction, ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; import { NavigationAction, NavigationDestination, @@ -15,7 +14,6 @@ import { import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; import { ContactService } from 'app/shared/services/contact.service'; import { testMockStore } from 'app/shared/utils/unit-test.utils'; -import { schema as OFFSET_TO_OPERATING_EXPENDITURES } from 'fecfile-validate/fecfile_validate_js/dist/OFFSET_TO_OPERATING_EXPENDITURES'; import { Confirmation, ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { CalendarModule } from 'primeng/calendar'; @@ -31,6 +29,7 @@ import { of } from 'rxjs'; import { environment } from '../../../environments/environment'; import { SharedModule } from '../../shared/shared.module'; import { TransactionGroupBComponent } from './transaction-group-b.component'; +import { TransactionTypeUtils } from 'app/shared/utils/transaction-type.utils'; describe('TransactionGroupBComponent', () => { let httpTestingController: HttpTestingController; @@ -86,21 +85,9 @@ describe('TransactionGroupBComponent', () => { httpTestingController = TestBed.inject(HttpTestingController); fixture = TestBed.createComponent(TransactionGroupBComponent); component = fixture.componentInstance; - component.transactionType = { - scheduleId: '', - componentGroupId: '', - contact: undefined, - generatePurposeDescriptionWrapper: () => 'test description', - getNewTransaction: () => { - return {} as Transaction; - }, - title: '', - schema: OFFSET_TO_OPERATING_EXPENDITURES, - transaction: transaction, - isDependentChild: false, - updateParentOnSave: false, - getSchemaName: () => 'foo', - } as TransactionType; + component.transaction = TransactionTypeUtils.factory( + ScheduleATransactionTypes.OFFSET_TO_OPERATING_EXPENDITURES + ).getNewTransaction(); fixture.detectChanges(); }); @@ -144,8 +131,8 @@ describe('TransactionGroupBComponent', () => { } }); - if (component.transactionType?.transaction) { - component.transactionType.transaction.id = undefined; + if (component.transaction) { + component.transaction.id = undefined; } const testTran = SchATransaction.fromJSON({ form_type: 'SA15', @@ -180,8 +167,8 @@ describe('TransactionGroupBComponent', () => { } }); - if (component.transactionType?.transaction) { - component.transactionType.transaction.id = '10'; + if (component.transaction) { + component.transaction.id = '10'; } component.form.patchValue({ ...transaction }); component.save(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.ANOTHER, transaction)); @@ -200,8 +187,8 @@ describe('TransactionGroupBComponent', () => { } }); - if (component.transactionType?.transaction) { - component.transactionType.transaction.id = undefined; + if (component.transaction) { + component.transaction.id = undefined; } const testTran = SchATransaction.fromJSON({ form_type: 'SA15', @@ -238,8 +225,8 @@ describe('TransactionGroupBComponent', () => { } }); - if (component.transactionType?.transaction) { - component.transactionType.transaction.id = undefined; + if (component.transaction) { + component.transaction.id = undefined; } const testTran = SchATransaction.fromJSON({ form_type: 'SA15', @@ -275,8 +262,8 @@ describe('TransactionGroupBComponent', () => { it('#save() should not save an invalid org record', () => { const testContact: Contact = new Contact(); testContact.id = 'testId'; - if (component.transactionType?.transaction) { - component.transactionType.transaction.contact = testContact; + if (component.transaction) { + component.transaction.contact = testContact; } spyOn(testContactService, 'create').and.returnValue(of(testContact)); spyOn(testConfirmationService, 'confirm').and.callFake((confirmation: Confirmation) => { @@ -285,8 +272,8 @@ describe('TransactionGroupBComponent', () => { } }); - if (component.transactionType?.transaction) { - component.transactionType.transaction.id = undefined; + if (component.transaction) { + component.transaction.id = undefined; } const testTran = SchATransaction.fromJSON({ form_type: 'SA15', diff --git a/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.ts b/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.ts index c2a581801a..85a7edb250 100644 --- a/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.ts +++ b/front-end/src/app/transactions/transaction-group-b/transaction-group-b.component.ts @@ -8,6 +8,7 @@ import { ContactTypeLabels, ContactTypes } from '../../shared/models/contact.mod templateUrl: './transaction-group-b.component.html', }) export class TransactionGroupBComponent extends TransactionTypeBaseComponent implements OnInit, OnDestroy { + // Default to Schedule A properties formProperties: string[] = [ 'entity_type', 'contributor_organization_name', @@ -28,7 +29,37 @@ export class TransactionGroupBComponent extends TransactionTypeBaseComponent imp 'memo_code', 'memo_text_input', ]; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL, ContactTypes.ORGANIZATION, ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); + + override ngOnInit(): void { + if (this.transaction?.transactionType?.scheduleId === 'B') { + this.formProperties = [ + 'entity_type', + 'payee_organization_name', + 'payee_last_name', + 'payee_first_name', + 'payee_middle_name', + 'payee_prefix', + 'payee_suffix', + 'payee_street_1', + 'payee_street_2', + 'payee_city', + 'payee_state', + 'payee_zip', + 'expenditure_date', + 'expenditure_amount', + 'aggregate_amount', + 'expenditure_purpose_descrip', + 'memo_code', + 'memo_text_input', + 'category_code', + ]; + } + super.ngOnInit(); + } + + override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ + ContactTypes.INDIVIDUAL, + ContactTypes.ORGANIZATION, + ContactTypes.COMMITTEE, + ]); } diff --git a/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.html b/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.html index 718a80286a..976340d123 100644 --- a/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.html +++ b/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.html @@ -1,63 +1,70 @@
    -

    {{ transactionType?.title }}

    +

    {{ transaction?.transactionType?.title }}

    Contact

    - - +
    - + - +

    Address

    - + - +

    Receipt Information

    Additional Information

    diff --git a/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.spec.ts b/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.spec.ts index 2a9aaec535..de987de1dc 100644 --- a/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.spec.ts +++ b/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.spec.ts @@ -3,8 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; +import { ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; +import { getTestTransactionByType, testMockStore } from 'app/shared/utils/unit-test.utils'; import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { CalendarModule } from 'primeng/calendar'; @@ -43,14 +44,14 @@ describe('TransactionGroupCComponent', () => { ConfirmDialogModule, ], declarations: [TransactionGroupCComponent], - providers: [MessageService, ConfirmationService, - FormBuilder, provideMockStore(testMockStore), FecDatePipe], + providers: [MessageService, ConfirmationService, FormBuilder, provideMockStore(testMockStore), FecDatePipe], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(TransactionGroupCComponent); component = fixture.componentInstance; + component.transaction = getTestTransactionByType(ScheduleATransactionTypes.OTHER_RECEIPTS); fixture.detectChanges(); }); diff --git a/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.ts b/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.ts index 02bb49f671..460457c326 100644 --- a/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.ts +++ b/front-end/src/app/transactions/transaction-group-c/transaction-group-c.component.ts @@ -30,7 +30,9 @@ export class TransactionGroupCComponent extends TransactionTypeBaseComponent imp 'memo_code', 'memo_text_input', ]; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL, ContactTypes.ORGANIZATION, ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); + override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ + ContactTypes.INDIVIDUAL, + ContactTypes.ORGANIZATION, + ContactTypes.COMMITTEE, + ]); } diff --git a/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.html b/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.html index b302f7caf9..3edcf5f4f5 100644 --- a/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.html +++ b/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.html @@ -1,56 +1,58 @@
    -

    {{ transactionType?.title }}

    +

    {{ transaction?.transactionType?.title }}

    Contact

    - - +
    - +

    Address

    - +

    Receipt Information

    Additional Information

    @@ -75,7 +77,7 @@

    Additional Information

    diff --git a/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.spec.ts b/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.spec.ts index 12195523d0..93772d0372 100644 --- a/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.spec.ts +++ b/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.spec.ts @@ -3,19 +3,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; -import { Transaction } from 'app/shared/models/transaction.model'; import { ContactTypes } from 'app/shared/models/contact.model'; -import { SchATransaction, ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; +import { ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; import { NavigationDestination, NavigationAction, NavigationEvent, } from 'app/shared/models/transaction-navigation-controls.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; +import { getTestTransactionByType, testMockStore, testTemplateMap } from 'app/shared/utils/unit-test.utils'; import { environment } from 'environments/environment'; -import { schema as TRIBAL_JF_TRANSFER_MEMO } from 'fecfile-validate/fecfile_validate_js/dist/TRIBAL_JF_TRANSFER_MEMO'; import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { CalendarModule } from 'primeng/calendar'; @@ -35,20 +32,7 @@ describe('TransactionGroupDComponent', () => { let component: TransactionGroupDComponent; let fixture: ComponentFixture; - const transaction = SchATransaction.fromJSON({ - form_type: 'SA12', - transaction_type_identifier: ScheduleATransactionTypes.TRIBAL_JF_TRANSFER_MEMO, - transaction_id: 'AAAAAAAAAAAAAAAAAAA', - entity_type: ContactTypes.ORGANIZATION, - contributor_organization_name: 'org name', - contributor_street_1: '123 Main St', - contributor_city: 'city', - contributor_state: 'VA', - contributor_zip: '20001', - contribution_date: '2022-08-11', - contribution_amount: 1, - contribution_aggregate: 2, - }); + const transaction = getTestTransactionByType(ScheduleATransactionTypes.TRIBAL_RECEIPT); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -78,27 +62,13 @@ describe('TransactionGroupDComponent', () => { httpTestingController = TestBed.inject(HttpTestingController); fixture = TestBed.createComponent(TransactionGroupDComponent); component = fixture.componentInstance; - component.transactionType = { - scheduleId: '', - componentGroupId: '', - contact: undefined, - generatePurposeDescriptionWrapper: () => 'test description', - getNewTransaction: () => { - return {} as Transaction; - }, - title: '', - schema: TRIBAL_JF_TRANSFER_MEMO, - transaction: transaction, - isDependentChild: false, - updateParentOnSave: false, - getSchemaName: () => 'foo', - } as TransactionType; + component.transaction = transaction; + component.templateMap = testTemplateMap; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); - component.ngOnInit(); expect(component.form.get('entity_type')?.value).toEqual(ContactTypes.ORGANIZATION); }); @@ -116,7 +86,7 @@ describe('TransactionGroupDComponent', () => { component.save(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, transaction)); expect(component.form.invalid).toBe(true); httpTestingController.expectNone( - `${environment.apiUrl}/transactions/schedule-a/1/?schema=TRIBAL_JF_TRANSFER_MEMO&fields_to_validate=` + `${environment.apiUrl}/transactions/schedule-a/1/?schema=TRIBAL_RECEIPT&fields_to_validate=` ); httpTestingController.verify(); }); diff --git a/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.ts b/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.ts index 3fc9abb0e6..cf0eb9a100 100644 --- a/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.ts +++ b/front-end/src/app/transactions/transaction-group-d/transaction-group-d.component.ts @@ -28,13 +28,13 @@ export class TransactionGroupDComponent extends TransactionTypeBaseComponent imp ]; subTransactionOptions: { [key: string]: string | ScheduleATransactionTypes }[] = []; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.ORGANIZATION].includes(option.code as ContactTypes) - ); + override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ + ContactTypes.ORGANIZATION, + ]); override ngOnInit(): void { super.ngOnInit(); - this.subTransactionOptions = (this.transactionType?.subTransactionTypes || []).map((type) => { + this.subTransactionOptions = (this.transaction?.transactionType?.subTransactionTypes || []).map((type) => { return { label: LabelUtils.get(ScheduleATransactionTypeLabels, type), value: type, diff --git a/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.html b/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.html index 553ae007fb..35c9a62548 100644 --- a/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.html +++ b/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.html @@ -1,51 +1,53 @@
    -

    {{ transactionType?.title }}

    +

    {{ transaction?.transactionType?.title }}

    Contact

    - - +
    - +

    Address

    - +

    Receipt Information

    Additional Information

    -
    +
    Additional Information diff --git a/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.spec.ts b/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.spec.ts index 229ca7b65f..6f766eaf57 100644 --- a/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.spec.ts +++ b/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.spec.ts @@ -53,9 +53,9 @@ describe('TransactionGroupEComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(TransactionGroupEComponent); component = fixture.componentInstance; - component.transactionType = TransactionTypeUtils.factory(ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER); - if (component.transactionType) - component.transactionType.transaction = component.transactionType?.getNewTransaction(); + component.transaction = TransactionTypeUtils.factory( + ScheduleATransactionTypes.JOINT_FUNDRAISING_TRANSFER + ).getNewTransaction(); fixture.detectChanges(); }); diff --git a/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.ts b/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.ts index d50d52101d..a341e94f7b 100644 --- a/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.ts +++ b/front-end/src/app/transactions/transaction-group-e/transaction-group-e.component.ts @@ -29,13 +29,11 @@ export class TransactionGroupEComponent extends TransactionTypeBaseComponent imp ]; subTransactionOptions: { [key: string]: string | ScheduleATransactionTypes }[] = []; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); + override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ContactTypes.COMMITTEE]); override ngOnInit(): void { super.ngOnInit(); - this.subTransactionOptions = (this.transactionType?.subTransactionTypes || []).map((type) => { + this.subTransactionOptions = (this.transaction?.transactionType?.subTransactionTypes || []).map((type) => { return { label: LabelUtils.get(ScheduleATransactionTypeLabels, type), value: type, diff --git a/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.html b/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.html deleted file mode 100644 index 939c28d442..0000000000 --- a/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.html +++ /dev/null @@ -1,69 +0,0 @@ - - -
    -

    {{ transactionType?.title }}

    -

    Contact

    -
    -
    -
    -
    - - - -
    -
    -
    - - - -

    Address

    - - -

    Receipt Information

    - - -

    Additional Information

    - -
    - -
    - -
    -
    -
    -
    - - -
    -
    - - diff --git a/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.spec.ts b/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.spec.ts deleted file mode 100644 index eeb14180fd..0000000000 --- a/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.spec.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { RouterTestingModule } from '@angular/router/testing'; -import { provideMockStore } from '@ngrx/store/testing'; -import { TransactionType } from 'app/shared/models/transaction-types/transaction-type.model'; -import { Transaction } from 'app/shared/models/transaction.model'; -import { - NavigationAction, - NavigationDestination, - NavigationEvent, -} from 'app/shared/models/transaction-navigation-controls.model'; -import { Contact, ContactTypes } from 'app/shared/models/contact.model'; -import { AggregationGroups, SchATransaction } from 'app/shared/models/scha-transaction.model'; -import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; -import { schema as PAC_JF_TRANSFER_MEMO } from 'fecfile-validate/fecfile_validate_js/dist/PAC_JF_TRANSFER_MEMO'; -import { Confirmation, ConfirmationService, MessageService } from 'primeng/api'; -import { ButtonModule } from 'primeng/button'; -import { CalendarModule } from 'primeng/calendar'; -import { CheckboxModule } from 'primeng/checkbox'; -import { ConfirmDialogModule } from 'primeng/confirmdialog'; -import { DividerModule } from 'primeng/divider'; -import { DropdownModule } from 'primeng/dropdown'; -import { InputNumberModule } from 'primeng/inputnumber'; -import { InputTextModule } from 'primeng/inputtext'; -import { InputTextareaModule } from 'primeng/inputtextarea'; -import { ToastModule } from 'primeng/toast'; -import { environment } from '../../../environments/environment'; -import { SharedModule } from '../../shared/shared.module'; -import { TransactionGroupFComponent } from './transaction-group-f.component'; -import { ContactService } from 'app/shared/services/contact.service'; -import { of } from 'rxjs'; - -describe('TransactionGroupFComponent', () => { - let httpTestingController: HttpTestingController; - let component: TransactionGroupFComponent; - let fixture: ComponentFixture; - let testConfirmationService: ConfirmationService; - let testContactService: ContactService; - - const transaction = SchATransaction.fromJSON({ - form_type: 'SA15', - filer_committee_id_number: 'C00000000', - transaction_type_identifier: 'PAC_JF_TRANSFER_MEMO', - transaction_id: 'AAAAAAAAAAAAAAAAAAA', - back_reference_tran_id_number: 'AAAAAAAAAAAAAAAAAAA', - back_reference_sched_name: 'SA12', - entity_type: ContactTypes.COMMITTEE, - contributor_organization_name: 'org name', - contributor_street_1: '123 Main St', - contributor_city: 'city', - contributor_state: 'VA', - contributor_zip: '20001', - contribution_date: '2022-08-11', - contribution_amount: 1, - contribution_aggregate: 2, - contribution_purpose_descrip: 'Joint Fundraising Memo: test', - aggregation_group: AggregationGroups.GENERAL, - memo_code: true, - donor_committee_fec_id: 'C00000000', - }); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - HttpClientTestingModule, - RouterTestingModule, - FormsModule, - ReactiveFormsModule, - ToastModule, - SharedModule, - DividerModule, - DropdownModule, - CalendarModule, - ButtonModule, - CheckboxModule, - InputNumberModule, - InputTextModule, - InputTextareaModule, - ConfirmDialogModule, - ], - declarations: [TransactionGroupFComponent], - providers: [MessageService, ConfirmationService, FormBuilder, provideMockStore(testMockStore), FecDatePipe], - }).compileComponents(); - testContactService = TestBed.inject(ContactService); - testConfirmationService = TestBed.inject(ConfirmationService); - }); - - beforeEach(() => { - httpTestingController = TestBed.inject(HttpTestingController); - fixture = TestBed.createComponent(TransactionGroupFComponent); - component = fixture.componentInstance; - component.transactionType = { - scheduleId: '', - componentGroupId: '', - contact: undefined, - generatePurposeDescriptionWrapper: () => 'test description', - getNewTransaction: () => { - return {} as Transaction; - }, - title: '', - schema: PAC_JF_TRANSFER_MEMO, - transaction: transaction, - isDependentChild: false, - updateParentOnSave: false, - getSchemaName: () => 'foo', - } as TransactionType; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - it('#save() should save a new com record', () => { - const testContact: Contact = new Contact(); - testContact.id = 'testId'; - spyOn(testContactService, 'create').and.returnValue(of(testContact)); - spyOn(testConfirmationService, 'confirm').and.callFake((confirmation: Confirmation) => { - if (confirmation.accept) { - return confirmation.accept(); - } - }); - - const testTran = SchATransaction.fromJSON({ - ...transaction, - }); - component.form.patchValue({ ...testTran }); - if (component.transactionType?.transaction) { - component.transactionType.transaction = testTran; - component.transactionType.transaction.id = undefined; - component.transactionType.transaction.contact = testContact; - } - fixture.detectChanges(); - component.handleNavigate(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, testTran)); - expect(component.form.invalid).toBe(false); - const req = httpTestingController.expectOne(`${environment.apiUrl}/transactions/schedule-a/`); - expect(req.request.method).toEqual('POST'); - httpTestingController.verify(); - }); - - it('#save() should not save an invalid record', () => { - component.form.patchValue({ ...transaction, ...{ contributor_state: 'not-valid' } }); - component.save(new NavigationEvent(NavigationAction.SAVE, NavigationDestination.LIST, transaction)); - expect(component.form.invalid).toBe(true); - httpTestingController.expectNone( - `${environment.apiUrl}/transactions/schedule-a/1/?schema=PAC_JF_TRANSFER_MEMO&fields_to_validate=` - ); - httpTestingController.verify(); - }); -}); diff --git a/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.ts b/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.ts deleted file mode 100644 index ab940f9371..0000000000 --- a/front-end/src/app/transactions/transaction-group-f/transaction-group-f.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { TransactionTypeBaseComponent } from 'app/shared/components/transaction-type-base/transaction-type-base.component'; -import { LabelUtils, PrimeOptions } from 'app/shared/utils/label.utils'; -import { ContactTypeLabels, ContactTypes } from '../../shared/models/contact.model'; - -@Component({ - selector: 'app-transaction-group-f', - templateUrl: './transaction-group-f.component.html', -}) -export class TransactionGroupFComponent extends TransactionTypeBaseComponent implements OnInit, OnDestroy { - formProperties: string[] = [ - 'entity_type', - 'contributor_organization_name', - 'donor_committee_fec_id', - 'contributor_street_1', - 'contributor_street_2', - 'contributor_city', - 'contributor_state', - 'contributor_zip', - 'contribution_date', - 'contribution_amount', - 'contribution_aggregate', - 'contribution_purpose_descrip', - 'memo_code', - 'memo_text_input', - ]; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); -} diff --git a/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.html b/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.html index aba3a13c19..28b9a2bb47 100644 --- a/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.html +++ b/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.html @@ -1,5 +1,5 @@ -

    {{ transactionType?.title }}

    +

    {{ transaction?.transactionType?.title }}

    This receipt type requires a memo transaction. Follow this two-step process to create both an earmark receipt and an earmark memo: @@ -17,42 +17,48 @@

    Contact

    - - +
    - +

    Address

    - +

    Receipt Information

    Additional Information

    This type of receipt requires a memo transaction

    @@ -77,55 +83,67 @@

    Conduit

    - - +
    - + - +

    Address

    Employer

    - +

    Receipt Information

    Additional Information

    @@ -135,7 +153,7 @@

    Additional Information

    diff --git a/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.spec.ts b/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.spec.ts index 2e336675c8..290733269b 100644 --- a/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.spec.ts +++ b/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.spec.ts @@ -4,10 +4,9 @@ import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; -import { PAC_EARMARK_MEMO } from 'app/shared/models/transaction-types/PAC_EARMARK_MEMO.model'; -import { PAC_EARMARK_RECEIPT } from 'app/shared/models/transaction-types/PAC_EARMARK_RECEIPT.model'; +import { ScheduleATransactionTypes } from 'app/shared/models/scha-transaction.model'; import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; +import { getTestTransactionByType, testMockStore, testTemplateMap } from 'app/shared/utils/unit-test.utils'; import { AccordionModule } from 'primeng/accordion'; import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; @@ -27,12 +26,6 @@ describe('TransactionGroupFgComponent', () => { let component: TransactionGroupFgComponent; let fixture: ComponentFixture; - const pacEarmarkReceipt = new PAC_EARMARK_RECEIPT(); - pacEarmarkReceipt.transaction = pacEarmarkReceipt.getNewTransaction(); - const pacEarmarkMemo = new PAC_EARMARK_MEMO(); - pacEarmarkMemo.transaction = pacEarmarkMemo.getNewTransaction(); - pacEarmarkReceipt.childTransactionType = pacEarmarkMemo; - beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ @@ -62,12 +55,14 @@ describe('TransactionGroupFgComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(TransactionGroupFgComponent); component = fixture.componentInstance; + component.transaction = getTestTransactionByType(ScheduleATransactionTypes.PAC_EARMARK_RECEIPT); + component.templateMap = testTemplateMap; + component.childTransaction = getTestTransactionByType(ScheduleATransactionTypes.PAC_EARMARK_MEMO); + component.childTemplateMap = testTemplateMap; fixture.detectChanges(); }); it('should create', () => { - component.transactionType = pacEarmarkReceipt; - component.ngOnInit(); expect(component).toBeTruthy(); }); }); diff --git a/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.ts b/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.ts index 342e76ef3e..da81af105b 100644 --- a/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.ts +++ b/front-end/src/app/transactions/transaction-group-fg/transaction-group-fg.component.ts @@ -48,10 +48,9 @@ export class TransactionGroupFgComponent extends DoubleTransactionTypeBaseCompon 'memo_code', 'memo_text_input', ]; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); - override childContactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL, ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); + override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ContactTypes.COMMITTEE]); + override childContactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels, [ + ContactTypes.INDIVIDUAL, + ContactTypes.COMMITTEE, + ]); } diff --git a/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.html b/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.html deleted file mode 100644 index 718a80286a..0000000000 --- a/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.html +++ /dev/null @@ -1,66 +0,0 @@ - - -
    -

    {{ transactionType?.title }}

    -

    Contact

    -
    -
    -
    -
    - - - -
    -
    -
    - - - - - - - - -

    Address

    - - - - - - -

    Receipt Information

    - - -

    Additional Information

    - -
    - - -
    -
    - - diff --git a/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.spec.ts b/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.spec.ts deleted file mode 100644 index c0e9b9d87e..0000000000 --- a/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { RouterTestingModule } from '@angular/router/testing'; -import { provideMockStore } from '@ngrx/store/testing'; -import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ButtonModule } from 'primeng/button'; -import { CalendarModule } from 'primeng/calendar'; -import { CheckboxModule } from 'primeng/checkbox'; -import { ConfirmDialogModule } from 'primeng/confirmdialog'; -import { DividerModule } from 'primeng/divider'; -import { DropdownModule } from 'primeng/dropdown'; -import { InputNumberModule } from 'primeng/inputnumber'; -import { InputTextModule } from 'primeng/inputtext'; -import { InputTextareaModule } from 'primeng/inputtextarea'; -import { ToastModule } from 'primeng/toast'; -import { SharedModule } from '../../shared/shared.module'; -import { TransactionGroupGComponent } from './transaction-group-g.component'; - -describe('TransactionGroupGComponent', () => { - let component: TransactionGroupGComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - HttpClientTestingModule, - RouterTestingModule, - FormsModule, - ReactiveFormsModule, - ToastModule, - SharedModule, - DividerModule, - DropdownModule, - CalendarModule, - ButtonModule, - CheckboxModule, - InputTextModule, - InputTextareaModule, - InputNumberModule, - ConfirmDialogModule, - ], - declarations: [TransactionGroupGComponent], - providers: [MessageService, ConfirmationService, FormBuilder, provideMockStore(testMockStore), FecDatePipe], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TransactionGroupGComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.ts b/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.ts deleted file mode 100644 index 0ece522909..0000000000 --- a/front-end/src/app/transactions/transaction-group-g/transaction-group-g.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { TransactionTypeBaseComponent } from 'app/shared/components/transaction-type-base/transaction-type-base.component'; -import { LabelUtils, PrimeOptions } from 'app/shared/utils/label.utils'; -import { ContactTypeLabels, ContactTypes } from '../../shared/models/contact.model'; - -@Component({ - selector: 'app-transaction-group-g', - templateUrl: './transaction-group-g.component.html', -}) -export class TransactionGroupGComponent extends TransactionTypeBaseComponent implements OnInit, OnDestroy { - formProperties: string[] = [ - 'entity_type', - 'contributor_organization_name', - 'contributor_last_name', - 'contributor_first_name', - 'contributor_middle_name', - 'contributor_prefix', - 'contributor_suffix', - 'contributor_street_1', - 'contributor_street_2', - 'contributor_city', - 'contributor_state', - 'contributor_zip', - 'contribution_date', - 'contribution_amount', - 'contribution_aggregate', - 'contribution_purpose_descrip', - 'contributor_employer', - 'contributor_occupation', - 'memo_code', - 'memo_text_input', - ]; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL, ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); -} diff --git a/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.html b/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.html deleted file mode 100644 index 866f012c3c..0000000000 --- a/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.html +++ /dev/null @@ -1,151 +0,0 @@ - -

    {{ transactionType?.title }}

    -

    - This type of receipt requires a memo transaction. Follow this two-step process to create both an earmark receipt and - an earmark memo: -

    - - - - STEP ONE: -

    Add receipt and contributor information

    -
    -

    Receipt

    -

    Contact

    -
    -
    -
    - - - -
    -
    - - - - - - - - -

    Address

    - - - -

    Employer

    - - -
    -

    Receipt Information

    - - -

    Additional Information

    - -
    -
    -
    -
    - - - - STEP TWO: -

    - Add earmarked memo and conduit information (REQUIRED FOR EARMARKED RECEIPTS) -

    -
    -

    Earmark memo

    -

    Conduit

    -
    -
    -
    -
    - - - -
    -
    -
    - - - - - - - - -

    Address

    - - - -

    Employer

    - - -
    -

    Receipt Information

    - - -

    Additional Information

    - -
    -
    -
    -
    -
    - - - - - diff --git a/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.spec.ts b/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.spec.ts deleted file mode 100644 index ec91b178d4..0000000000 --- a/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { RouterTestingModule } from '@angular/router/testing'; -import { provideMockStore } from '@ngrx/store/testing'; -import { EARMARK_MEMO } from 'app/shared/models/transaction-types/EARMARK_MEMO.model'; -import { FecDatePipe } from 'app/shared/pipes/fec-date.pipe'; -import { testMockStore } from 'app/shared/utils/unit-test.utils'; -import { AccordionModule } from 'primeng/accordion'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { ButtonModule } from 'primeng/button'; -import { CalendarModule } from 'primeng/calendar'; -import { CheckboxModule } from 'primeng/checkbox'; -import { DividerModule } from 'primeng/divider'; -import { DropdownModule } from 'primeng/dropdown'; -import { InputNumberModule } from 'primeng/inputnumber'; -import { InputTextModule } from 'primeng/inputtext'; -import { InputTextareaModule } from 'primeng/inputtextarea'; -import { ToastModule } from 'primeng/toast'; -import { EARMARK_RECEIPT } from '../../shared/models/transaction-types/EARMARK_RECEIPT.model'; -import { SharedModule } from '../../shared/shared.module'; -import { TransactionGroupGgComponent } from './transaction-group-gg.component'; -import { ConfirmDialogModule } from 'primeng/confirmdialog'; - -describe('TransactionGroupGgComponent', () => { - let component: TransactionGroupGgComponent; - let fixture: ComponentFixture; - - const earmarkReceipt = new EARMARK_RECEIPT(); - earmarkReceipt.transaction = earmarkReceipt.getNewTransaction(); - const earmarkMemo = new EARMARK_MEMO(); - earmarkMemo.transaction = earmarkMemo.getNewTransaction(); - earmarkReceipt.childTransactionType = earmarkMemo; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - HttpClientTestingModule, - RouterTestingModule, - FormsModule, - ReactiveFormsModule, - ToastModule, - SharedModule, - DividerModule, - DropdownModule, - CalendarModule, - ButtonModule, - AccordionModule, - CheckboxModule, - InputTextModule, - InputTextareaModule, - InputNumberModule, - BrowserAnimationsModule, - ConfirmDialogModule, - ], - declarations: [TransactionGroupGgComponent], - providers: [MessageService, ConfirmationService, FormBuilder, provideMockStore(testMockStore), FecDatePipe], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TransactionGroupGgComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - component.transactionType = earmarkReceipt; - component.ngOnInit(); - expect(component).toBeTruthy(); - }); -}); diff --git a/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.ts b/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.ts deleted file mode 100644 index 374e83a781..0000000000 --- a/front-end/src/app/transactions/transaction-group-gg/transaction-group-gg.component.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ContactTypeLabels, ContactTypes } from 'app/shared/models/contact.model'; -import { LabelUtils, PrimeOptions } from 'app/shared/utils/label.utils'; -import { DoubleTransactionTypeBaseComponent } from 'app/shared/components/transaction-type-base/double-transaction-type-base.component'; - -@Component({ - selector: 'app-transaction-group-gg', - templateUrl: './transaction-group-gg.component.html', -}) -export class TransactionGroupGgComponent extends DoubleTransactionTypeBaseComponent implements OnInit, OnDestroy { - formProperties: string[] = [ - 'entity_type', - 'contributor_organization_name', - 'contributor_last_name', - 'contributor_first_name', - 'contributor_middle_name', - 'contributor_prefix', - 'contributor_suffix', - 'contributor_street_1', - 'contributor_street_2', - 'contributor_city', - 'contributor_state', - 'contributor_zip', - 'contribution_date', - 'contribution_amount', - 'contribution_aggregate', - 'contribution_purpose_descrip', - 'contributor_employer', - 'contributor_occupation', - 'donor_committee_fec_id', - 'memo_code', - 'memo_text_input', - ]; - - childFormProperties: string[] = [ - 'entity_type', - 'contributor_organization_name', - 'contributor_last_name', - 'contributor_first_name', - 'contributor_middle_name', - 'contributor_prefix', - 'contributor_suffix', - 'contributor_street_1', - 'contributor_street_2', - 'contributor_city', - 'contributor_state', - 'contributor_zip', - 'contribution_date', - 'contribution_amount', - 'contribution_aggregate', - 'contribution_purpose_descrip', - 'contributor_employer', - 'contributor_occupation', - 'donor_committee_fec_id', - 'memo_code', - 'memo_text_input', - ]; - override contactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL, ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); - override childContactTypeOptions: PrimeOptions = LabelUtils.getPrimeOptions(ContactTypeLabels).filter((option) => - [ContactTypes.INDIVIDUAL, ContactTypes.COMMITTEE].includes(option.code as ContactTypes) - ); -} diff --git a/front-end/src/app/transactions/transaction-list/transaction-list.component.html b/front-end/src/app/transactions/transaction-list/transaction-list.component.html index 9fb0473c83..68c0871f1d 100644 --- a/front-end/src/app/transactions/transaction-list/transaction-list.component.html +++ b/front-end/src/app/transactions/transaction-list/transaction-list.component.html @@ -38,20 +38,18 @@
    Transactions during coverage dates
    Transaction type - - Name - - - Contribution date + Name + + Contribution date Memo code - - Amount + + Amount - - Aggregate + + Aggregate Transaction ID Associated With @@ -62,24 +60,29 @@
    Transactions during coverage dates
    {{ - item.transaction_type_identifier | label : scheduleATransactionTypeLabels + item.transaction_type_identifier | label : scheduleTransactionTypeLabels }}
    Unitemized
    - {{ item.contributor_organization_name || item.contributor_last_name + ', ' + item.contributor_first_name }} + {{ + item[item.transactionType.templateMap.organization_name] || + item[item.transactionType.templateMap.last_name] + + ', ' + + item[item.transactionType.templateMap.first_name] + }} - {{ item.contribution_date | fecDate }} + {{ item[item.transactionType.templateMap.date] | fecDate }} {{ item.memo_code | memoCode }} - {{ item.contribution_amount | currency }} + {{ item[item.transactionType.templateMap.amount] | currency }} - {{ item.contribution_aggregate | currency }} + {{ item[item.transactionType.templateMap.aggregate] | currency }} {{ item.transaction_id }} diff --git a/front-end/src/app/transactions/transaction-list/transaction-list.component.ts b/front-end/src/app/transactions/transaction-list/transaction-list.component.ts index dbed158552..3d7eabf20f 100644 --- a/front-end/src/app/transactions/transaction-list/transaction-list.component.ts +++ b/front-end/src/app/transactions/transaction-list/transaction-list.component.ts @@ -9,6 +9,7 @@ import { Transaction } from 'app/shared/models/transaction.model'; import { ConfirmationService, MessageService } from 'primeng/api'; import { TransactionService } from 'app/shared/services/transaction.service'; import { ScheduleATransactionTypeLabels } from 'app/shared/models/scha-transaction.model'; +import { ScheduleBTransactionTypeLabels } from 'app/shared/models/schb-transaction.model'; import { LabelList } from 'app/shared/utils/label.utils'; @Component({ @@ -18,7 +19,7 @@ import { LabelList } from 'app/shared/utils/label.utils'; export class TransactionListComponent extends TableListBaseComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); report: F3xSummary | undefined; - scheduleATransactionTypeLabels: LabelList = ScheduleATransactionTypeLabels; + scheduleTransactionTypeLabels: LabelList = [...ScheduleATransactionTypeLabels, ...ScheduleBTransactionTypeLabels]; constructor( protected override messageService: MessageService, diff --git a/front-end/src/app/transactions/transaction-type-picker/transaction-type-picker.component.ts b/front-end/src/app/transactions/transaction-type-picker/transaction-type-picker.component.ts index 78fd9c3e1d..390e027717 100644 --- a/front-end/src/app/transactions/transaction-type-picker/transaction-type-picker.component.ts +++ b/front-end/src/app/transactions/transaction-type-picker/transaction-type-picker.component.ts @@ -5,6 +5,7 @@ import { Subject, takeUntil } from 'rxjs'; import { Store } from '@ngrx/store'; import { selectActiveReport } from 'app/store/active-report.selectors'; import { Report } from 'app/shared/interfaces/report.interface'; +import { TransactionTypes, TransactionGroupTypes } from 'app/shared/models/transaction.model'; import { ScheduleATransactionGroups, ScheduleATransactionGroupsType, @@ -21,8 +22,6 @@ import { LabelList } from 'app/shared/utils/label.utils'; import { getTransactionTypeClass } from 'app/shared/utils/transaction-type.utils'; type Categories = 'receipt' | 'disbursement'; -type GroupsTypes = ScheduleATransactionGroupsType | ScheduleBTransactionGroupsType; -type TransactionTypes = ScheduleATransactionTypes | ScheduleBTransactionTypes; @Component({ selector: 'app-transaction-type-picker', @@ -55,7 +54,7 @@ export class TransactionTypePickerComponent implements OnInit, OnDestroy { this.destroy$.complete(); } - getTransactionGroups(): GroupsTypes[] { + getTransactionGroups(): TransactionGroupTypes[] { if (this.category == 'disbursement') { return [ ScheduleBTransactionGroups.OPERATING_EXPENDITURES, @@ -74,7 +73,7 @@ export class TransactionTypePickerComponent implements OnInit, OnDestroy { ]; } - getTransactionTypes(group: GroupsTypes): TransactionTypes[] { + getTransactionTypes(group: TransactionGroupTypes): TransactionTypes[] { switch (group) { case ScheduleATransactionGroups.CONTRIBUTIONS_FROM_INDIVIDUALS_PERSONS: return [ @@ -128,6 +127,7 @@ export class TransactionTypePickerComponent implements OnInit, OnDestroy { ScheduleATransactionTypes.PARTY_RECOUNT_RECEIPT, ScheduleATransactionTypes.PAC_RECOUNT_RECEIPT, ScheduleATransactionTypes.TRIBAL_RECOUNT_RECEIPT, + ScheduleATransactionTypes.PARTNERSHIP_RECOUNT_ACCOUNT_RECEIPT, ScheduleATransactionTypes.INDIVIDUAL_NATIONAL_PARTY_RECOUNT_ACCOUNT, ScheduleATransactionTypes.PARTY_NATIONAL_PARTY_RECOUNT_ACCOUNT, ScheduleATransactionTypes.PAC_NATIONAL_PARTY_RECOUNT_ACCOUNT, @@ -144,6 +144,8 @@ export class TransactionTypePickerComponent implements OnInit, OnDestroy { ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_CONVENTION_ACCOUNT_CONTRIBUTION, ScheduleATransactionTypes.EARMARK_RECEIPT_FOR_HEADQUARTERS_ACCOUNT_CONTRIBUTION, ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_RECOUNT_ACCOUNT, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_CONVENTION_ACCOUNT, + ScheduleATransactionTypes.PARTNERSHIP_NATIONAL_PARTY_HEADQUARTERS_ACCOUNT, ]; case ScheduleBTransactionGroups.OPERATING_EXPENDITURES: return [ @@ -217,7 +219,7 @@ export class TransactionTypePickerComponent implements OnInit, OnDestroy { getRouterLink(transactionType: string): string | undefined { if (this.report && !this.isTransactionDisabled(transactionType)) { - return `/transactions/report/${this.report?.id}}/create/${transactionType}`; + return `/transactions/report/${this.report?.id}/create/${transactionType}`; } return undefined; } diff --git a/front-end/src/app/transactions/transactions-routing.module.ts b/front-end/src/app/transactions/transactions-routing.module.ts index 1642dff29f..04378621e1 100644 --- a/front-end/src/app/transactions/transactions-routing.module.ts +++ b/front-end/src/app/transactions/transactions-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { ReportResolver } from 'app/shared/resolvers/report.resolver'; -import { TransactionTypeResolver } from 'app/shared/resolvers/transaction-type.resolver'; +import { TransactionResolver } from 'app/shared/resolvers/transaction.resolver'; import { ReportIsEditableGuard } from '../shared/guards/report-is-editable.guard'; import { TransactionContainerComponent } from './transaction-container/transaction-container.component'; import { TransactionTypePickerComponent } from './transaction-type-picker/transaction-type-picker.component'; @@ -30,7 +30,7 @@ const routes: Routes = [ path: 'report/:reportId/create/:transactionType', component: TransactionContainerComponent, resolve: { - transactionType: TransactionTypeResolver, + transaction: TransactionResolver, }, canActivate: [ReportIsEditableGuard], }, @@ -38,14 +38,14 @@ const routes: Routes = [ path: 'report/:reportId/list/edit/:transactionId', component: TransactionContainerComponent, resolve: { - transactionType: TransactionTypeResolver, + transaction: TransactionResolver, }, }, { path: 'report/:reportId/list/edit/:parentTransactionId/create-sub-transaction/:transactionType', component: TransactionContainerComponent, resolve: { - transactionType: TransactionTypeResolver, + transaction: TransactionResolver, }, canActivate: [ReportIsEditableGuard], }, diff --git a/front-end/src/app/transactions/transactions.module.ts b/front-end/src/app/transactions/transactions.module.ts index 18fed82eb6..856e6f10a1 100644 --- a/front-end/src/app/transactions/transactions.module.ts +++ b/front-end/src/app/transactions/transactions.module.ts @@ -24,10 +24,7 @@ import { TransactionGroupBComponent } from './transaction-group-b/transaction-gr import { TransactionGroupCComponent } from './transaction-group-c/transaction-group-c.component'; import { TransactionGroupDComponent } from './transaction-group-d/transaction-group-d.component'; import { TransactionGroupEComponent } from './transaction-group-e/transaction-group-e.component'; -import { TransactionGroupFComponent } from './transaction-group-f/transaction-group-f.component'; -import { TransactionGroupGComponent } from './transaction-group-g/transaction-group-g.component'; import { TransactionGroupAgComponent } from './transaction-group-ag/transaction-group-ag.component'; -import { TransactionGroupGgComponent } from './transaction-group-gg/transaction-group-gg.component'; import { TransactionGroupFgComponent } from './transaction-group-fg/transaction-group-fg.component'; import { MemoCodePipe, TransactionListComponent } from './transaction-list/transaction-list.component'; import { TransactionTypePickerComponent } from './transaction-type-picker/transaction-type-picker.component'; @@ -44,10 +41,7 @@ import { TransactionsRoutingModule } from './transactions-routing.module'; TransactionGroupCComponent, TransactionGroupDComponent, TransactionGroupEComponent, - TransactionGroupFComponent, - TransactionGroupGComponent, TransactionGroupAgComponent, - TransactionGroupGgComponent, TransactionGroupFgComponent, ], imports: [ diff --git a/front-end/src/assets/styles/typography.css b/front-end/src/assets/styles/typography.css index 5f871dd7f6..4063758661 100644 --- a/front-end/src/assets/styles/typography.css +++ b/front-end/src/assets/styles/typography.css @@ -258,12 +258,6 @@ body { padding: 0; position: relative; } -main[aria-hidden='true'] { - display: block !important; -} -[aria-hidden='true'] { - display: none !important; -} @media print { footer, nav { diff --git a/front-end/src/styles.scss b/front-end/src/styles.scss index eff8a2e20a..9135d509c9 100644 --- a/front-end/src/styles.scss +++ b/front-end/src/styles.scss @@ -130,6 +130,13 @@ label.disabled { font-size: 48px; } +.form-type-dialog { + width: 50%; +} +.form-type-dialog p-dropdown { + display: flex; +} + // PRIMENG PANELMENU STYLE OVERRIDE .menu-item-disabled { @@ -273,3 +280,23 @@ label.disabled { color: black; border: 1px solid black; } + +.action-options { + &:before { + content:none !important; + } + + &:after { + content:none !important; + } + + padding: 0; + margin-top: 0px !important; +} +.action-options .p-overlaypanel-content { + padding: .5em; +} +.action-options .p-overlaypanel-content .p-button { + width: 100%; + text-align: inherit; +} \ No newline at end of file