diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000000..166302f8911 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "extends": [ + "@salesforce/eslint-config-lwc/recommended" + ], + "globals": { + "clearDOM": "readonly", + "flushPromises": "readonly" + }, + "rules": { + "no-use-before-define": [ + "error", + { + "functions": false, + "classes": true, + "variables": true + } + ], + "@lwc/no-async-await": "off" + } +} diff --git a/.forceignore b/.forceignore index 02b2d9c2328..860395bcb93 100644 --- a/.forceignore +++ b/.forceignore @@ -4,4 +4,7 @@ # LWC Jest **/__tests__/** -**/__mocks__/** \ No newline at end of file +**/__mocks__/** +**/tsconfig.json + +**/*.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7fbaa1ea9cc..7340db555b3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,4 +13,9 @@ /runtime.txt @SalesforceFoundation/release-engineering-reviewers # GitHub Actions -.github/workflows/ @SalesforceFoundation/release-engineering-reviewers \ No newline at end of file +.github/workflows/ @SalesforceFoundation/release-engineering-reviewers + +# NPSP Special Folders +/unpackaged/config/core_instrumentation_mock_classes/ @force2b +/unpackaged/config/crlp_testing/ @force2b +/force-app/main/instrumentation/ @force2b \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/read-this-for-all-support-and-questions.md b/.github/ISSUE_TEMPLATE/read-this-for-all-support-and-questions.md new file mode 100644 index 00000000000..86f7cb91d71 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/read-this-for-all-support-and-questions.md @@ -0,0 +1,15 @@ +--- +name: READ THIS FOR ALL SUPPORT AND QUESTIONS +about: 'GO HERE: http://sfdc.co/npchub' +title: '' +labels: invalid +assignees: '' + +--- + +==== IMPORTANT NOTES ==== + +The Nonprofit Success Pack team does not review or respond to support requests or questions posted in this repository. + +Instead, please post all questions and issues directly in the Nonprofit Hub of the Trailblazer Community: http://sfdc.co/npchub +======================================================================== diff --git a/.github/workflows/compliance.yml b/.github/workflows/compliance.yml new file mode 100644 index 00000000000..1a9e6136db3 --- /dev/null +++ b/.github/workflows/compliance.yml @@ -0,0 +1,138 @@ +##################################################################################################################### +# @author Michael Smith +# @date 2021-08-23 +# @description As SFDO Managed Packages start using the newly introduced Instrumentation Services to 'log' +# usage data to Splunk and Argus, this Compliance Check action does two things: +# 1. Ensures that Packaged Apex Classes do not make any direct reference to the core SfdoLogUtils class. +# Any code references to this class will cause the Compliance build to fail. +# 2. At least in the short term, adds an Instrumentation reviewer to any PR that in any way references or +# or uses the SfdoInstrumentationService. This is primarily meant as a short term action to aid in +# adoption of the new instrumentation services. +##################################################################################################################### +name: "Instrumentation Compliance" +on: + pull_request: + # Filter the job to only execute if CLS files (not in the unpackaged folder) are in the PR + paths: + - '**.cls' + - '!unpackaged/**.cls' + +jobs: + Instrumentation_Service_Compliance_Verification: + + # Constant vars + # These should be adjusted per repository + env: + DEFAULT_REVIEWER: force2b + ALTERNATE_REVIEWER: lparrott + ASSIGNED_LABEL: Instrumentation + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Get PR Owner + id: pr_owner + run: | + echo "::set-output name=owner::${{ github.event.pull_request.user.login }}" + + - name: Set Reviewer + id: set_reviewer + run: | + if [[ "${{ steps.pr_owner.outputs.owner != env.DEFAULT_REVIEWER}}" == "true" ]] + then + echo "::set-output name=reviewer::${{ env.DEFAULT_REVIEWER }}" + else + echo "::set-output name=reviewer::${{ env.ALTERNATE_REVIEWER }}" + fi + + - name: Check for previously assigned Instrumentation Label + id: label_check + run: | + echo "::set-output name=prlabels::${{ join(github.event.pull_request.labels.*.name, ',') }}" + + - name: Check for references to the SfdoLogUtils or SfdoInstrumentationServices Apex classes + # If the Instrumentation label is already assigned to the PR, all checks are skipped to avoid redundant PR comments and alerts + id: get_pr_diff + if: contains(github.event.pull_request.labels.*.name, env.ASSIGNED_LABEL) == false + run: | + echo "- Did not find label '${{env.ASSIGNED_LABEL}}' in '${{steps.label_check.outputs.prlabels}}'" + echo "- Current PR Owner: '${{steps.pr_owner.outputs.owner}}'" + echo "- Assigned Reviewer: '${{steps.set_reviewer.outputs.reviewer}}'" + if [ $GITHUB_BASE_REF ]; then + # This changes the context of subseqent commands to get the full change for the PR + git fetch origin $GITHUB_HEAD_REF:$GITHUB_HEAD_REF + + # Find references to "Sfdo" in the force-app/ folder structure. Filters for the specific class references in later steps. + export DIFF=$( git diff-tree origin/$GITHUB_BASE_REF..$GITHUB_HEAD_REF --patch-with-raw -- force-app/** | grep 'Sfdo' ) + fi + + # Assign the "git diff-tree" output to an internal workflow var to use later + # Escape newlines (replace \n with %0A) so it's a single line of text + echo "::set-output name=diff::$( echo "$DIFF" | sed ':a;N;$!ba;s/\n/%0A/g' )" + + - name: Assign Reviewer if SfdoInstrumentationService is in use + id: assign_reviewers_if_instrumentation + if: contains(steps.get_pr_diff.outputs.diff, 'SfdoInstrumentationService') + uses: SalesforceFoundation/github-script@v4 + with: + script: | + github.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: [ "${{env.ASSIGNED_LABEL}}" ] + }) + github.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + assignees: [ "${{steps.set_reviewer.outputs.reviewer}}" ] + }) + github.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + reviewers: [ "${{steps.set_reviewer.outputs.reviewer}}" ] + }) + github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: ':warning: @${{steps.set_reviewer.outputs.reviewer}} This Pull Request references the "SfdoInstrumentationService" class and must be reviewed by an Instrumentation owner. :warning:' + }) + + - name: Fail Build if SfdoLogUtils core class is directly referenced + id: assign_reviewers_if_core_class + if: contains(steps.get_pr_diff.outputs.diff, 'SfdoLogUtils.log(') + uses: SalesforceFoundation/github-script@v4 + with: + script: | + github.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: [ "${{env.ASSIGNED_LABEL}}" ] + }) + github.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + assignees: [ "${{steps.set_reviewer.outputs.reviewer}}" ] + }) + github.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + reviewers: [ "${{steps.set_reviewer.outputs.reviewer}}" ] + }) + github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: ':no_entry: @${{steps.set_reviewer.outputs.reviewer}} This Pull Request references the "SfdoLogUtils" class which should not be called directly by package code :no_entry:' + }) + core.setFailed('This Pull Request may be calling an SfdoLogUtils.log() method which should not be called directly by package code') + diff --git a/.gitignore b/.gitignore index ccbbe1f5506..56d032e1675 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ Referenced Packages *.sublime-project *.sublime-workspace **/.sfdx/ +**/.sf/ **/.vscode/ **/.idea/ **/.mypy_cache/ @@ -42,7 +43,9 @@ robot/Cumulus/results/ datasets/dev_org/test_data.db *.db .cci - +.sfdx +.sf # LWC force-app/main/default/lwc/.eslintrc.json /node_modules +/coverage diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 00000000000..7343fb69224 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,2 @@ +tabWidth: 4 +printWidth: 120 \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000000..ce7a494556d --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# Comment line immediately above ownership line is reserved for related other information. Please be careful while editing. +#ECCN:Open Source diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..b4612a7bc59 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,105 @@ +# Salesforce Open Source Community Code of Conduct + +## About the Code of Conduct + +Equality is a core value at Salesforce. We believe a diverse and inclusive +community fosters innovation and creativity, and are committed to building a +culture where everyone feels included. + +Salesforce open-source projects are committed to providing a friendly, safe, and +welcoming environment for all, regardless of gender identity and expression, +sexual orientation, disability, physical appearance, body size, ethnicity, nationality, +race, age, religion, level of experience, education, socioeconomic status, or +other similar personal characteristics. + +The goal of this code of conduct is to specify a baseline standard of behavior so +that people with different social values and communication styles can work +together effectively, productively, and respectfully in our open source community. +It also establishes a mechanism for reporting issues and resolving conflicts. + +All questions and reports of abusive, harassing, or otherwise unacceptable behavior +in a Salesforce open-source project may be reported by contacting the Salesforce +Open Source Conduct Committee at ossconduct@salesforce.com. + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of gender +identity and expression, sexual orientation, disability, physical appearance, +body size, ethnicity, nationality, race, age, religion, level of experience, education, +socioeconomic status, or other similar personal characteristics. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy toward other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Personal attacks, insulting/derogatory comments, or trolling +* Public or private harassment +* Publishing, or threatening to publish, others' private information—such as +a physical or electronic address—without explicit permission +* Other conduct which could reasonably be considered inappropriate in a +professional setting +* Advocating for or encouraging any of the above behaviors + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned with this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project email +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the Salesforce Open Source Conduct Committee +at ossconduct@salesforce.com. All complaints will be reviewed and investigated +and will result in a response that is deemed necessary and appropriate to the +circumstances. The committee is obligated to maintain confidentiality with +regard to the reporter of an incident. Further details of specific enforcement +policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership and the Salesforce Open Source Conduct +Committee. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home], +version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. +It includes adaptions and additions from [Go Community Code of Conduct][golang-coc], +[CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc]. + +This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us]. + +[contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/) +[golang-coc]: https://golang.org/conduct +[cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md +[microsoft-coc]: https://opensource.microsoft.com/codeofconduct/ +[cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..a37b5918f02 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,21 @@ +# Contributing Guide For NPSP + +This page lists the operational governance model of this project, as well as the recommendations and requirements for how to best contribute to NPSP. We strive to obey these as best as possible. As always, thanks for contributing – we hope these guidelines make it easier and shed some light on our approach and processes. + +# Governance Model + +## Salesforce Sponsored + +The intent and goal of open sourcing this project is to increase the contributor and user base. However, only Salesforce employees will be given `admin` rights and will be the final arbitrars of what contributions are accepted or not. + +# Issues, requests & ideas + +The Nonprofit Success Pack team does not review or respond to support requests or questions posted in this repository. + +Instead, please post all questions and issues directly in the Nonprofit Hub of the Trailblazer Community: http://sfdc.co/npchub + +# Code of Conduct +Please follow our [Code of Conduct](CODE_OF_CONDUCT.md). + +# License +By contributing your code, you agree to license your contribution under the terms of our project [LICENSE](LICENSE) and to sign the [Salesforce CLA](https://cla.salesforce.com/sign-cla) diff --git a/README.md b/README.md index 6b840f6e297..cfaf31d2434 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,36 @@ ![Salesforce Nonprofit Success Pack](https://cloud.githubusercontent.com/assets/450473/18836784/15e1774a-83c7-11e6-8434-0521d4fbebc0.png "Salesforce Nonprofit Success Pack") -#### For Users +## Important Note -* Ask questions or get help -* Log a confirmed Issue or Feature Request -* User Documentation -* Check out existing bugs and feature and enhancement requests. -* Release Notes and Beta Releases +In 2023, Salesforce launched the Nonprofit Cloud which is at the foundation of Salesforce for Nonprofits. Nonprofit Cloud helps teams unify their data to work beyond silos and better collaborate, share, learn from, and use their data. Use Nonprofit Cloud as your a single solution to begin or continue in your digital transformation journey. Read more about the [new Nonprofit Cloud](https://www.salesforce.com/blog/new-nonprofit-cloud/). Some key highlights: + +* Salesforce will to continue support our managed package products, including the Nonprofit Success Pack. Tens of thousands of customers use our current managed package products to achieve great results. +* Many of our ISV partners are building fantastic solutions for our managed packages as well as the new Nonprofit Cloud. We’re working with these partners to ensure that they understand our new product architecture, and are able to bring their years of experience and learnings to the new Nonprofit Cloud. +* Salesforce remains deeply committed to nonprofit pricing. There are many new capabilities within the new Nonprofit Cloud and we work hard to maintain our nonprofit discounts across the new products. Our goals are to simplify access to nonprofit technology and make it easier for customers to get started. +* Salesforce continues to grant free licenses with the Power of Us Program for the managed packages and Nonprofit Cloud. With Nonprofit Cloud, the Power of Us program also includes more features than ever to accelerate and deepen the nonprofit experience. +* Take the new product for a test drive. Sign-up for a [Nonprofit Cloud Trial org](https://help.salesforce.com/s/articleView?id=sfdo.NPC_Create_Nonprofit_Cloud_Trial_Org.htm&type=5). +* Visit the [Nonprofit Hub](https://trailhead.salesforce.com/trailblazer-community/groups/0F9300000001ocxCAA?tab=discussion&sort=LAST_MODIFIED_DATE_DESC) to chat with others about how nonprofits use Salesforce for social good. + +--- +### For Nonprofit Success Pack Users and admins + +* Check out existing [Nonprofit feature and enhancement requests](https://ideas.salesforce.com/s/search#t=All&sort=relevancy&f:@sfcategoryfull=[Nonprofit%7CNonprofit%20Cloud,Nonprofit%7CNonprofit%20Success%20Pack%20(NPSP)%20-%20Managed%20Package]). +* [Ask questions or get help with the Nonprofit Success Pack](https://trailhead.salesforce.com/trailblazer-community/groups/0F94S000000kHitSAE) +* [Ask for support or questions with other Nonprofit Users and Partners](https://trailhead.salesforce.com/trailblazer-community/groups/0F9300000001ocxCAA) +* [Nonprofit Success Pack (NPSP) Documentation](https://help.salesforce.com/s/articleView?id=sfdo.Nonprofit_Success_Pack.htm) +* [Release Notes](https://sfdc.co/bnL4Cb) +* [Known Issues](https://issues.salesforce.com/#f[sfcategoryfull]=Nonprofit%7CNonprofit%20Success%20Pack%20(NPSP)%20-%20Managed%20Package) + +### Try out the Nonprofit Success Pack -#### Try it out You can install NPSP utilizing our custom application installer into any Developer Edition, Sandbox or Enterprise Edition Salesforce org. -* NPSP Installer -#### Resources -* NPSP Apex class documentation -* NPSP Data Dictionary +* [NPSP Installer](https://install.salesforce.org/products/npsp) + +### Try out the New Nonprofit Cloud -#### Meta +* [Nonprofit Cloud Learning Org Signup](https://help.salesforce.com/s/articleView?id=sfdo.NPC_Create_Nonprofit_Cloud_Trial_Org.htm&type=5) -The Nonprofit Success Pack (“NPSP”) is an open-source package licensed by Salesforce.org (“SFDO”) under the BSD-3 Clause License, found at https://opensource.org/licenses/BSD-3-Clause. ANY MASTER SUBSCRIPTION AGREEMENT YOU OR YOUR ENTITY MAY HAVE WITH SFDO DOES NOT APPLY TO YOUR USE OF NPSP. NPSP IS PROVIDED “AS IS” AND AS AVAILABLE, AND SFDO MAKES NO WARRANTY OF ANY KIND REGARDING NPSP, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, FREEDOM FROM DEFECTS OR NON-INFRINGEMENT, TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. -SFDO WILL HAVE NO LIABILITY ARISING OUT OF OR RELATED TO YOUR USE OF NPSP FOR ANY DIRECT DAMAGES OR FOR ANY LOST PROFITS, REVENUES, GOODWILL OR INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY, COVER, BUSINESS INTERRUPTION OR PUNITIVE DAMAGES, WHETHER AN ACTION IS IN CONTRACT OR TORT AND REGARDLESS OF THE THEORY OF LIABILITY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES OR IF A REMEDY OTHERWISE FAILS OF ITS ESSENTIAL PURPOSE. THE FOREGOING DISCLAIMER WILL NOT APPLY TO THE EXTENT PROHIBITED BY LAW. SFDO DISCLAIMS ALL LIABILITY AND INDEMNIFICATION OBLIGATIONS FOR ANY HARM OR DAMAGES CAUSED BY ANY THIRD-PARTY HOSTING PROVIDERS. +### Meta -_ducking-octo-happiness, laughing-archer_ +The Nonprofit Success Pack (“NPSP”) is an open-source package licensed by Salesforce.org (“SFDO”) under the BSD-3 Clause License, found at https://opensource.org/licenses/BSD-3-Clause. ANY MASTER SUBSCRIPTION AGREEMENT YOU OR YOUR ENTITY MAY HAVE WITH SFDO DOES NOT APPLY TO YOUR USE OF NPSP. NPSP IS PROVIDED “AS IS” AND AS AVAILABLE, AND SFDO MAKES NO WARRANTY OF ANY KIND REGARDING NPSP, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, FREEDOM FROM DEFECTS OR NON-INFRINGEMENT, TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. SFDO WILL HAVE NO LIABILITY ARISING OUT OF OR RELATED TO YOUR USE OF NPSP FOR ANY DIRECT DAMAGES OR FOR ANY LOST PROFITS, REVENUES, GOODWILL OR INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY, COVER, BUSINESS INTERRUPTION OR PUNITIVE DAMAGES, WHETHER AN ACTION IS IN CONTRACT OR TORT AND REGARDLESS OF THE THEORY OF LIABILITY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES OR IF A REMEDY OTHERWISE FAILS OF ITS ESSENTIAL PURPOSE. THE FOREGOING DISCLAIMER WILL NOT APPLY TO THE EXTENT PROHIBITED BY LAW. SFDO DISCLAIMS ALL LIABILITY AND INDEMNIFICATION OBLIGATIONS FOR ANY HARM OR DAMAGES CAUSED BY ANY THIRD-PARTY HOSTING PROVIDERS. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..e31774df287 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +## Security + +Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com) +as soon as it is discovered. This library limits its runtime dependencies in +order to reduce the total cost of ownership as much as can be, but all consumers +should remain vigilant and have their security stakeholders review all third-party +products (3PP) like this one and their dependencies. \ No newline at end of file diff --git a/StaticResourceSources/npsp-slds/npsp-common.css b/StaticResourceSources/npsp-slds/npsp-common.css index 7d781f4f9ec..34e695ff644 100644 --- a/StaticResourceSources/npsp-slds/npsp-common.css +++ b/StaticResourceSources/npsp-slds/npsp-common.css @@ -17,6 +17,7 @@ input.lookupInput { input.lookupInputSLDS { width: 100%; margin-right: -30px !important; + border-color: #747474 !important; } body .dateInput input[type="text"], .slds-vf-scope .dateInput input[type="text"] { margin-left: 0; diff --git a/cumulusci.yml b/cumulusci.yml index 8f4d4308eeb..2f7b3d2925f 100644 --- a/cumulusci.yml +++ b/cumulusci.yml @@ -1,11 +1,11 @@ -minimum_cumulusci_version: 3.23.0 +minimum_cumulusci_version: 3.74.0 project: name: Cumulus source_format: sfdx package: name: Nonprofit Success Pack namespace: npsp - api_version: 48.0 + api_version: 53.0 install_class: STG_InstallScript uninstall_class: STG_UninstallScript git: @@ -98,6 +98,14 @@ tasks: path: unpackaged/config/trial namespace_inject: $project_config.project__package__namespace + deploy_tso_config: + description: Deploys the post-install configuration for a trial/new org. + class_path: cumulusci.tasks.salesforce.Deploy + group: Salesforce Metadata + options: + path: unpackaged/config/trial_tso + namespace_inject: $project_config.project__package__namespace + deploy_pmm_trial_config: description: Deploys the PMM customizations for the trial metadata. PMM must be installed. class_path: cumulusci.tasks.salesforce.Deploy @@ -114,6 +122,14 @@ tasks: path: unpackaged/config/qa namespace_inject: $project_config.project__package__namespace + deploy_gift_entry_unmanaged: + description: Deploys additional metadata for use with Gift Entry + class_path: cumulusci.tasks.salesforce.Deploy + group: Salesforce Metadata + options: + path: unpackaged/gift_entry + namespace_inject: $project_config.project__package__namespace + retrieve_qa_config: description: Retrieves additional fields used for QA purposes class_path: cumulusci.tasks.salesforce.sourcetracking.RetrieveChanges @@ -205,13 +221,18 @@ tasks: options: update_future_releases: True + github_release: + options: + release_content: | + Check out the [Salesforce Release Notes](https://sfdc.co/bnL4Cb) or [Known Issues](https://issues.salesforce.com/) for details. + github_release_notes: options: trial_info: "`TBD`" is_rd2_enabled: description: This preflight check ensures that Enhanced Recurring Donations is enabled - class_path: tasks.is_rd2_enabled + class_path: tasks.check_rd2_enablement.is_rd2_enabled group: NPSP robot: @@ -582,6 +603,8 @@ tasks: - NPSP_Engagement_Plan_Record_Page - NPSP_GAU_Allocation - NPSP_General_Accounting_Unit + Class: + - SfdoLogUtils enable_pilot_in_scratch_org: description: Initialize the PilotEnabled Feature Parameter override in a Scratch Org environment @@ -593,6 +616,13 @@ tasks: errSettings.%%%NAMESPACE%%%OverrideFeature_PilotEnabled__c = true; upsert errSettings; + deploy_core_mock_classes: + description: Deploy mock apex class(9)es) that will prevent compilation errors when the packaged code references file-based apex in the core stack for instrumentation purposes + class_path: cumulusci.tasks.salesforce.Deploy + group: Salesforce Metadata + options: + path: unpackaged/config/core_instrumentation_mock_classes + run_tests: options: retry_failures: @@ -627,11 +657,6 @@ tasks: options: output_style: salesforce elements: - # Strip all underlying field translations, leaving translations for new fields in these objects - - path: force-app/main/default/*ranslations/**/*ranslation-meta.xml - xpath: /*/*[re:match(ns:name, '...+__..+')] - - path: force-app/tdtm/*ranslations/**/*ranslation-meta.xml - xpath: /*/*[re:match(ns:name, '...+__..+')] # Strip any underlying package object label translations - path: force-app/main/default/objectTranslations/npe01**/**objectTranslation-meta.xml xpath: //ns:layouts|//ns:nameFieldLabel|//ns:gender|//ns:caseValues|//ns:plural|//ns:startsWith @@ -722,13 +747,15 @@ tasks: xpath: //ns:gender|//ns:caseValues|//ns:nameFieldLabel|//ns:standardFields set_user_language: - description: Update the language of the Current User to the specified language as '-o param1 English' (Other languages are are Spanish, French, German and Dutch). + description: Update the language of the Current User to the specified language as '-o param1 english' class_path: cumulusci.tasks.apex.anon.AnonymousApexTask group: Org Config options: apex: >- User u = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId() LIMIT 1]; - Map languageMap = new Map{ 'English' => 'en_US', 'Spanish' => 'es', 'French' => 'fr', 'German' => 'de', 'Dutch' => 'nl_NL', 'english' => 'en_US', 'spanish' => 'es', 'french' => 'fr', 'german' => 'de', 'dutch' => 'nl_NL' }; + Map languageMap = new Map{ 'English' => 'en_US', 'Spanish' => 'es', + 'French' => 'fr', 'German' => 'de', 'Dutch' => 'nl_NL', 'Japanese' => 'ja', 'english' => 'en_US', + 'spanish' => 'es', 'french' => 'fr', 'german' => 'de', 'dutch' => 'nl_NL', 'japanese' => 'ja' }; u.LanguageLocaleKey = languageMap.get('%%%PARAM_1%%%'); Update u; @@ -736,8 +763,36 @@ tasks: description: Set the BDI Mapping mode to 'Help Text' or 'Data Import Field Mapping' class_path: tasks.set_BDI_mapping_mode.SetBDIMappingMode + prepare_unlocked_package_source: + description: Munge source code to be buildable as an Unlocked Package + class_path: cumulusci.tasks.util.FindReplace + options: + find: SfdoLogUtils + replace: SfdoLogUtilsUnlocked + path: force-app/main + file_pattern: + - "*.cls" flows: + + release_production: + steps: + 3: + task: None + + build_unlocked_test_package: + steps: + 0: + task: command + options: + command: | + cp unpackaged/config/core_instrumentation_mock_classes/classes/SfdoLogUtils.cls \ + force-app/main/default/classes/SfdoLogUtilsUnlocked.cls && \ + cp unpackaged/config/core_instrumentation_mock_classes/classes/SfdoLogUtils.cls-meta.xml \ + force-app/main/default/classes/SfdoLogUtilsUnlocked.cls-meta.xml + 0.1: + task: prepare_unlocked_package_source + install_regression: steps: 1: @@ -824,6 +879,7 @@ flows: task: update_admin_profile 9: task: test_data_relationships + ignore_failure: true 10: task: deploy_reports 11: @@ -835,6 +891,12 @@ flows: options: api_names: QA,Gift_Entry + deploy_unmanaged: + description: Override to the deploy_unmanaged flow to force a mock apex class to be deployed that will prevent compilation errors when the packaged code references file-based apex in the core stack for instrumentation purposes. + steps: + 2.1: + when: not org_config.namespace + task: deploy_core_mock_classes qa_org_namespaced: description: Execute the qa_org flow against a namespaced scratch org and patch help text. @@ -857,9 +919,14 @@ flows: 3: task: update_admin_profile + qa_org_unlocked: + steps: + 2: + flow: config_regression + ci_feature_2gp_trial: steps: - 1: + 1: flow: install_2gp_commit 2: flow: config_apextest @@ -868,6 +935,35 @@ flows: 4: task: run_tests + config_trial_org: + steps: + 1: + flow: dependencies + 2: + task: install_managed + 3: + task: deploy_post + 4: + task: deploy_tso_config + 5: + task: test_data_relationships + ignore_failure: true + 6: + task: deploy_trial_translations + 7: + task: deploy_reports + 8: + flow: enable_rd2 + 9: + flow: enable_gift_entry + 10: + task: execute_anon + options: + path: scripts/trial/TrialCustomSettingsDefaults.cls + apex: updateCustomSettingsDefaults(); + 11: + task: update_admin_profile + config_trial: steps: 1: @@ -930,6 +1026,45 @@ flows: 2: task: update_admin_profile + enable_gift_entry: + description: Enable the Gift Entry feature. + group: NPSP + steps: + 1: + flow: enable_advanced_mapping + 2: + task: execute_anon + options: + apex: > + String namespace = ('%%%NAMESPACE%%%').replace('__',''); + Type t = Type.forName(namespace, 'Callable_Api'); + Callable apiClass = (Callable)t.newInstance(); + apiClass.call('settings.enablegiftentry', null); + + enable_advanced_mapping: + description: Enable the BDI Advanced Mapping feature. + group: NPSP + steps: + 1: + task: execute_anon + options: + apex: > + String namespace = ('%%%NAMESPACE%%%').replace('__',''); + Type t = Type.forName(namespace, 'Callable_Api'); + Callable apiClass = (Callable)t.newInstance(); + apiClass.call('settings.enableadvancedmapping', null); + 2: + task: custom_settings_value_wait + options: + object: "%%%NAMESPACE%%%Data_Import_Settings__c" + field: "%%%NAMESPACE%%%Field_Mapping_Method__c" + value: "Data Import Field Mapping" + + enable_rd2_managed: + steps: + 1: + flow: enable_rd2 + enable_rd2: description: 'Fully configures and enables a Scratch org with Enhanced Recurring Donations (unmanaged code only)' group: NPSP @@ -985,25 +1120,6 @@ flows: options: class_name: RD2_DataMigration_BATCH - enable_rd2_managed: - description: 'Fully configures and enables a Scratch org with Enhanced Recurring Donations (managed package only)' - group: NPSP - steps: - 1: - flow: enable_rd2 - options: - enable_crlp: - managed: true - param1: false - custom_settings_value_wait: - managed: true - execute_anon: - managed: true - unmanaged: false - deploy_rd2_config: - managed: true - unmanaged: false - config_managed: steps: 2: @@ -1112,6 +1228,7 @@ flows: flow: config_trial 5: task: test_data_relationships + ignore_failure: true 6: task: deploy_reports 7: @@ -1293,6 +1410,8 @@ flows: - Error__c.All - Grant_Deadline__c.All - Trigger_Handler__c.All_Triggers + Class: + - SfdoLogUtils 2.7: task: deploy_rd2_config ## It's necessary to redeploy the RD2 config after deploying the "packaged" unmanaged code to @@ -1589,6 +1708,59 @@ flows: path: ["force-app/main/default/featureParameters/Data_CountGERowsLast30DaysBatch.featureParameterInteger-meta.xml", "force-app/main/default/featureParameters/P10_Application_Date.featureParameterDate-meta.xml"] + robot_stable_no_rd2: + description: Runs all stable robot tests except those tagged "feature:RD2" + steps: + 1: + task: robot + options: + vars: 'BROWSER:headlesschrome,TIMEOUT:180.0' + exclude: unstable,deprecated,feature:RD2 + + robot_stable_rd2: + description: Runs stable robot tests tagged "feature:RD2" + steps: + 1: + task: robot + options: + vars: 'BROWSER:headlesschrome,TIMEOUT:180.0' + exclude: unstable,deprecated + include: feature:RD2 + + robot_stable_all: + description: Runs all stable robot tests except those tagged "feature:RD2", then enables RD2 and runs those tagged "feature:RD2" + steps: + 1: + task: robot + ignore_failure: true + options: + vars: 'BROWSER:headlesschrome,TIMEOUT:180.0' + exclude: unstable,deprecated,feature:RD2 + options: + outputdir: robot/Cumulus/results + output: output1.xml + log: log1.html + report: report1.html + 2: + flow: enable_rd2 + 3: + task: robot + ignore_failure: true + options: + vars: 'BROWSER:headlesschrome,TIMEOUT:180.0' + exclude: unstable,deprecated + include: feature:RD2 + options: + outputdir: robot/Cumulus/results + output: output2.xml + log: log2.html + report: report2.html + 4: + task: command + ignore_failure: true + options: + command: python3 -m robot.rebot --outputdir robot/Cumulus/results --output output.xml robot/Cumulus/results/output1.xml robot/Cumulus/results/output2.xml + orgs: scratch: dev_multicurrency: @@ -1617,8 +1789,12 @@ orgs: namespaced: True trial: config_file: orgs/trial.json - enterprise: + metecho_trial: config_file: orgs/enterprise.json + days: 30 + setup_flow: config_trial_org + enterprise: + config_file: orgs/dev.json plans: existing_org: @@ -1782,4 +1958,4 @@ plans: 2: task: install_managed options: - security_type: PUSH \ No newline at end of file + security_type: PUSH diff --git a/datasets/dev_org/mapping.yml b/datasets/dev_org/mapping.yml new file mode 100644 index 00000000000..1b3fae721b5 --- /dev/null +++ b/datasets/dev_org/mapping.yml @@ -0,0 +1,159 @@ +Account: + sf_object: Account + table: Account + fields: + Name: Name + BillingCountry: BillingCountry + BillingStreet: BillingStreet + BillingState: BillingState + BillingCity: BillingCity + BillingPostalCode: BillingPostalCode + npe01__SYSTEM_AccountType__c: npe01__SYSTEM_AccountType__c + RecordTypeId: RecordTypeId +Address__c: + sf_object: Address__c + table: Address__c + fields: + MailingCountry__c: MailingCountry__c + MailingStreet__c: MailingStreet__c + MailingState__c: MailingState__c + MailingCity__c: MailingCity__c + MailingPostalCode__c: MailingPostalCode__c + Seasonal_Start_Month__c: Seasonal_Start_Month__c + Seasonal_Start_Day__c: Seasonal_Start_Day__c + Seasonal_End_Month__c: Seasonal_End_Month__c + Seasonal_End_Day__c: Seasonal_End_Day__c + lookups: + Household_Account__c: + table: Account +Contact: + sf_object: Contact + table: Contact + fields: + Salutation: Salutation + FirstName: FirstName + LastName: LastName + Email: Email + Phone: Phone + Title: Title + lookups: + AccountId: + table: Account + Primary_Affiliation__c: + table: Account +Campaign: + sf_object: Campaign + table: Campaign + fields: + Name: name +npe03__Recurring_Donation__c: + sf_object: npe03__Recurring_Donation__c + table: npe03__Recurring_Donation__c + fields: + Name: Name + npe03__Amount__c: npe03__Amount__c + npe03__Date_Established__c: npe03__Date_Established__c + npe03__Schedule_Type__c: npe03__Schedule_Type__c + npe03__Open_Ended_Status__c: npe03__Open_Ended_Status__c + npe03__Last_Payment_Date__c: npe03__Last_Payment_Date__c + npe03__Next_Payment_Date__c: npe03__Next_Payment_Date__c + npe03__Installment_Period__c: npe03__Installment_Period__c + Day_of_Month__c: Day_of_Month__c + InstallmentFrequency__c: InstallmentFrequency__c + StartDate__c: StartDate__c + PaymentMethod__c: PaymentMethod__c + RecurringType__c: RecurringType__c + Status__c: Status__c + lookups: + npe03__Contact__c: + table: Contact +Opportunity: + sf_object: Opportunity + table: Opportunity + fields: + Name: name + Amount: amount + StageName: stage_name + CloseDate: close_date + npe01__Do_Not_Automatically_Create_Payment__c: dont_create_payments + lookups: + AccountId: + table: Account + Primary_Contact__c: + table: Contact + npe03__Recurring_Donation__c: + table: npe03__Recurring_Donation__c + CampaignId: + table: Campaign +OpportunityContactRole: + sf_object: OpportunityContactRole + table: OpportunityContactRole + fields: + Role: Role + IsPrimary: IsPrimary + lookups: + ContactId: + table: Contact + OpportunityId: + table: Opportunity +Partial_Soft_Credit__c: + sf_object: Partial_Soft_Credit__c + table: Partial_Soft_Credit__c + fields: + Role_Name__c: Role_Name__c + Amount__c: Amount__c + lookups: + Contact__c: + table: Contact + Opportunity__c: + table: Opportunity +npe01__OppPayment__c: + sf_object: npe01__OppPayment__c + table: npe01__OppPayment__c + fields: + npe01__Payment_Amount__c: npe01__Payment_Amount__c + npe01__Payment_Date__c: npe01__Payment_Date__c + npe01__Scheduled_Date__c: npe01__Scheduled_Date__c + npe01__Paid__c: npe01__Paid__c + npe01__Written_Off__c: npe01__Written_Off__c + lookups: + npe01__Opportunity__c: + table: Opportunity +General_Accounting_Unit__c: + sf_object: General_Accounting_Unit__c + table: General_Accounting_Unit__c + fields: + Name: name +Allocation__c: + sf_object: Allocation__c + table: Allocation__c + fields: + Percent__c: Percent__c + Amount__c: Amount__c + lookups: + Opportunity__c: + table: Opportunity + General_Accounting_Unit__c: + table: General_Accounting_Unit__c + Recurring_Donation__c: + table: npe03__Recurring_Donation__c +npe4__Relationship__c: + sf_object: npe4__Relationship__c + table: npe4__Relationship__c + fields: + npe4__Type__c: npe4__Type__c + lookups: + npe4__Contact__c: + table: Contact + npe4__RelatedContact__c: + table: Contact +CampaignMember: + sf_object: CampaignMember + table: CampaignMember + fields: + Status: status + lookups: + ContactId: + table: Contact + CampaignId: + table: Campaign \ No newline at end of file diff --git a/datasets/dev_org/test_data.sql b/datasets/dev_org/test_data.sql index a895df82929..b248b2f960c 100644 --- a/datasets/dev_org/test_data.sql +++ b/datasets/dev_org/test_data.sql @@ -1,494 +1,582 @@ BEGIN TRANSACTION; CREATE TABLE "Account" ( - id INTEGER NOT NULL, - "Name" VARCHAR(255), - "BillingCountry" VARCHAR(255), - "BillingStreet" VARCHAR(255), - "BillingState" VARCHAR(255), - "BillingCity" VARCHAR(255), - "BillingPostalCode" VARCHAR(255), - "npe01__SYSTEM_AccountType__c" VARCHAR(255), - "RecordTypeId" VARCHAR(255), + id INTEGER NOT NULL, + "Name" VARCHAR(255), + "BillingCountry" VARCHAR(255), + "BillingStreet" VARCHAR(255), + "BillingState" VARCHAR(255), + "BillingCity" VARCHAR(255), + "BillingPostalCode" VARCHAR(255), + "npe01__SYSTEM_AccountType__c" VARCHAR(255), + "RecordTypeId" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "Account" VALUES(1,'Patterson Household','Argentina','26 Blackbird Junction','','La Banda','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(2,'Bradley Household','Argentina','392 Mariners Cove Street','','La Falda','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(3,'Harper Household','Brazil','01 Corry Park','','Irati','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(4,'Thompson Household','Brazil','10 Butterfield Place','','Açucena','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(5,'Williams Household','Brazil','20 Oakridge Circle','','Guaratuba','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(6,'Harvey Household','Brazil','3170 Mitchell Parkway','','Campinas','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(7,'Patterson Household','Brazil','405 Sunbrook Terrace','','Codajás','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(8,'Hart and Hawkins Household','Canada','819 Sycamore Avenue','','Dollard-Des Ormeaux','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(9,'Howell Household','China','02 Golf View Pass','','Xindu','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(10,'Gilbert Household','China','03 Arapahoe Hill','','Jiangxi','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(11,'Gardner Household','China','0377 Shasta Trail','','Panzhou','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(12,'Ross Household','China','1056 Corscot Center','','Guanzhou','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(13,'Black Household','China','18 Drewry Street','','Yangshufang','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(14,'Frazier Household','China','18 Twin Pines Circle','','Laojiangjunjie','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(15,'Nichols and Murray Household','China','2 Victoria Point','','Liquan Chengguanzhen','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(16,'Flores Household','China','26 Annamark Alley','','Hatu Buh','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(17,'Long Household','China','3 Buena Vista Road','','Yonghe','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(18,'Fowler Household','China','428 Katie Court','','Jiangning','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(19,'Pierce Household','China','491 Lerdahl Pass','','Shangjiangxu','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(20,'Johnston Household','China','57 Mitchell Circle','','Gaotuo','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(21,'Gomez Household','China','59 Sheridan Avenue','','Xiaowuzhan','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(22,'Knight Household','China','612 Almo Hill','','Shuidong','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(23,'Sanders Household','China','67377 Memorial Road','','Baikui','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(24,'Flores Household','China','73 Browning Junction','','Licheng','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(25,'Fernandez Household','China','8193 Dunning Point','','Gangnan','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(26,'Hicks Household','China','82521 Pond Terrace','','Bailang','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(27,'Chapman Household','China','90472 Montana Center','','Tangqian','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(28,'Mills Household','China','9068 Clove Pass','','Xinfeng','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(29,'Nelson Household','Colombia','2 Mccormick Drive','','Arboleda','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(30,'Grant Household','Czech Republic','230 Veith Place','','Želiv','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(31,'Ward Household','Czech Republic','59 Mosinee Drive','','Dřiteň','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(32,'James Household','Czech Republic','65 Maywood Drive','','Hošťka','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(33,'Wallace Household','Czech Republic','82 Lukken Terrace','','Sedlice','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(34,'Chapman Household','Czech Republic','9407 Dottie Street','','Žirovnice','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(35,'Fuller Household','Ecuador','67 Milwaukee Parkway','','Montalvo','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(36,'Jones Household','Finland','8 Loomis Alley','','Muhos','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(37,'Banks Household','France','4564 Victoria Parkway','Rhône-Alpes','Meylan','38244 CEDEX','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(38,'Perkins Household','France','5 Burning Wood Alley','Poitou-Charentes','Royan','17209 CEDEX','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(39,'Medina and Morales Household','France','861 Dahle Street','Centre','Orléans','45933 CEDEX 9','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(40,'Burton Household','France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(41,'Hayes Household','France','97798 Green Trail','Aquitaine','Boé','47555 CEDEX','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(42,'Oliver Household','Indonesia','075 High Crossing Center','','Kajanan','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(43,'Alvarez Household','Indonesia','2 Chinook Road','','Sumberdangdang','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(44,'Watson Household','Indonesia','402 Loomis Lane','','Cileunyi','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(45,'Wallace Household','Indonesia','52 Mifflin Circle','','Tempaling','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(46,'Riley Household','Italy','2 Old Gate Parkway','Sicilia','Messina','98146','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(47,'Rivera Household','Japan','9 Carpenter Trail','','Jōetsu','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(48,'Stephens Household','Kazakhstan','0491 La Follette Crossing','','Taldyqorghan','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(49,'Fisher Household','Kazakhstan','1902 Randy Alley','','Kokshetau','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(50,'Torres Household','New Zealand','535 Susan Trail','','Red Hill','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(51,'Jenkins Household','Nigeria','7060 Marcy Center','','Tegina','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(52,'Anderson Household','Philippines','0087 Russell Pass','','Takub','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(53,'Perry Household','Philippines','0990 Mayfield Hill','','Calaba','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(54,'Morrison Household','Philippines','1 Oriole Parkway','','Hermosa','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(55,'Ward Household','Philippines','126 Morningstar Park','','Bantacan','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(56,'Washington Household','Philippines','309 Basil Junction','','Sexmoan','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(57,'Franklin Household','Philippines','4026 Monument Terrace','','Lais','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(58,'Hunt Household','Greece','0 Carpenter Lane','','Markópoulo Oropoú','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(59,'Garrett Household','Greece','18113 Helena Place','','Kariaí','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(60,'Alvarez, Taylor, Stone, Smith, Russell, Medina, Kelly, Johnson and Harrison Household','Greece','7589 Pennsylvania Junction','','Kassándreia','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(61,'Bailey Household','Philippines','75 Ramsey Pass','','Bantacan','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(62,'Thomas Household','Philippines','909 Alpine Avenue','','Lumbang','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(63,'Lane Household','Philippines','92336 Bunker Hill Drive','','Mambago','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(64,'Perry Household','Poland','0 Darwin Lane','','Kowale','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(65,'Anderson Household','Poland','098 Scoville Point','','Przecław','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(66,'Hansen Household','Poland','13 Crownhardt Point','','Wilkowice','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(67,'Willis Household','Poland','843 Cottonwood Lane','','Świnna','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(68,'Holmes Household','Poland','942 Vidon Hill','','Kiełczów','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(69,'Kelly Household','Portugal','0 Shopko Avenue','Lisboa','Aldeia de Juzo','2750-015','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(70,'Reyes Household','Portugal','49997 Oakridge Pass','Coimbra','Ribeira','3360-257','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(71,'Burke Household','Russia','0747 Drewry Crossing','','Staropyshminsk','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(72,'Webb Household','Russia','4 Lillian Trail','','Slyudyanka','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(73,'Long Household','Russia','755 Thierer Place','','Uchkulan','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(74,'Powell Household','Russia','8 Transport Plaza','','Peresvet','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(75,'Powell Household','Russia','93 Eastlawn Crossing','','Bolgar','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(76,'Jenkins Household','Sierra Leone','484 Londonderry Plaza','','Kayima','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(77,'Gonzales Household','South Korea','701 Waubesa Crossing','','Enjitsu','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(78,'Richards Household','Sweden','62 Graedel Center','Stockholm','Sundbyberg','172 69','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(79,'Sullivan Household','Syria','24609 Thompson Parkway','','Tasīl','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(80,'Chapman Household','Thailand','1 Basil Circle','','Bueng Samakkhi','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(81,'Hudson Household','Thailand','4328 Hoffman Road','','Kamalasai','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(82,'Warren Household','Thailand','5730 International Lane','','Phuket','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(83,'King Household','Ukraine','2072 Hayes Plaza','','Rybache','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(84,'Riley Household','Ukraine','39955 Canary Avenue','','Truskavets','','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(85,'Porter Household','United States','07050 Doe Crossing Plaza','Minnesota','Saint Paul','55108','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(86,'Austin Household','United States','36055 Maple Hill','Connecticut','Waterbury','06705','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(87,'Carr Household','United States','50 Cherokee Way','Louisiana','Baton Rouge','70894','Household Account','0122a000000PCxAAAW'); -INSERT INTO "Account" VALUES(88,'Aimbo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(89,'Aivee','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(90,'Avamba','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(91,'Blognation','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(92,'Brainbox','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(93,'Brightbean','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(94,'Brightdog','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(95,'Browsecat','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(96,'Bubblebox','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(97,'Centidel','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(98,'Centizu','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(99,'Chatterpoint','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(100,'Dabtype','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(101,'Divape','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(102,'Dynazzy','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(103,'Eare','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(104,'Eazzy','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(105,'Edgeify','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(106,'Edgewire','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(107,'Fatz','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(108,'Flashdog','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(109,'Flashpoint','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(110,'Flashspan','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(111,'Flipbug','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(112,'Fliptune','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(113,'Gevee','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(114,'Gigabox','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(115,'Innotype','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(116,'Jabberbean','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(117,'Devshare','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(118,'Jatri','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(119,'Jaxspan','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(120,'Jayo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(121,'Kazu','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(122,'Kwideo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(123,'Lajo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(124,'Latz','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(125,'Layo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(126,'Meeveo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(127,'Mynte','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(128,'Myworks','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(129,'Ntags','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(130,'Oloo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(131,'Omba','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(132,'Oyoloo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(133,'Podcat','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(134,'Quimm','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(135,'Realblab','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(136,'Realpoint','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(137,'Rhynyx','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(138,'Roodel','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(139,'Skibox','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(140,'Skidoo','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(141,'Skimia','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(142,'Skinder','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(143,'Skinte','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(144,'Skyba','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(145,'Tagchat','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(146,'Tagtune','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(147,'Talane','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(148,'Tambee','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(149,'Tavu','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(150,'Tazzy','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(151,'Thoughtbridge','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(152,'Thoughtsphere','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(153,'Trilia','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(154,'Twinte','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(155,'Twitternation','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(156,'Twitterwire','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(157,'Voolith','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(158,'Voonix','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(159,'Wikivu','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(160,'Wordtune','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(161,'Yata','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(162,'Yombu','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(163,'Youopia','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(164,'Yozio','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(165,'Zazio','','','','','','','0122a000000PCxBAAW'); -INSERT INTO "Account" VALUES(166,'Zoomlounge','','','','','','','0122a000000PCxBAAW'); +INSERT INTO "Account" VALUES(1,'Sample Account for Entitlements','','','','','','',''); +INSERT INTO "Account" VALUES(2,'Gevee','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(3,'Gigabox','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(4,'Innotype','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(5,'Jabberbean','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(6,'Devshare','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(7,'Jatri','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(8,'Jaxspan','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(9,'Jayo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(10,'Kazu','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(11,'Kwideo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(12,'Lajo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(13,'Latz','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(14,'Layo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(15,'Meeveo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(16,'Mynte','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(17,'Myworks','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(18,'Ntags','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(19,'Oloo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(20,'Flashpoint','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(21,'Flashspan','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(22,'Flipbug','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(23,'Fliptune','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(24,'Omba','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(25,'Oyoloo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(26,'Podcat','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(27,'Quimm','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(28,'Realblab','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(29,'Realpoint','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(30,'Rhynyx','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(31,'Roodel','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(32,'Skibox','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(33,'Skidoo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(34,'Skimia','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(35,'Skinder','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(36,'Skinte','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(37,'Skyba','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(38,'Tagchat','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(39,'Tagtune','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(40,'Talane','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(41,'Tambee','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(42,'Tavu','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(43,'Tazzy','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(44,'Thoughtbridge','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(45,'Thoughtsphere','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(46,'Trilia','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(47,'Twinte','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(48,'Twitternation','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(49,'Twitterwire','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(50,'Voolith','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(51,'Voonix','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(52,'Wikivu','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(53,'Wordtune','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(54,'Yata','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(55,'Yombu','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(56,'Youopia','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(57,'Yozio','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(58,'Zazio','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(59,'Zoomlounge','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(60,'Patterson Household','Argentina','26 Blackbird Junction','','La Banda','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(61,'Bradley Household','Argentina','392 Mariners Cove Street','','La Falda','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(62,'Harper Household','Brazil','01 Corry Park','','Irati','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(63,'Thompson Household','Brazil','10 Butterfield Place','','Açucena','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(64,'Williams Household','Brazil','20 Oakridge Circle','','Guaratuba','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(65,'Harvey Household','Brazil','3170 Mitchell Parkway','','Campinas','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(66,'Patterson Household','Brazil','405 Sunbrook Terrace','','Codajás','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(67,'Hart and Hawkins Household','Canada','819 Sycamore Avenue','','Dollard-Des Ormeaux','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(68,'Howell Household','China','02 Golf View Pass','','Xindu','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(69,'Gilbert Household','China','03 Arapahoe Hill','','Jiangxi','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(70,'Gardner Household','China','0377 Shasta Trail','','Panzhou','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(71,'Ross Household','China','1056 Corscot Center','','Guanzhou','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(72,'Black Household','China','18 Drewry Street','','Yangshufang','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(73,'Frazier Household','China','18 Twin Pines Circle','','Laojiangjunjie','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(74,'Murray and Nichols Household','China','2 Victoria Point','','Liquan Chengguanzhen','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(75,'Flores Household','China','26 Annamark Alley','','Hatu Buh','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(76,'Long Household','China','3 Buena Vista Road','','Yonghe','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(77,'Fowler Household','China','428 Katie Court','','Jiangning','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(78,'Pierce Household','China','491 Lerdahl Pass','','Shangjiangxu','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(79,'Johnston Household','China','57 Mitchell Circle','','Gaotuo','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(80,'Gomez Household','China','59 Sheridan Avenue','','Xiaowuzhan','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(81,'Knight Household','China','612 Almo Hill','','Shuidong','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(82,'Sanders Household','China','67377 Memorial Road','','Baikui','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(83,'Flores Household','China','73 Browning Junction','','Licheng','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(84,'Fernandez Household','China','8193 Dunning Point','','Gangnan','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(85,'Hicks Household','China','82521 Pond Terrace','','Bailang','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(86,'Chapman Household','China','90472 Montana Center','','Tangqian','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(87,'Mills Household','China','9068 Clove Pass','','Xinfeng','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(88,'Nelson Household','Colombia','2 Mccormick Drive','','Arboleda','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(89,'Grant Household','Czech Republic','230 Veith Place','','Želiv','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(90,'Ward Household','Czech Republic','59 Mosinee Drive','','Dřiteň','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(91,'James Household','Czech Republic','65 Maywood Drive','','Hošťka','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(92,'Wallace Household','Czech Republic','82 Lukken Terrace','','Sedlice','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(93,'Chapman Household','Czech Republic','9407 Dottie Street','','Žirovnice','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(94,'Fuller Household','Ecuador','67 Milwaukee Parkway','','Montalvo','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(95,'Jones Household','Finland','8 Loomis Alley','','Muhos','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(96,'Banks Household','France','4564 Victoria Parkway','Rhône-Alpes','Meylan','38244 CEDEX','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(97,'Perkins Household','France','5 Burning Wood Alley','Poitou-Charentes','Royan','17209 CEDEX','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(98,'Medina and Morales Household','France','861 Dahle Street','Centre','Orléans','45933 CEDEX 9','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(99,'Burton Household','France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(100,'Hayes Household','France','97798 Green Trail','Aquitaine','Boé','47555 CEDEX','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(101,'Oliver Household','Indonesia','075 High Crossing Center','','Kajanan','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(102,'Alvarez Household','Indonesia','2 Chinook Road','','Sumberdangdang','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(103,'Watson Household','Indonesia','402 Loomis Lane','','Cileunyi','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(104,'Wallace Household','Indonesia','52 Mifflin Circle','','Tempaling','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(105,'Riley Household','Italy','2 Old Gate Parkway','Sicilia','Messina','98146','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(106,'Rivera Household','Japan','9 Carpenter Trail','','Jōetsu','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(107,'Stephens Household','Kazakhstan','0491 La Follette Crossing','','Taldyqorghan','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(108,'Fisher Household','Kazakhstan','1902 Randy Alley','','Kokshetau','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(109,'Torres Household','New Zealand','535 Susan Trail','','Red Hill','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(110,'Jenkins Household','Nigeria','7060 Marcy Center','','Tegina','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(111,'Anderson Household','Philippines','0087 Russell Pass','','Takub','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(112,'Perry Household','Philippines','0990 Mayfield Hill','','Calaba','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(113,'Morrison Household','Philippines','1 Oriole Parkway','','Hermosa','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(114,'Ward Household','Philippines','126 Morningstar Park','','Bantacan','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(115,'Washington Household','Philippines','309 Basil Junction','','Sexmoan','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(116,'Franklin Household','Philippines','4026 Monument Terrace','','Lais','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(117,'Hunt Household','Greece','0 Carpenter Lane','','Markópoulo Oropoú','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(118,'Garrett Household','Greece','18113 Helena Place','','Kariaí','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(119,'Taylor, Alvarez, Stone, Smith, Russell, Medina, Kelly, Johnson and Harrison Household','Greece','7589 Pennsylvania Junction','','Kassándreia','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(120,'Bailey Household','Philippines','75 Ramsey Pass','','Bantacan','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(121,'Thomas Household','Philippines','909 Alpine Avenue','','Lumbang','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(122,'Lane Household','Philippines','92336 Bunker Hill Drive','','Mambago','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(123,'Perry Household','Poland','0 Darwin Lane','','Kowale','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(124,'Anderson Household','Poland','098 Scoville Point','','Przecław','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(125,'Hansen Household','Poland','13 Crownhardt Point','','Wilkowice','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(126,'Willis Household','Poland','843 Cottonwood Lane','','Świnna','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(127,'Holmes Household','Poland','942 Vidon Hill','','Kiełczów','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(128,'Kelly Household','Portugal','0 Shopko Avenue','Lisboa','Aldeia de Juzo','2750-015','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(129,'Reyes Household','Portugal','49997 Oakridge Pass','Coimbra','Ribeira','3360-257','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(130,'Burke Household','Russia','0747 Drewry Crossing','','Staropyshminsk','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(131,'Webb Household','Russia','4 Lillian Trail','','Slyudyanka','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(132,'Long Household','Russia','755 Thierer Place','','Uchkulan','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(133,'Powell Household','Russia','8 Transport Plaza','','Peresvet','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(134,'Powell Household','Russia','93 Eastlawn Crossing','','Bolgar','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(135,'Jenkins Household','Sierra Leone','484 Londonderry Plaza','','Kayima','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(136,'Gonzales Household','South Korea','701 Waubesa Crossing','','Enjitsu','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(137,'Richards Household','Sweden','62 Graedel Center','Stockholm','Sundbyberg','172 69','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(138,'Sullivan Household','Syria','24609 Thompson Parkway','','Tasīl','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(139,'Chapman Household','Thailand','1 Basil Circle','','Bueng Samakkhi','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(140,'Hudson Household','Thailand','4328 Hoffman Road','','Kamalasai','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(141,'Warren Household','Thailand','5730 International Lane','','Phuket','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(142,'King Household','Ukraine','2072 Hayes Plaza','','Rybache','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(143,'Riley Household','Ukraine','39955 Canary Avenue','','Truskavets','','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(144,'Porter Household','United States','07050 Doe Crossing Plaza','Minnesota','Saint Paul','55108','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(145,'Austin Household','United States','36055 Maple Hill','Connecticut','Waterbury','06705','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(146,'Carr Household','United States','50 Cherokee Way','Louisiana','Baton Rouge','70894','Household Account','0128A000002hay0QAA'); +INSERT INTO "Account" VALUES(147,'Aimbo','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(148,'Aivee','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(149,'Avamba','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(150,'Blognation','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(151,'Brainbox','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(152,'Brightbean','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(153,'Brightdog','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(154,'Browsecat','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(155,'Bubblebox','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(156,'Centidel','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(157,'Centizu','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(158,'Chatterpoint','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(159,'Dabtype','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(160,'Divape','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(161,'Dynazzy','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(162,'Eare','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(163,'Eazzy','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(164,'Edgeify','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(165,'Edgewire','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(166,'Fatz','','','','','','','0128A000002hay1QAA'); +INSERT INTO "Account" VALUES(167,'Flashdog','','','','','','','0128A000002hay1QAA'); CREATE TABLE "Account_rt_mapping" ( - record_type_id VARCHAR(18) NOT NULL, - developer_name VARCHAR(255), + record_type_id VARCHAR(18) NOT NULL, + developer_name VARCHAR(255), PRIMARY KEY (record_type_id) ); -INSERT INTO "Account_rt_mapping" VALUES('0122a000000PCxAAAW','HH_Account'); -INSERT INTO "Account_rt_mapping" VALUES('0122a000000PCxBAAW','Organization'); +INSERT INTO "Account_rt_mapping" VALUES('0128A000002hay0QAA','HH_Account'); +INSERT INTO "Account_rt_mapping" VALUES('0128A000002hay1QAA','Organization'); CREATE TABLE "Address__c" ( - id INTEGER NOT NULL, - "MailingCountry__c" VARCHAR(255), - "MailingStreet__c" VARCHAR(255), - "MailingState__c" VARCHAR(255), - "MailingCity__c" VARCHAR(255), - "MailingPostalCode__c" VARCHAR(255), - "Seasonal_Start_Month__c" VARCHAR(255), - "Seasonal_Start_Day__c" VARCHAR(255), - "Seasonal_End_Month__c" VARCHAR(255), - "Seasonal_End_Day__c" VARCHAR(255), - household_account__c VARCHAR(255), + id INTEGER NOT NULL, + "MailingCountry__c" VARCHAR(255), + "MailingStreet__c" VARCHAR(255), + "MailingState__c" VARCHAR(255), + "MailingCity__c" VARCHAR(255), + "MailingPostalCode__c" VARCHAR(255), + "Seasonal_Start_Month__c" VARCHAR(255), + "Seasonal_Start_Day__c" VARCHAR(255), + "Seasonal_End_Month__c" VARCHAR(255), + "Seasonal_End_Day__c" VARCHAR(255), + "Household_Account__c" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "Address__c" VALUES(1,'United States','07050 Doe Crossing Plaza','Minnesota','Saint Paul','55108','','','','','33'); -INSERT INTO "Address__c" VALUES(2,'United States','36055 Maple Hill','Connecticut','Waterbury','06705','','','','','33'); -INSERT INTO "Address__c" VALUES(3,'United States','50 Cherokee Way','Louisiana','Baton Rouge','70894','','','','','33'); -INSERT INTO "Address__c" VALUES(4,'Argentina','26 Blackbird Junction','','La Banda','','','','','','33'); -INSERT INTO "Address__c" VALUES(5,'Argentina','392 Mariners Cove Street','','La Falda','','','','','','33'); -INSERT INTO "Address__c" VALUES(6,'Brazil','01 Corry Park','','Irati','','','','','','33'); -INSERT INTO "Address__c" VALUES(7,'Brazil','10 Butterfield Place','','Açucena','','','','','','33'); -INSERT INTO "Address__c" VALUES(8,'Brazil','20 Oakridge Circle','','Guaratuba','','','','','','33'); -INSERT INTO "Address__c" VALUES(9,'Brazil','3170 Mitchell Parkway','','Campinas','','','','','','33'); -INSERT INTO "Address__c" VALUES(10,'Brazil','405 Sunbrook Terrace','','Codajás','','','','','','33'); -INSERT INTO "Address__c" VALUES(11,'Canada','819 Sycamore Avenue','','Dollard-Des Ormeaux','','','','','','33'); -INSERT INTO "Address__c" VALUES(12,'China','02 Golf View Pass','','Xindu','','','','','','33'); -INSERT INTO "Address__c" VALUES(13,'China','03 Arapahoe Hill','','Jiangxi','','','','','','33'); -INSERT INTO "Address__c" VALUES(14,'China','0377 Shasta Trail','','Panzhou','','','','','','33'); -INSERT INTO "Address__c" VALUES(15,'China','1056 Corscot Center','','Guanzhou','','','','','','33'); -INSERT INTO "Address__c" VALUES(16,'China','18 Drewry Street','','Yangshufang','','','','','','33'); -INSERT INTO "Address__c" VALUES(17,'China','18 Twin Pines Circle','','Laojiangjunjie','','','','','','33'); -INSERT INTO "Address__c" VALUES(18,'China','2 Victoria Point','','Liquan Chengguanzhen','','','','','','33'); -INSERT INTO "Address__c" VALUES(19,'China','26 Annamark Alley','','Hatu Buh','','','','','','33'); -INSERT INTO "Address__c" VALUES(20,'China','3 Buena Vista Road','','Yonghe','','','','','','33'); -INSERT INTO "Address__c" VALUES(21,'China','428 Katie Court','','Jiangning','','','','','','33'); -INSERT INTO "Address__c" VALUES(22,'China','491 Lerdahl Pass','','Shangjiangxu','','','','','','33'); -INSERT INTO "Address__c" VALUES(23,'China','57 Mitchell Circle','','Gaotuo','','','','','','33'); -INSERT INTO "Address__c" VALUES(24,'China','59 Sheridan Avenue','','Xiaowuzhan','','','','','','33'); -INSERT INTO "Address__c" VALUES(25,'China','612 Almo Hill','','Shuidong','','','','','','33'); -INSERT INTO "Address__c" VALUES(26,'China','67377 Memorial Road','','Baikui','','','','','','33'); -INSERT INTO "Address__c" VALUES(27,'China','73 Browning Junction','','Licheng','','','','','','33'); -INSERT INTO "Address__c" VALUES(28,'China','8193 Dunning Point','','Gangnan','','','','','','33'); -INSERT INTO "Address__c" VALUES(29,'China','82521 Pond Terrace','','Bailang','','','','','','33'); -INSERT INTO "Address__c" VALUES(30,'China','90472 Montana Center','','Tangqian','','','','','','33'); -INSERT INTO "Address__c" VALUES(31,'China','9068 Clove Pass','','Xinfeng','','','','','','33'); -INSERT INTO "Address__c" VALUES(32,'Indonesia','52 Mifflin Circle','','Tempaling','','','','','','33'); -INSERT INTO "Address__c" VALUES(33,'Italy','2 Old Gate Parkway','Sicilia','Messina','98146','','','','','33'); -INSERT INTO "Address__c" VALUES(34,'Japan','9 Carpenter Trail','','Jōetsu','','','','','','33'); -INSERT INTO "Address__c" VALUES(35,'Kazakhstan','0491 La Follette Crossing','','Taldyqorghan','','','','','','33'); -INSERT INTO "Address__c" VALUES(36,'Kazakhstan','1902 Randy Alley','','Kokshetau','','','','','','33'); -INSERT INTO "Address__c" VALUES(37,'New Zealand','535 Susan Trail','','Red Hill','','','','','','33'); -INSERT INTO "Address__c" VALUES(38,'Nigeria','7060 Marcy Center','','Tegina','','','','','','33'); -INSERT INTO "Address__c" VALUES(39,'Philippines','0087 Russell Pass','','Takub','','','','','','33'); -INSERT INTO "Address__c" VALUES(40,'Philippines','0990 Mayfield Hill','','Calaba','','','','','','33'); -INSERT INTO "Address__c" VALUES(41,'Philippines','1 Oriole Parkway','','Hermosa','','','','','','33'); -INSERT INTO "Address__c" VALUES(42,'Philippines','126 Morningstar Park','','Bantacan','','','','','','33'); -INSERT INTO "Address__c" VALUES(43,'Czech Republic','82 Lukken Terrace','','Sedlice','','','','','','33'); -INSERT INTO "Address__c" VALUES(44,'Czech Republic','9407 Dottie Street','','Žirovnice','','','','','','34'); -INSERT INTO "Address__c" VALUES(45,'Ecuador','67 Milwaukee Parkway','','Montalvo','','','','','','35'); -INSERT INTO "Address__c" VALUES(46,'Finland','8 Loomis Alley','','Muhos','','','','','','36'); -INSERT INTO "Address__c" VALUES(47,'France','4564 Victoria Parkway','Rhône-Alpes','Meylan','38244 CEDEX','','','','','37'); -INSERT INTO "Address__c" VALUES(48,'France','5 Burning Wood Alley','Poitou-Charentes','Royan','17209 CEDEX','','','','','38'); -INSERT INTO "Address__c" VALUES(49,'France','861 Dahle Street','Centre','Orléans','45933 CEDEX 9','','','','','39'); -INSERT INTO "Address__c" VALUES(50,'Brazil','10 Butterfield Place','','Açucena','','','','','','4'); -INSERT INTO "Address__c" VALUES(51,'France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','6','15','8','30','40'); -INSERT INTO "Address__c" VALUES(52,'France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','','','','','40'); -INSERT INTO "Address__c" VALUES(53,'France','97798 Green Trail','Aquitaine','Boé','47555 CEDEX','','','','','41'); -INSERT INTO "Address__c" VALUES(54,'Greece','0 Carpenter Lane','','Markópoulo Oropoú','','','','','','58'); -INSERT INTO "Address__c" VALUES(55,'Greece','18113 Helena Place','','Kariaí','','','','','','59'); -INSERT INTO "Address__c" VALUES(56,'Greece','7589 Pennsylvania Junction','','Kassándreia','','','','','','60'); -INSERT INTO "Address__c" VALUES(57,'Indonesia','075 High Crossing Center','','Kajanan','','','','','','42'); -INSERT INTO "Address__c" VALUES(58,'Indonesia','2 Chinook Road','','Sumberdangdang','','','','','','43'); -INSERT INTO "Address__c" VALUES(59,'Indonesia','402 Loomis Lane','','Cileunyi','','','','','','44'); -INSERT INTO "Address__c" VALUES(60,'Indonesia','52 Mifflin Circle','','Tempaling','','','','','','45'); -INSERT INTO "Address__c" VALUES(61,'Italy','2 Old Gate Parkway','Sicilia','Messina','98146','','','','','46'); -INSERT INTO "Address__c" VALUES(62,'Brazil','20 Oakridge Circle','','Guaratuba','','','','','','5'); -INSERT INTO "Address__c" VALUES(63,'Japan','9 Carpenter Trail','','Jōetsu','','','','','','47'); -INSERT INTO "Address__c" VALUES(64,'Kazakhstan','0491 La Follette Crossing','','Taldyqorghan','','','','','','48'); -INSERT INTO "Address__c" VALUES(65,'Kazakhstan','1902 Randy Alley','','Kokshetau','','','','','','49'); -INSERT INTO "Address__c" VALUES(66,'New Zealand','535 Susan Trail','','Red Hill','','6','15','8','30','50'); -INSERT INTO "Address__c" VALUES(67,'New Zealand','535 Susan Trail','','Red Hill','','','','','','50'); -INSERT INTO "Address__c" VALUES(68,'Nigeria','7060 Marcy Center','','Tegina','','','','','','51'); -INSERT INTO "Address__c" VALUES(69,'Philippines','0087 Russell Pass','','Takub','','','','','','52'); -INSERT INTO "Address__c" VALUES(70,'Philippines','0990 Mayfield Hill','','Calaba','','','','','','53'); -INSERT INTO "Address__c" VALUES(71,'Philippines','1 Oriole Parkway','','Hermosa','','','','','','54'); -INSERT INTO "Address__c" VALUES(72,'Philippines','126 Morningstar Park','','Bantacan','','','','','','55'); -INSERT INTO "Address__c" VALUES(73,'Philippines','309 Basil Junction','','Sexmoan','','','','','','56'); -INSERT INTO "Address__c" VALUES(74,'Brazil','3170 Mitchell Parkway','','Campinas','','','','','','6'); -INSERT INTO "Address__c" VALUES(75,'Philippines','4026 Monument Terrace','','Lais','','','','','','57'); -INSERT INTO "Address__c" VALUES(76,'Philippines','75 Ramsey Pass','','Bantacan','','','','','','61'); -INSERT INTO "Address__c" VALUES(77,'Philippines','909 Alpine Avenue','','Lumbang','','','','','','62'); -INSERT INTO "Address__c" VALUES(78,'Philippines','92336 Bunker Hill Drive','','Mambago','','','','','','63'); -INSERT INTO "Address__c" VALUES(79,'Poland','0 Darwin Lane','','Kowale','','6','15','8','30','64'); -INSERT INTO "Address__c" VALUES(80,'Poland','0 Darwin Lane','','Kowale','','','','','','64'); -INSERT INTO "Address__c" VALUES(81,'Poland','098 Scoville Point','','Przecław','','','','','','65'); -INSERT INTO "Address__c" VALUES(82,'Poland','13 Crownhardt Point','','Wilkowice','','','','','','66'); -INSERT INTO "Address__c" VALUES(83,'Poland','843 Cottonwood Lane','','Świnna','','','','','','67'); -INSERT INTO "Address__c" VALUES(84,'Poland','942 Vidon Hill','','Kiełczów','','','','','','68'); -INSERT INTO "Address__c" VALUES(85,'Portugal','0 Shopko Avenue','Lisboa','Aldeia de Juzo','2750-015','','','','','69'); -INSERT INTO "Address__c" VALUES(86,'Brazil','405 Sunbrook Terrace','','Codajás','','','','','','7'); -INSERT INTO "Address__c" VALUES(87,'Portugal','49997 Oakridge Pass','Coimbra','Ribeira','3360-257','','','','','70'); -INSERT INTO "Address__c" VALUES(88,'Russia','0747 Drewry Crossing','','Staropyshminsk','','','','','','71'); -INSERT INTO "Address__c" VALUES(89,'Russia','4 Lillian Trail','','Slyudyanka','','','','','','72'); -INSERT INTO "Address__c" VALUES(90,'Russia','755 Thierer Place','','Uchkulan','','','','','','73'); -INSERT INTO "Address__c" VALUES(91,'Russia','8 Transport Plaza','','Peresvet','','','','','','74'); -INSERT INTO "Address__c" VALUES(92,'Russia','93 Eastlawn Crossing','','Bolgar','','','','','','75'); -INSERT INTO "Address__c" VALUES(93,'Sierra Leone','484 Londonderry Plaza','','Kayima','','','','','','76'); -INSERT INTO "Address__c" VALUES(94,'South Korea','701 Waubesa Crossing','','Enjitsu','','','','','','77'); -INSERT INTO "Address__c" VALUES(95,'Sweden','62 Graedel Center','Stockholm','Sundbyberg','172 69','','','','','78'); -INSERT INTO "Address__c" VALUES(96,'Syria','24609 Thompson Parkway','','Tasīl','','','','','','79'); -INSERT INTO "Address__c" VALUES(97,'Canada','819 Sycamore Avenue','','Dollard-Des Ormeaux','','','','','','8'); -INSERT INTO "Address__c" VALUES(98,'Thailand','1 Basil Circle','','Bueng Samakkhi','','','','','','80'); -INSERT INTO "Address__c" VALUES(99,'Thailand','4328 Hoffman Road','','Kamalasai','','','','','','81'); -INSERT INTO "Address__c" VALUES(100,'Thailand','5730 International Lane','','Phuket','','','','','','82'); -INSERT INTO "Address__c" VALUES(101,'Ukraine','2072 Hayes Plaza','','Rybache','','','','','','83'); -INSERT INTO "Address__c" VALUES(102,'Ukraine','39955 Canary Avenue','','Truskavets','','','','','','84'); -INSERT INTO "Address__c" VALUES(103,'United States','07050 Doe Crossing Plaza','Minnesota','Saint Paul','55108','','','','','85'); -INSERT INTO "Address__c" VALUES(104,'United States','36055 Maple Hill','Connecticut','Waterbury','06705','','','','','86'); -INSERT INTO "Address__c" VALUES(105,'United States','50 Cherokee Way','Louisiana','Baton Rouge','70894','','','','','87'); -INSERT INTO "Address__c" VALUES(106,'China','02 Golf View Pass','','Xindu','','6','15','8','30','9'); -INSERT INTO "Address__c" VALUES(107,'China','02 Golf View Pass','','Xindu','','','','','','9'); -INSERT INTO "Address__c" VALUES(108,'Argentina','26 Blackbird Junction','','La Banda','','6','15','8','30','1'); -INSERT INTO "Address__c" VALUES(109,'Argentina','26 Blackbird Junction','','La Banda','','','','','','1'); -INSERT INTO "Address__c" VALUES(110,'China','03 Arapahoe Hill','','Jiangxi','','','','','','10'); -INSERT INTO "Address__c" VALUES(111,'China','0377 Shasta Trail','','Panzhou','','','','','','11'); -INSERT INTO "Address__c" VALUES(112,'China','1056 Corscot Center','','Guanzhou','','','','','','12'); -INSERT INTO "Address__c" VALUES(113,'China','18 Drewry Street','','Yangshufang','','','','','','13'); -INSERT INTO "Address__c" VALUES(114,'China','18 Twin Pines Circle','','Laojiangjunjie','','','','','','14'); -INSERT INTO "Address__c" VALUES(115,'China','2 Victoria Point','','Liquan Chengguanzhen','','','','','','15'); -INSERT INTO "Address__c" VALUES(116,'China','26 Annamark Alley','','Hatu Buh','','','','','','16'); -INSERT INTO "Address__c" VALUES(117,'China','3 Buena Vista Road','','Yonghe','','','','','','17'); -INSERT INTO "Address__c" VALUES(118,'China','428 Katie Court','','Jiangning','','','','','','18'); -INSERT INTO "Address__c" VALUES(119,'China','491 Lerdahl Pass','','Shangjiangxu','','','','','','19'); -INSERT INTO "Address__c" VALUES(120,'Argentina','392 Mariners Cove Street','','La Falda','','','','','','2'); -INSERT INTO "Address__c" VALUES(121,'China','57 Mitchell Circle','','Gaotuo','','','','','','20'); -INSERT INTO "Address__c" VALUES(122,'China','59 Sheridan Avenue','','Xiaowuzhan','','','','','','21'); -INSERT INTO "Address__c" VALUES(123,'China','612 Almo Hill','','Shuidong','','','','','','22'); -INSERT INTO "Address__c" VALUES(124,'China','67377 Memorial Road','','Baikui','','','','','','23'); -INSERT INTO "Address__c" VALUES(125,'China','73 Browning Junction','','Licheng','','','','','','24'); -INSERT INTO "Address__c" VALUES(126,'China','8193 Dunning Point','','Gangnan','','','','','','25'); -INSERT INTO "Address__c" VALUES(127,'China','82521 Pond Terrace','','Bailang','','','','','','26'); -INSERT INTO "Address__c" VALUES(128,'China','90472 Montana Center','','Tangqian','','','','','','27'); -INSERT INTO "Address__c" VALUES(129,'China','9068 Clove Pass','','Xinfeng','','','','','','28'); -INSERT INTO "Address__c" VALUES(130,'Colombia','2 Mccormick Drive','','Arboleda','','','','','','29'); -INSERT INTO "Address__c" VALUES(131,'Brazil','01 Corry Park','','Irati','','','','','','3'); -INSERT INTO "Address__c" VALUES(132,'Czech Republic','230 Veith Place','','Želiv','','','','','','30'); -INSERT INTO "Address__c" VALUES(133,'Czech Republic','59 Mosinee Drive','','Dřiteň','','','','','','31'); -INSERT INTO "Address__c" VALUES(134,'Czech Republic','65 Maywood Drive','','Hošťka','','','','','','32'); -INSERT INTO "Address__c" VALUES(135,'Colombia','2 Mccormick Drive','','Arboleda','','','','','','33'); -INSERT INTO "Address__c" VALUES(136,'Czech Republic','230 Veith Place','','Želiv','','','','','','33'); -INSERT INTO "Address__c" VALUES(137,'Czech Republic','59 Mosinee Drive','','Dřiteň','','','','','','33'); -INSERT INTO "Address__c" VALUES(138,'Czech Republic','65 Maywood Drive','','Hošťka','','','','','','33'); -INSERT INTO "Address__c" VALUES(139,'Czech Republic','82 Lukken Terrace','','Sedlice','','','','','','33'); -INSERT INTO "Address__c" VALUES(140,'Czech Republic','9407 Dottie Street','','Žirovnice','','','','','','33'); -INSERT INTO "Address__c" VALUES(141,'Ecuador','67 Milwaukee Parkway','','Montalvo','','','','','','33'); -INSERT INTO "Address__c" VALUES(142,'Finland','8 Loomis Alley','','Muhos','','','','','','33'); -INSERT INTO "Address__c" VALUES(143,'France','4564 Victoria Parkway','Rhône-Alpes','Meylan','38244 CEDEX','','','','','33'); -INSERT INTO "Address__c" VALUES(144,'France','5 Burning Wood Alley','Poitou-Charentes','Royan','17209 CEDEX','','','','','33'); -INSERT INTO "Address__c" VALUES(145,'France','861 Dahle Street','Centre','Orléans','45933 CEDEX 9','','','','','33'); -INSERT INTO "Address__c" VALUES(146,'France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','','','','','33'); -INSERT INTO "Address__c" VALUES(147,'France','97798 Green Trail','Aquitaine','Boé','47555 CEDEX','','','','','33'); -INSERT INTO "Address__c" VALUES(148,'Greece','18113 Helena Place','','Kariaí','','','','','','33'); -INSERT INTO "Address__c" VALUES(149,'Greece','7589 Pennsylvania Junction','','Kassándreia','','','','','','33'); -INSERT INTO "Address__c" VALUES(150,'Indonesia','075 High Crossing Center','','Kajanan','','','','','','33'); -INSERT INTO "Address__c" VALUES(151,'Indonesia','2 Chinook Road','','Sumberdangdang','','','','','','33'); -INSERT INTO "Address__c" VALUES(152,'Indonesia','402 Loomis Lane','','Cileunyi','','','','','','33'); -INSERT INTO "Address__c" VALUES(153,'Philippines','309 Basil Junction','','Sexmoan','','','','','','33'); -INSERT INTO "Address__c" VALUES(154,'Philippines','4026 Monument Terrace','','Lais','','','','','','33'); -INSERT INTO "Address__c" VALUES(155,'Philippines','75 Ramsey Pass','','Bantacan','','','','','','33'); -INSERT INTO "Address__c" VALUES(156,'Philippines','909 Alpine Avenue','','Lumbang','','','','','','33'); -INSERT INTO "Address__c" VALUES(157,'Philippines','92336 Bunker Hill Drive','','Mambago','','','','','','33'); -INSERT INTO "Address__c" VALUES(158,'Poland','0 Darwin Lane','','Kowale','','','','','','33'); -INSERT INTO "Address__c" VALUES(159,'Poland','098 Scoville Point','','Przecław','','','','','','33'); -INSERT INTO "Address__c" VALUES(160,'Poland','13 Crownhardt Point','','Wilkowice','','','','','','33'); -INSERT INTO "Address__c" VALUES(161,'Poland','843 Cottonwood Lane','','Świnna','','','','','','33'); -INSERT INTO "Address__c" VALUES(162,'Poland','942 Vidon Hill','','Kiełczów','','','','','','33'); -INSERT INTO "Address__c" VALUES(163,'Portugal','0 Shopko Avenue','Lisboa','Aldeia de Juzo','2750-015','','','','','33'); -INSERT INTO "Address__c" VALUES(164,'Portugal','49997 Oakridge Pass','Coimbra','Ribeira','3360-257','','','','','33'); -INSERT INTO "Address__c" VALUES(165,'Russia','0747 Drewry Crossing','','Staropyshminsk','','','','','','33'); -INSERT INTO "Address__c" VALUES(166,'Russia','4 Lillian Trail','','Slyudyanka','','','','','','33'); -INSERT INTO "Address__c" VALUES(167,'Russia','755 Thierer Place','','Uchkulan','','','','','','33'); -INSERT INTO "Address__c" VALUES(168,'Russia','8 Transport Plaza','','Peresvet','','','','','','33'); -INSERT INTO "Address__c" VALUES(169,'Russia','93 Eastlawn Crossing','','Bolgar','','','','','','33'); -INSERT INTO "Address__c" VALUES(170,'Sierra Leone','484 Londonderry Plaza','','Kayima','','','','','','33'); -INSERT INTO "Address__c" VALUES(171,'South Korea','701 Waubesa Crossing','','Enjitsu','','','','','','33'); -INSERT INTO "Address__c" VALUES(172,'Sweden','62 Graedel Center','Stockholm','Sundbyberg','172 69','','','','','33'); -INSERT INTO "Address__c" VALUES(173,'Syria','24609 Thompson Parkway','','Tasīl','','','','','','33'); -INSERT INTO "Address__c" VALUES(174,'Thailand','1 Basil Circle','','Bueng Samakkhi','','','','','','33'); -INSERT INTO "Address__c" VALUES(175,'Thailand','4328 Hoffman Road','','Kamalasai','','','','','','33'); -INSERT INTO "Address__c" VALUES(176,'Thailand','5730 International Lane','','Phuket','','','','','','33'); -INSERT INTO "Address__c" VALUES(177,'Ukraine','2072 Hayes Plaza','','Rybache','','','','','','33'); -INSERT INTO "Address__c" VALUES(178,'Ukraine','39955 Canary Avenue','','Truskavets','','','','','','33'); -INSERT INTO "Address__c" VALUES(179,'Greece','0 Carpenter Lane','','Markópoulo Oropoú','','','','','','33'); +INSERT INTO "Address__c" VALUES(1,'China','03 Arapahoe Hill','','Jiangxi','','','','','','69'); +INSERT INTO "Address__c" VALUES(2,'China','0377 Shasta Trail','','Panzhou','','','','','','70'); +INSERT INTO "Address__c" VALUES(3,'China','1056 Corscot Center','','Guanzhou','','','','','','71'); +INSERT INTO "Address__c" VALUES(4,'Argentina','26 Blackbird Junction','','La Banda','','','','','','60'); +INSERT INTO "Address__c" VALUES(5,'Argentina','392 Mariners Cove Street','','La Falda','','','','','','61'); +INSERT INTO "Address__c" VALUES(6,'Brazil','01 Corry Park','','Irati','','','','','','62'); +INSERT INTO "Address__c" VALUES(7,'Brazil','10 Butterfield Place','','Açucena','','','','','','63'); +INSERT INTO "Address__c" VALUES(8,'Brazil','20 Oakridge Circle','','Guaratuba','','','','','','64'); +INSERT INTO "Address__c" VALUES(9,'Brazil','3170 Mitchell Parkway','','Campinas','','','','','','65'); +INSERT INTO "Address__c" VALUES(10,'Brazil','405 Sunbrook Terrace','','Codajás','','','','','','66'); +INSERT INTO "Address__c" VALUES(11,'Canada','819 Sycamore Avenue','','Dollard-Des Ormeaux','','','','','','67'); +INSERT INTO "Address__c" VALUES(12,'China','02 Golf View Pass','','Xindu','','','','','','68'); +INSERT INTO "Address__c" VALUES(13,'China','18 Drewry Street','','Yangshufang','','','','','','72'); +INSERT INTO "Address__c" VALUES(14,'China','18 Twin Pines Circle','','Laojiangjunjie','','','','','','73'); +INSERT INTO "Address__c" VALUES(15,'China','2 Victoria Point','','Liquan Chengguanzhen','','','','','','74'); +INSERT INTO "Address__c" VALUES(16,'China','26 Annamark Alley','','Hatu Buh','','','','','','75'); +INSERT INTO "Address__c" VALUES(17,'China','3 Buena Vista Road','','Yonghe','','','','','','76'); +INSERT INTO "Address__c" VALUES(18,'China','428 Katie Court','','Jiangning','','','','','','77'); +INSERT INTO "Address__c" VALUES(19,'France','97798 Green Trail','Aquitaine','Boé','47555 CEDEX','','','','','100'); +INSERT INTO "Address__c" VALUES(20,'China','491 Lerdahl Pass','','Shangjiangxu','','','','','','78'); +INSERT INTO "Address__c" VALUES(21,'China','57 Mitchell Circle','','Gaotuo','','','','','','79'); +INSERT INTO "Address__c" VALUES(22,'China','59 Sheridan Avenue','','Xiaowuzhan','','','','','','80'); +INSERT INTO "Address__c" VALUES(23,'China','612 Almo Hill','','Shuidong','','','','','','81'); +INSERT INTO "Address__c" VALUES(24,'China','67377 Memorial Road','','Baikui','','','','','','82'); +INSERT INTO "Address__c" VALUES(25,'China','73 Browning Junction','','Licheng','','','','','','83'); +INSERT INTO "Address__c" VALUES(26,'China','8193 Dunning Point','','Gangnan','','','','','','84'); +INSERT INTO "Address__c" VALUES(27,'China','82521 Pond Terrace','','Bailang','','','','','','85'); +INSERT INTO "Address__c" VALUES(28,'China','90472 Montana Center','','Tangqian','','','','','','86'); +INSERT INTO "Address__c" VALUES(29,'China','9068 Clove Pass','','Xinfeng','','','','','','87'); +INSERT INTO "Address__c" VALUES(30,'Colombia','2 Mccormick Drive','','Arboleda','','','','','','88'); +INSERT INTO "Address__c" VALUES(31,'Czech Republic','230 Veith Place','','Želiv','','','','','','89'); +INSERT INTO "Address__c" VALUES(32,'Czech Republic','59 Mosinee Drive','','Dřiteň','','','','','','90'); +INSERT INTO "Address__c" VALUES(33,'Czech Republic','65 Maywood Drive','','Hošťka','','','','','','91'); +INSERT INTO "Address__c" VALUES(34,'Czech Republic','82 Lukken Terrace','','Sedlice','','','','','','92'); +INSERT INTO "Address__c" VALUES(35,'Czech Republic','9407 Dottie Street','','Žirovnice','','','','','','93'); +INSERT INTO "Address__c" VALUES(36,'Ecuador','67 Milwaukee Parkway','','Montalvo','','','','','','94'); +INSERT INTO "Address__c" VALUES(37,'Finland','8 Loomis Alley','','Muhos','','','','','','95'); +INSERT INTO "Address__c" VALUES(38,'France','4564 Victoria Parkway','Rhône-Alpes','Meylan','38244 CEDEX','','','','','96'); +INSERT INTO "Address__c" VALUES(39,'France','5 Burning Wood Alley','Poitou-Charentes','Royan','17209 CEDEX','','','','','97'); +INSERT INTO "Address__c" VALUES(40,'France','861 Dahle Street','Centre','Orléans','45933 CEDEX 9','','','','','98'); +INSERT INTO "Address__c" VALUES(41,'France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','','','','','99'); +INSERT INTO "Address__c" VALUES(42,'Indonesia','075 High Crossing Center','','Kajanan','','','','','','101'); +INSERT INTO "Address__c" VALUES(43,'Indonesia','2 Chinook Road','','Sumberdangdang','','','','','','102'); +INSERT INTO "Address__c" VALUES(44,'Indonesia','402 Loomis Lane','','Cileunyi','','','','','','103'); +INSERT INTO "Address__c" VALUES(45,'Indonesia','52 Mifflin Circle','','Tempaling','','','','','','104'); +INSERT INTO "Address__c" VALUES(46,'Italy','2 Old Gate Parkway','Sicilia','Messina','98146','','','','','105'); +INSERT INTO "Address__c" VALUES(47,'Japan','9 Carpenter Trail','','Jōetsu','','','','','','106'); +INSERT INTO "Address__c" VALUES(48,'Kazakhstan','0491 La Follette Crossing','','Taldyqorghan','','','','','','107'); +INSERT INTO "Address__c" VALUES(49,'Kazakhstan','1902 Randy Alley','','Kokshetau','','','','','','108'); +INSERT INTO "Address__c" VALUES(50,'New Zealand','535 Susan Trail','','Red Hill','','','','','','109'); +INSERT INTO "Address__c" VALUES(51,'Poland','098 Scoville Point','','Przecław','','','','','','124'); +INSERT INTO "Address__c" VALUES(52,'Nigeria','7060 Marcy Center','','Tegina','','','','','','110'); +INSERT INTO "Address__c" VALUES(53,'Philippines','0087 Russell Pass','','Takub','','','','','','111'); +INSERT INTO "Address__c" VALUES(54,'Philippines','0990 Mayfield Hill','','Calaba','','','','','','112'); +INSERT INTO "Address__c" VALUES(55,'Philippines','1 Oriole Parkway','','Hermosa','','','','','','113'); +INSERT INTO "Address__c" VALUES(56,'Philippines','126 Morningstar Park','','Bantacan','','','','','','114'); +INSERT INTO "Address__c" VALUES(57,'Philippines','309 Basil Junction','','Sexmoan','','','','','','115'); +INSERT INTO "Address__c" VALUES(58,'Philippines','4026 Monument Terrace','','Lais','','','','','','116'); +INSERT INTO "Address__c" VALUES(59,'Greece','0 Carpenter Lane','','Markópoulo Oropoú','','','','','','117'); +INSERT INTO "Address__c" VALUES(60,'Greece','18113 Helena Place','','Kariaí','','','','','','118'); +INSERT INTO "Address__c" VALUES(61,'Greece','7589 Pennsylvania Junction','','Kassándreia','','','','','','119'); +INSERT INTO "Address__c" VALUES(62,'Philippines','75 Ramsey Pass','','Bantacan','','','','','','120'); +INSERT INTO "Address__c" VALUES(63,'Philippines','909 Alpine Avenue','','Lumbang','','','','','','121'); +INSERT INTO "Address__c" VALUES(64,'Philippines','92336 Bunker Hill Drive','','Mambago','','','','','','122'); +INSERT INTO "Address__c" VALUES(65,'Poland','0 Darwin Lane','','Kowale','','','','','','123'); +INSERT INTO "Address__c" VALUES(66,'Poland','942 Vidon Hill','','Kiełczów','','','','','','127'); +INSERT INTO "Address__c" VALUES(67,'Portugal','0 Shopko Avenue','Lisboa','Aldeia de Juzo','2750-015','','','','','128'); +INSERT INTO "Address__c" VALUES(68,'Portugal','49997 Oakridge Pass','Coimbra','Ribeira','3360-257','','','','','129'); +INSERT INTO "Address__c" VALUES(69,'Poland','13 Crownhardt Point','','Wilkowice','','','','','','125'); +INSERT INTO "Address__c" VALUES(70,'Poland','843 Cottonwood Lane','','Świnna','','','','','','126'); +INSERT INTO "Address__c" VALUES(71,'Russia','0747 Drewry Crossing','','Staropyshminsk','','','','','','130'); +INSERT INTO "Address__c" VALUES(72,'Russia','4 Lillian Trail','','Slyudyanka','','','','','','131'); +INSERT INTO "Address__c" VALUES(73,'Russia','755 Thierer Place','','Uchkulan','','','','','','132'); +INSERT INTO "Address__c" VALUES(74,'Russia','8 Transport Plaza','','Peresvet','','','','','','133'); +INSERT INTO "Address__c" VALUES(75,'Russia','93 Eastlawn Crossing','','Bolgar','','','','','','134'); +INSERT INTO "Address__c" VALUES(76,'Sierra Leone','484 Londonderry Plaza','','Kayima','','','','','','135'); +INSERT INTO "Address__c" VALUES(77,'South Korea','701 Waubesa Crossing','','Enjitsu','','','','','','136'); +INSERT INTO "Address__c" VALUES(78,'Sweden','62 Graedel Center','Stockholm','Sundbyberg','172 69','','','','','137'); +INSERT INTO "Address__c" VALUES(79,'Syria','24609 Thompson Parkway','','Tasīl','','','','','','138'); +INSERT INTO "Address__c" VALUES(80,'Thailand','1 Basil Circle','','Bueng Samakkhi','','','','','','139'); +INSERT INTO "Address__c" VALUES(81,'Thailand','4328 Hoffman Road','','Kamalasai','','','','','','140'); +INSERT INTO "Address__c" VALUES(82,'Thailand','5730 International Lane','','Phuket','','','','','','141'); +INSERT INTO "Address__c" VALUES(83,'Ukraine','2072 Hayes Plaza','','Rybache','','','','','','142'); +INSERT INTO "Address__c" VALUES(84,'Ukraine','39955 Canary Avenue','','Truskavets','','','','','','143'); +INSERT INTO "Address__c" VALUES(85,'United States','07050 Doe Crossing Plaza','Minnesota','Saint Paul','55108','','','','','144'); +INSERT INTO "Address__c" VALUES(86,'United States','36055 Maple Hill','Connecticut','Waterbury','06705','','','','','145'); +INSERT INTO "Address__c" VALUES(87,'United States','50 Cherokee Way','Louisiana','Baton Rouge','70894','','','','','146'); +INSERT INTO "Address__c" VALUES(88,'Argentina','26 Blackbird Junction','','La Banda','','6','15','8','30','60'); +INSERT INTO "Address__c" VALUES(89,'Argentina','26 Blackbird Junction','','La Banda','','','','','','60'); +INSERT INTO "Address__c" VALUES(90,'China','03 Arapahoe Hill','','Jiangxi','','','','','','69'); +INSERT INTO "Address__c" VALUES(91,'China','0377 Shasta Trail','','Panzhou','','','','','','70'); +INSERT INTO "Address__c" VALUES(92,'China','1056 Corscot Center','','Guanzhou','','','','','','71'); +INSERT INTO "Address__c" VALUES(93,'China','18 Drewry Street','','Yangshufang','','','','','','72'); +INSERT INTO "Address__c" VALUES(94,'China','18 Twin Pines Circle','','Laojiangjunjie','','','','','','73'); +INSERT INTO "Address__c" VALUES(95,'China','2 Victoria Point','','Liquan Chengguanzhen','','','','','','74'); +INSERT INTO "Address__c" VALUES(96,'China','26 Annamark Alley','','Hatu Buh','','','','','','75'); +INSERT INTO "Address__c" VALUES(97,'China','3 Buena Vista Road','','Yonghe','','','','','','76'); +INSERT INTO "Address__c" VALUES(98,'China','428 Katie Court','','Jiangning','','','','','','77'); +INSERT INTO "Address__c" VALUES(99,'China','491 Lerdahl Pass','','Shangjiangxu','','','','','','78'); +INSERT INTO "Address__c" VALUES(100,'Argentina','392 Mariners Cove Street','','La Falda','','','','','','61'); +INSERT INTO "Address__c" VALUES(101,'China','57 Mitchell Circle','','Gaotuo','','','','','','79'); +INSERT INTO "Address__c" VALUES(102,'China','59 Sheridan Avenue','','Xiaowuzhan','','','','','','80'); +INSERT INTO "Address__c" VALUES(103,'China','612 Almo Hill','','Shuidong','','','','','','81'); +INSERT INTO "Address__c" VALUES(104,'China','67377 Memorial Road','','Baikui','','','','','','82'); +INSERT INTO "Address__c" VALUES(105,'China','73 Browning Junction','','Licheng','','','','','','83'); +INSERT INTO "Address__c" VALUES(106,'China','8193 Dunning Point','','Gangnan','','','','','','84'); +INSERT INTO "Address__c" VALUES(107,'China','82521 Pond Terrace','','Bailang','','','','','','85'); +INSERT INTO "Address__c" VALUES(108,'Kazakhstan','0491 La Follette Crossing','','Taldyqorghan','','','','','','92'); +INSERT INTO "Address__c" VALUES(109,'Kazakhstan','1902 Randy Alley','','Kokshetau','','','','','','92'); +INSERT INTO "Address__c" VALUES(110,'New Zealand','535 Susan Trail','','Red Hill','','','','','','92'); +INSERT INTO "Address__c" VALUES(111,'Nigeria','7060 Marcy Center','','Tegina','','','','','','92'); +INSERT INTO "Address__c" VALUES(112,'Philippines','0087 Russell Pass','','Takub','','','','','','92'); +INSERT INTO "Address__c" VALUES(113,'Philippines','0990 Mayfield Hill','','Calaba','','','','','','92'); +INSERT INTO "Address__c" VALUES(114,'Philippines','1 Oriole Parkway','','Hermosa','','','','','','92'); +INSERT INTO "Address__c" VALUES(115,'Philippines','126 Morningstar Park','','Bantacan','','','','','','92'); +INSERT INTO "Address__c" VALUES(116,'Czech Republic','82 Lukken Terrace','','Sedlice','','','','','','92'); +INSERT INTO "Address__c" VALUES(117,'Colombia','2 Mccormick Drive','','Arboleda','','','','','','92'); +INSERT INTO "Address__c" VALUES(118,'Czech Republic','230 Veith Place','','Želiv','','','','','','92'); +INSERT INTO "Address__c" VALUES(119,'Czech Republic','59 Mosinee Drive','','Dřiteň','','','','','','92'); +INSERT INTO "Address__c" VALUES(120,'Czech Republic','65 Maywood Drive','','Hošťka','','','','','','92'); +INSERT INTO "Address__c" VALUES(121,'Czech Republic','82 Lukken Terrace','','Sedlice','','','','','','92'); +INSERT INTO "Address__c" VALUES(122,'Czech Republic','9407 Dottie Street','','Žirovnice','','','','','','92'); +INSERT INTO "Address__c" VALUES(123,'Ecuador','67 Milwaukee Parkway','','Montalvo','','','','','','92'); +INSERT INTO "Address__c" VALUES(124,'Finland','8 Loomis Alley','','Muhos','','','','','','92'); +INSERT INTO "Address__c" VALUES(125,'France','4564 Victoria Parkway','Rhône-Alpes','Meylan','38244 CEDEX','','','','','92'); +INSERT INTO "Address__c" VALUES(126,'France','5 Burning Wood Alley','Poitou-Charentes','Royan','17209 CEDEX','','','','','92'); +INSERT INTO "Address__c" VALUES(127,'France','861 Dahle Street','Centre','Orléans','45933 CEDEX 9','','','','','92'); +INSERT INTO "Address__c" VALUES(128,'France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','','','','','92'); +INSERT INTO "Address__c" VALUES(129,'France','97798 Green Trail','Aquitaine','Boé','47555 CEDEX','','','','','92'); +INSERT INTO "Address__c" VALUES(130,'Greece','18113 Helena Place','','Kariaí','','','','','','92'); +INSERT INTO "Address__c" VALUES(131,'Greece','7589 Pennsylvania Junction','','Kassándreia','','','','','','92'); +INSERT INTO "Address__c" VALUES(132,'Indonesia','075 High Crossing Center','','Kajanan','','','','','','92'); +INSERT INTO "Address__c" VALUES(133,'China','90472 Montana Center','','Tangqian','','','','','','86'); +INSERT INTO "Address__c" VALUES(134,'China','9068 Clove Pass','','Xinfeng','','','','','','87'); +INSERT INTO "Address__c" VALUES(135,'Colombia','2 Mccormick Drive','','Arboleda','','','','','','88'); +INSERT INTO "Address__c" VALUES(136,'Brazil','01 Corry Park','','Irati','','','','','','62'); +INSERT INTO "Address__c" VALUES(137,'Czech Republic','230 Veith Place','','Želiv','','','','','','89'); +INSERT INTO "Address__c" VALUES(138,'Czech Republic','59 Mosinee Drive','','Dřiteň','','','','','','90'); +INSERT INTO "Address__c" VALUES(139,'Czech Republic','65 Maywood Drive','','Hošťka','','','','','','91'); +INSERT INTO "Address__c" VALUES(140,'United States','07050 Doe Crossing Plaza','Minnesota','Saint Paul','55108','','','','','92'); +INSERT INTO "Address__c" VALUES(141,'United States','36055 Maple Hill','Connecticut','Waterbury','06705','','','','','92'); +INSERT INTO "Address__c" VALUES(142,'United States','50 Cherokee Way','Louisiana','Baton Rouge','70894','','','','','92'); +INSERT INTO "Address__c" VALUES(143,'Argentina','26 Blackbird Junction','','La Banda','','','','','','92'); +INSERT INTO "Address__c" VALUES(144,'Argentina','392 Mariners Cove Street','','La Falda','','','','','','92'); +INSERT INTO "Address__c" VALUES(145,'Brazil','01 Corry Park','','Irati','','','','','','92'); +INSERT INTO "Address__c" VALUES(146,'Brazil','10 Butterfield Place','','Açucena','','','','','','92'); +INSERT INTO "Address__c" VALUES(147,'Brazil','20 Oakridge Circle','','Guaratuba','','','','','','92'); +INSERT INTO "Address__c" VALUES(148,'Brazil','3170 Mitchell Parkway','','Campinas','','','','','','92'); +INSERT INTO "Address__c" VALUES(149,'Brazil','405 Sunbrook Terrace','','Codajás','','','','','','92'); +INSERT INTO "Address__c" VALUES(150,'Canada','819 Sycamore Avenue','','Dollard-Des Ormeaux','','','','','','92'); +INSERT INTO "Address__c" VALUES(151,'China','02 Golf View Pass','','Xindu','','','','','','92'); +INSERT INTO "Address__c" VALUES(152,'China','03 Arapahoe Hill','','Jiangxi','','','','','','92'); +INSERT INTO "Address__c" VALUES(153,'China','0377 Shasta Trail','','Panzhou','','','','','','92'); +INSERT INTO "Address__c" VALUES(154,'China','1056 Corscot Center','','Guanzhou','','','','','','92'); +INSERT INTO "Address__c" VALUES(155,'China','18 Drewry Street','','Yangshufang','','','','','','92'); +INSERT INTO "Address__c" VALUES(156,'China','18 Twin Pines Circle','','Laojiangjunjie','','','','','','92'); +INSERT INTO "Address__c" VALUES(157,'China','2 Victoria Point','','Liquan Chengguanzhen','','','','','','92'); +INSERT INTO "Address__c" VALUES(158,'China','26 Annamark Alley','','Hatu Buh','','','','','','92'); +INSERT INTO "Address__c" VALUES(159,'China','3 Buena Vista Road','','Yonghe','','','','','','92'); +INSERT INTO "Address__c" VALUES(160,'China','428 Katie Court','','Jiangning','','','','','','92'); +INSERT INTO "Address__c" VALUES(161,'China','491 Lerdahl Pass','','Shangjiangxu','','','','','','92'); +INSERT INTO "Address__c" VALUES(162,'Sweden','62 Graedel Center','Stockholm','Sundbyberg','172 69','','','','','92'); +INSERT INTO "Address__c" VALUES(163,'Syria','24609 Thompson Parkway','','Tasīl','','','','','','92'); +INSERT INTO "Address__c" VALUES(164,'Thailand','1 Basil Circle','','Bueng Samakkhi','','','','','','92'); +INSERT INTO "Address__c" VALUES(165,'Thailand','4328 Hoffman Road','','Kamalasai','','','','','','92'); +INSERT INTO "Address__c" VALUES(166,'Thailand','5730 International Lane','','Phuket','','','','','','92'); +INSERT INTO "Address__c" VALUES(167,'Ukraine','2072 Hayes Plaza','','Rybache','','','','','','92'); +INSERT INTO "Address__c" VALUES(168,'Ukraine','39955 Canary Avenue','','Truskavets','','','','','','92'); +INSERT INTO "Address__c" VALUES(169,'Greece','0 Carpenter Lane','','Markópoulo Oropoú','','','','','','92'); +INSERT INTO "Address__c" VALUES(170,'Czech Republic','9407 Dottie Street','','Žirovnice','','','','','','93'); +INSERT INTO "Address__c" VALUES(171,'Ecuador','67 Milwaukee Parkway','','Montalvo','','','','','','94'); +INSERT INTO "Address__c" VALUES(172,'Finland','8 Loomis Alley','','Muhos','','','','','','95'); +INSERT INTO "Address__c" VALUES(173,'France','4564 Victoria Parkway','Rhône-Alpes','Meylan','38244 CEDEX','','','','','96'); +INSERT INTO "Address__c" VALUES(174,'France','5 Burning Wood Alley','Poitou-Charentes','Royan','17209 CEDEX','','','','','97'); +INSERT INTO "Address__c" VALUES(175,'France','861 Dahle Street','Centre','Orléans','45933 CEDEX 9','','','','','98'); +INSERT INTO "Address__c" VALUES(176,'Brazil','10 Butterfield Place','','Açucena','','','','','','63'); +INSERT INTO "Address__c" VALUES(177,'France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','6','15','8','30','99'); +INSERT INTO "Address__c" VALUES(178,'France','87 Cambridge Hill','Picardie','Amiens','80891 CEDEX 3','','','','','99'); +INSERT INTO "Address__c" VALUES(179,'France','97798 Green Trail','Aquitaine','Boé','47555 CEDEX','','','','','100'); +INSERT INTO "Address__c" VALUES(180,'Indonesia','075 High Crossing Center','','Kajanan','','','','','','101'); +INSERT INTO "Address__c" VALUES(181,'Indonesia','2 Chinook Road','','Sumberdangdang','','','','','','102'); +INSERT INTO "Address__c" VALUES(182,'Indonesia','402 Loomis Lane','','Cileunyi','','','','','','103'); +INSERT INTO "Address__c" VALUES(183,'Indonesia','52 Mifflin Circle','','Tempaling','','','','','','104'); +INSERT INTO "Address__c" VALUES(184,'Italy','2 Old Gate Parkway','Sicilia','Messina','98146','','','','','105'); +INSERT INTO "Address__c" VALUES(185,'Japan','9 Carpenter Trail','','Jōetsu','','','','','','106'); +INSERT INTO "Address__c" VALUES(186,'Kazakhstan','0491 La Follette Crossing','','Taldyqorghan','','','','','','107'); +INSERT INTO "Address__c" VALUES(187,'Kazakhstan','1902 Randy Alley','','Kokshetau','','','','','','108'); +INSERT INTO "Address__c" VALUES(188,'Brazil','20 Oakridge Circle','','Guaratuba','','','','','','64'); +INSERT INTO "Address__c" VALUES(189,'Russia','8 Transport Plaza','','Peresvet','','','','','','133'); +INSERT INTO "Address__c" VALUES(190,'Russia','93 Eastlawn Crossing','','Bolgar','','','','','','134'); +INSERT INTO "Address__c" VALUES(191,'Sierra Leone','484 Londonderry Plaza','','Kayima','','','','','','135'); +INSERT INTO "Address__c" VALUES(192,'South Korea','701 Waubesa Crossing','','Enjitsu','','','','','','136'); +INSERT INTO "Address__c" VALUES(193,'Sweden','62 Graedel Center','Stockholm','Sundbyberg','172 69','','','','','137'); +INSERT INTO "Address__c" VALUES(194,'Syria','24609 Thompson Parkway','','Tasīl','','','','','','138'); +INSERT INTO "Address__c" VALUES(195,'Canada','819 Sycamore Avenue','','Dollard-Des Ormeaux','','','','','','67'); +INSERT INTO "Address__c" VALUES(196,'Thailand','1 Basil Circle','','Bueng Samakkhi','','','','','','139'); +INSERT INTO "Address__c" VALUES(197,'Thailand','4328 Hoffman Road','','Kamalasai','','','','','','140'); +INSERT INTO "Address__c" VALUES(198,'Thailand','5730 International Lane','','Phuket','','','','','','141'); +INSERT INTO "Address__c" VALUES(199,'Ukraine','2072 Hayes Plaza','','Rybache','','','','','','142'); +INSERT INTO "Address__c" VALUES(200,'Ukraine','39955 Canary Avenue','','Truskavets','','','','','','143'); +INSERT INTO "Address__c" VALUES(201,'United States','07050 Doe Crossing Plaza','Minnesota','Saint Paul','55108','','','','','144'); +INSERT INTO "Address__c" VALUES(202,'United States','36055 Maple Hill','Connecticut','Waterbury','06705','','','','','145'); +INSERT INTO "Address__c" VALUES(203,'United States','50 Cherokee Way','Louisiana','Baton Rouge','70894','','','','','146'); +INSERT INTO "Address__c" VALUES(204,'China','02 Golf View Pass','','Xindu','','6','15','8','30','68'); +INSERT INTO "Address__c" VALUES(205,'China','02 Golf View Pass','','Xindu','','','','','','68'); +INSERT INTO "Address__c" VALUES(206,'Russia','4 Lillian Trail','','Slyudyanka','','','','','','131'); +INSERT INTO "Address__c" VALUES(207,'Russia','755 Thierer Place','','Uchkulan','','','','','','132'); +INSERT INTO "Address__c" VALUES(208,'Indonesia','2 Chinook Road','','Sumberdangdang','','','','','','92'); +INSERT INTO "Address__c" VALUES(209,'Indonesia','402 Loomis Lane','','Cileunyi','','','','','','92'); +INSERT INTO "Address__c" VALUES(210,'Philippines','309 Basil Junction','','Sexmoan','','','','','','92'); +INSERT INTO "Address__c" VALUES(211,'Philippines','4026 Monument Terrace','','Lais','','','','','','92'); +INSERT INTO "Address__c" VALUES(212,'Philippines','75 Ramsey Pass','','Bantacan','','','','','','92'); +INSERT INTO "Address__c" VALUES(213,'Philippines','909 Alpine Avenue','','Lumbang','','','','','','92'); +INSERT INTO "Address__c" VALUES(214,'Philippines','92336 Bunker Hill Drive','','Mambago','','','','','','92'); +INSERT INTO "Address__c" VALUES(215,'Poland','0 Darwin Lane','','Kowale','','','','','','92'); +INSERT INTO "Address__c" VALUES(216,'Poland','098 Scoville Point','','Przecław','','','','','','92'); +INSERT INTO "Address__c" VALUES(217,'Poland','13 Crownhardt Point','','Wilkowice','','','','','','92'); +INSERT INTO "Address__c" VALUES(218,'Poland','843 Cottonwood Lane','','Świnna','','','','','','92'); +INSERT INTO "Address__c" VALUES(219,'Poland','942 Vidon Hill','','Kiełczów','','','','','','92'); +INSERT INTO "Address__c" VALUES(220,'Portugal','0 Shopko Avenue','Lisboa','Aldeia de Juzo','2750-015','','','','','92'); +INSERT INTO "Address__c" VALUES(221,'Portugal','49997 Oakridge Pass','Coimbra','Ribeira','3360-257','','','','','92'); +INSERT INTO "Address__c" VALUES(222,'Russia','0747 Drewry Crossing','','Staropyshminsk','','','','','','92'); +INSERT INTO "Address__c" VALUES(223,'Russia','4 Lillian Trail','','Slyudyanka','','','','','','92'); +INSERT INTO "Address__c" VALUES(224,'Russia','755 Thierer Place','','Uchkulan','','','','','','92'); +INSERT INTO "Address__c" VALUES(225,'Russia','8 Transport Plaza','','Peresvet','','','','','','92'); +INSERT INTO "Address__c" VALUES(226,'Russia','93 Eastlawn Crossing','','Bolgar','','','','','','92'); +INSERT INTO "Address__c" VALUES(227,'Sierra Leone','484 Londonderry Plaza','','Kayima','','','','','','92'); +INSERT INTO "Address__c" VALUES(228,'South Korea','701 Waubesa Crossing','','Enjitsu','','','','','','92'); +INSERT INTO "Address__c" VALUES(229,'China','57 Mitchell Circle','','Gaotuo','','','','','','92'); +INSERT INTO "Address__c" VALUES(230,'China','59 Sheridan Avenue','','Xiaowuzhan','','','','','','92'); +INSERT INTO "Address__c" VALUES(231,'China','612 Almo Hill','','Shuidong','','','','','','92'); +INSERT INTO "Address__c" VALUES(232,'China','67377 Memorial Road','','Baikui','','','','','','92'); +INSERT INTO "Address__c" VALUES(233,'China','73 Browning Junction','','Licheng','','','','','','92'); +INSERT INTO "Address__c" VALUES(234,'China','8193 Dunning Point','','Gangnan','','','','','','92'); +INSERT INTO "Address__c" VALUES(235,'China','82521 Pond Terrace','','Bailang','','','','','','92'); +INSERT INTO "Address__c" VALUES(236,'China','90472 Montana Center','','Tangqian','','','','','','92'); +INSERT INTO "Address__c" VALUES(237,'China','9068 Clove Pass','','Xinfeng','','','','','','92'); +INSERT INTO "Address__c" VALUES(238,'Indonesia','52 Mifflin Circle','','Tempaling','','','','','','92'); +INSERT INTO "Address__c" VALUES(239,'Italy','2 Old Gate Parkway','Sicilia','Messina','98146','','','','','92'); +INSERT INTO "Address__c" VALUES(240,'Japan','9 Carpenter Trail','','Jōetsu','','','','','','92'); +INSERT INTO "Address__c" VALUES(241,'New Zealand','535 Susan Trail','','Red Hill','','6','15','8','30','109'); +INSERT INTO "Address__c" VALUES(242,'New Zealand','535 Susan Trail','','Red Hill','','','','','','109'); +INSERT INTO "Address__c" VALUES(243,'Nigeria','7060 Marcy Center','','Tegina','','','','','','110'); +INSERT INTO "Address__c" VALUES(244,'Philippines','0087 Russell Pass','','Takub','','','','','','111'); +INSERT INTO "Address__c" VALUES(245,'Philippines','0990 Mayfield Hill','','Calaba','','','','','','112'); +INSERT INTO "Address__c" VALUES(246,'Philippines','1 Oriole Parkway','','Hermosa','','','','','','113'); +INSERT INTO "Address__c" VALUES(247,'Philippines','126 Morningstar Park','','Bantacan','','','','','','114'); +INSERT INTO "Address__c" VALUES(248,'Philippines','309 Basil Junction','','Sexmoan','','','','','','115'); +INSERT INTO "Address__c" VALUES(249,'Philippines','4026 Monument Terrace','','Lais','','','','','','116'); +INSERT INTO "Address__c" VALUES(250,'Greece','0 Carpenter Lane','','Markópoulo Oropoú','','','','','','117'); +INSERT INTO "Address__c" VALUES(251,'Greece','18113 Helena Place','','Kariaí','','','','','','118'); +INSERT INTO "Address__c" VALUES(252,'Brazil','3170 Mitchell Parkway','','Campinas','','','','','','65'); +INSERT INTO "Address__c" VALUES(253,'Greece','7589 Pennsylvania Junction','','Kassándreia','','','','','','119'); +INSERT INTO "Address__c" VALUES(254,'Philippines','75 Ramsey Pass','','Bantacan','','','','','','120'); +INSERT INTO "Address__c" VALUES(255,'Philippines','909 Alpine Avenue','','Lumbang','','','','','','121'); +INSERT INTO "Address__c" VALUES(256,'Philippines','92336 Bunker Hill Drive','','Mambago','','','','','','122'); +INSERT INTO "Address__c" VALUES(257,'Poland','0 Darwin Lane','','Kowale','','6','15','8','30','123'); +INSERT INTO "Address__c" VALUES(258,'Poland','0 Darwin Lane','','Kowale','','','','','','123'); +INSERT INTO "Address__c" VALUES(259,'Poland','098 Scoville Point','','Przecław','','','','','','124'); +INSERT INTO "Address__c" VALUES(260,'Poland','13 Crownhardt Point','','Wilkowice','','','','','','125'); +INSERT INTO "Address__c" VALUES(261,'Poland','843 Cottonwood Lane','','Świnna','','','','','','126'); +INSERT INTO "Address__c" VALUES(262,'Poland','942 Vidon Hill','','Kiełczów','','','','','','127'); +INSERT INTO "Address__c" VALUES(263,'Portugal','0 Shopko Avenue','Lisboa','Aldeia de Juzo','2750-015','','','','','128'); +INSERT INTO "Address__c" VALUES(264,'Brazil','405 Sunbrook Terrace','','Codajás','','','','','','66'); +INSERT INTO "Address__c" VALUES(265,'Portugal','49997 Oakridge Pass','Coimbra','Ribeira','3360-257','','','','','129'); +INSERT INTO "Address__c" VALUES(266,'Russia','0747 Drewry Crossing','','Staropyshminsk','','','','','','130'); CREATE TABLE "Allocation__c" ( - id INTEGER NOT NULL, - "Percent__c" VARCHAR(255), - "Amount__c" VARCHAR(255), - opportunity__c VARCHAR(255), - general_accounting_unit__c VARCHAR(255), - recurring_donation__c VARCHAR(255), + id INTEGER NOT NULL, + "Percent__c" VARCHAR(255), + "Amount__c" VARCHAR(255), + "Opportunity__c" VARCHAR(255), + "General_Accounting_Unit__c" VARCHAR(255), + "Recurring_Donation__c" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "Allocation__c" VALUES(1,'10.0','','19','13',''); -INSERT INTO "Allocation__c" VALUES(2,'10.0','','19','15',''); -INSERT INTO "Allocation__c" VALUES(3,'10.0','','19','20',''); -INSERT INTO "Allocation__c" VALUES(4,'10.0','','19','29',''); -INSERT INTO "Allocation__c" VALUES(5,'10.0','','19','45',''); -INSERT INTO "Allocation__c" VALUES(6,'10.0','','19','47',''); -INSERT INTO "Allocation__c" VALUES(7,'10.0','','19','1',''); -INSERT INTO "Allocation__c" VALUES(8,'10.0','','19','3',''); -INSERT INTO "Allocation__c" VALUES(9,'10.0','','19','3',''); -INSERT INTO "Allocation__c" VALUES(10,'10.0','','19','12',''); -INSERT INTO "Allocation__c" VALUES(11,'10.0','','204','22',''); -INSERT INTO "Allocation__c" VALUES(12,'10.0','','204','27',''); -INSERT INTO "Allocation__c" VALUES(13,'10.0','','204','28',''); -INSERT INTO "Allocation__c" VALUES(14,'10.0','','204','8',''); -INSERT INTO "Allocation__c" VALUES(15,'10.0','','204','36',''); -INSERT INTO "Allocation__c" VALUES(16,'10.0','','204','41',''); -INSERT INTO "Allocation__c" VALUES(17,'10.0','','204','45',''); -INSERT INTO "Allocation__c" VALUES(18,'10.0','','204','48',''); -INSERT INTO "Allocation__c" VALUES(19,'10.0','','204','4',''); -INSERT INTO "Allocation__c" VALUES(20,'10.0','','204','24',''); -INSERT INTO "Allocation__c" VALUES(21,'10.0','','208','16',''); -INSERT INTO "Allocation__c" VALUES(22,'10.0','','208','7',''); -INSERT INTO "Allocation__c" VALUES(23,'10.0','','208','23',''); -INSERT INTO "Allocation__c" VALUES(24,'10.0','','208','30',''); -INSERT INTO "Allocation__c" VALUES(25,'10.0','','208','31',''); -INSERT INTO "Allocation__c" VALUES(26,'10.0','','208','34',''); -INSERT INTO "Allocation__c" VALUES(27,'10.0','','208','40',''); -INSERT INTO "Allocation__c" VALUES(28,'10.0','','208','44',''); -INSERT INTO "Allocation__c" VALUES(29,'10.0','','208','5',''); -INSERT INTO "Allocation__c" VALUES(30,'10.0','','208','2',''); -INSERT INTO "Allocation__c" VALUES(31,'10.0','','149','18',''); -INSERT INTO "Allocation__c" VALUES(32,'10.0','','149','26',''); -INSERT INTO "Allocation__c" VALUES(33,'10.0','','149','28',''); -INSERT INTO "Allocation__c" VALUES(34,'10.0','','149','33',''); -INSERT INTO "Allocation__c" VALUES(35,'10.0','','149','35',''); -INSERT INTO "Allocation__c" VALUES(36,'10.0','','149','37',''); -INSERT INTO "Allocation__c" VALUES(37,'10.0','','149','42',''); -INSERT INTO "Allocation__c" VALUES(38,'10.0','','149','42',''); -INSERT INTO "Allocation__c" VALUES(39,'10.0','','149','4',''); -INSERT INTO "Allocation__c" VALUES(40,'10.0','','149','2',''); -INSERT INTO "Allocation__c" VALUES(41,'10.0','','164','14',''); -INSERT INTO "Allocation__c" VALUES(42,'10.0','','164','15',''); -INSERT INTO "Allocation__c" VALUES(43,'10.0','','164','17',''); -INSERT INTO "Allocation__c" VALUES(44,'10.0','','164','33',''); -INSERT INTO "Allocation__c" VALUES(45,'10.0','','164','38',''); -INSERT INTO "Allocation__c" VALUES(46,'10.0','','164','38',''); -INSERT INTO "Allocation__c" VALUES(47,'10.0','','164','41',''); -INSERT INTO "Allocation__c" VALUES(48,'10.0','','164','50',''); -INSERT INTO "Allocation__c" VALUES(49,'10.0','','164','1',''); -INSERT INTO "Allocation__c" VALUES(50,'10.0','','164','12',''); -INSERT INTO "Allocation__c" VALUES(51,'10.0','','89','19',''); -INSERT INTO "Allocation__c" VALUES(52,'10.0','','89','26',''); -INSERT INTO "Allocation__c" VALUES(53,'10.0','','89','27',''); -INSERT INTO "Allocation__c" VALUES(54,'10.0','','89','36',''); -INSERT INTO "Allocation__c" VALUES(55,'10.0','','89','47',''); -INSERT INTO "Allocation__c" VALUES(56,'10.0','','89','47',''); -INSERT INTO "Allocation__c" VALUES(57,'10.0','','89','2',''); -INSERT INTO "Allocation__c" VALUES(58,'10.0','','89','24',''); -INSERT INTO "Allocation__c" VALUES(59,'10.0','','89','10',''); -INSERT INTO "Allocation__c" VALUES(60,'10.0','','89','11',''); -INSERT INTO "Allocation__c" VALUES(61,'10.0','','199','14',''); -INSERT INTO "Allocation__c" VALUES(62,'10.0','','199','16',''); -INSERT INTO "Allocation__c" VALUES(63,'10.0','','199','21',''); -INSERT INTO "Allocation__c" VALUES(64,'10.0','','13','19',''); -INSERT INTO "Allocation__c" VALUES(65,'10.0','','13','29',''); -INSERT INTO "Allocation__c" VALUES(66,'10.0','','13','32',''); -INSERT INTO "Allocation__c" VALUES(67,'10.0','','13','9',''); -INSERT INTO "Allocation__c" VALUES(68,'10.0','','13','50',''); -INSERT INTO "Allocation__c" VALUES(69,'10.0','','13','50',''); -INSERT INTO "Allocation__c" VALUES(70,'10.0','','13','25',''); -INSERT INTO "Allocation__c" VALUES(71,'10.0','','13','12',''); -INSERT INTO "Allocation__c" VALUES(72,'10.0','','13','12',''); -INSERT INTO "Allocation__c" VALUES(73,'10.0','','13','18',''); -INSERT INTO "Allocation__c" VALUES(74,'10.0','','199','22',''); -INSERT INTO "Allocation__c" VALUES(75,'10.0','','199','38',''); -INSERT INTO "Allocation__c" VALUES(76,'10.0','','199','39',''); -INSERT INTO "Allocation__c" VALUES(77,'10.0','','199','40',''); -INSERT INTO "Allocation__c" VALUES(78,'10.0','','199','49',''); -INSERT INTO "Allocation__c" VALUES(79,'10.0','','199','12',''); -INSERT INTO "Allocation__c" VALUES(80,'10.0','','123','6',''); -INSERT INTO "Allocation__c" VALUES(81,'10.0','','123','15',''); -INSERT INTO "Allocation__c" VALUES(82,'10.0','','123','17',''); -INSERT INTO "Allocation__c" VALUES(83,'10.0','','123','20',''); -INSERT INTO "Allocation__c" VALUES(84,'10.0','','123','7',''); -INSERT INTO "Allocation__c" VALUES(85,'10.0','','123','35',''); -INSERT INTO "Allocation__c" VALUES(86,'10.0','','123','43',''); -INSERT INTO "Allocation__c" VALUES(87,'10.0','','123','46',''); -INSERT INTO "Allocation__c" VALUES(88,'10.0','','123','47',''); -INSERT INTO "Allocation__c" VALUES(89,'10.0','','123','3',''); -INSERT INTO "Allocation__c" VALUES(90,'10.0','','124','13',''); -INSERT INTO "Allocation__c" VALUES(91,'10.0','','124','15',''); -INSERT INTO "Allocation__c" VALUES(92,'10.0','','124','18',''); -INSERT INTO "Allocation__c" VALUES(93,'10.0','','124','20',''); -INSERT INTO "Allocation__c" VALUES(94,'10.0','','124','30',''); -INSERT INTO "Allocation__c" VALUES(95,'10.0','','124','34',''); -INSERT INTO "Allocation__c" VALUES(96,'10.0','','124','44',''); -INSERT INTO "Allocation__c" VALUES(97,'10.0','','124','24',''); -INSERT INTO "Allocation__c" VALUES(98,'10.0','','124','3',''); -INSERT INTO "Allocation__c" VALUES(99,'10.0','','124','12',''); -INSERT INTO "Allocation__c" VALUES(100,'10.0','','199','22',''); +INSERT INTO "Allocation__c" VALUES(1,'10.0','39.3','156','15',''); +INSERT INTO "Allocation__c" VALUES(2,'10.0','39.3','156','17',''); +INSERT INTO "Allocation__c" VALUES(3,'10.0','39.3','156','20',''); +INSERT INTO "Allocation__c" VALUES(4,'10.0','39.3','156','3',''); +INSERT INTO "Allocation__c" VALUES(5,'10.0','39.3','156','35',''); +INSERT INTO "Allocation__c" VALUES(6,'10.0','39.3','156','41',''); +INSERT INTO "Allocation__c" VALUES(7,'10.0','39.3','156','44',''); +INSERT INTO "Allocation__c" VALUES(8,'10.0','39.3','156','47',''); +INSERT INTO "Allocation__c" VALUES(9,'10.0','39.3','156','6',''); +INSERT INTO "Allocation__c" VALUES(10,'10.0','39.3','156','7',''); +INSERT INTO "Allocation__c" VALUES(11,'10.0','64.3','157','12',''); +INSERT INTO "Allocation__c" VALUES(12,'10.0','64.3','157','13',''); +INSERT INTO "Allocation__c" VALUES(13,'10.0','64.3','157','15',''); +INSERT INTO "Allocation__c" VALUES(14,'10.0','64.3','157','18',''); +INSERT INTO "Allocation__c" VALUES(15,'10.0','64.3','157','20',''); +INSERT INTO "Allocation__c" VALUES(16,'10.0','64.3','157','24',''); +INSERT INTO "Allocation__c" VALUES(17,'10.0','64.3','157','3',''); +INSERT INTO "Allocation__c" VALUES(18,'10.0','64.3','157','30',''); +INSERT INTO "Allocation__c" VALUES(19,'10.0','64.3','157','34',''); +INSERT INTO "Allocation__c" VALUES(20,'10.0','64.3','157','42',''); +INSERT INTO "Allocation__c" VALUES(21,'10.0','496.5','45','12',''); +INSERT INTO "Allocation__c" VALUES(22,'10.0','496.5','45','12',''); +INSERT INTO "Allocation__c" VALUES(23,'10.0','496.5','45','18',''); +INSERT INTO "Allocation__c" VALUES(24,'10.0','496.5','45','19',''); +INSERT INTO "Allocation__c" VALUES(25,'10.0','496.5','45','25',''); +INSERT INTO "Allocation__c" VALUES(26,'10.0','496.5','45','29',''); +INSERT INTO "Allocation__c" VALUES(27,'10.0','496.5','45','32',''); +INSERT INTO "Allocation__c" VALUES(28,'10.0','496.5','45','50',''); +INSERT INTO "Allocation__c" VALUES(29,'10.0','496.5','45','50',''); +INSERT INTO "Allocation__c" VALUES(30,'10.0','496.5','45','9',''); +INSERT INTO "Allocation__c" VALUES(31,'10.0','32.7','182','18',''); +INSERT INTO "Allocation__c" VALUES(32,'10.0','32.7','182','2',''); +INSERT INTO "Allocation__c" VALUES(33,'10.0','32.7','182','26',''); +INSERT INTO "Allocation__c" VALUES(34,'10.0','32.7','182','28',''); +INSERT INTO "Allocation__c" VALUES(35,'10.0','32.7','182','33',''); +INSERT INTO "Allocation__c" VALUES(36,'10.0','32.7','182','35',''); +INSERT INTO "Allocation__c" VALUES(37,'10.0','32.7','182','37',''); +INSERT INTO "Allocation__c" VALUES(38,'10.0','32.7','182','4',''); +INSERT INTO "Allocation__c" VALUES(39,'10.0','32.7','182','40',''); +INSERT INTO "Allocation__c" VALUES(40,'10.0','32.7','182','40',''); +INSERT INTO "Allocation__c" VALUES(41,'10.0','54.0','197','1',''); +INSERT INTO "Allocation__c" VALUES(42,'10.0','54.0','197','12',''); +INSERT INTO "Allocation__c" VALUES(43,'10.0','54.0','197','14',''); +INSERT INTO "Allocation__c" VALUES(44,'10.0','54.0','197','15',''); +INSERT INTO "Allocation__c" VALUES(45,'10.0','8.5','20','34',''); +INSERT INTO "Allocation__c" VALUES(46,'10.0','8.5','20','45',''); +INSERT INTO "Allocation__c" VALUES(47,'10.0','8.5','20','42',''); +INSERT INTO "Allocation__c" VALUES(48,'10.0','8.5','20','5',''); +INSERT INTO "Allocation__c" VALUES(49,'10.0','8.5','20','31',''); +INSERT INTO "Allocation__c" VALUES(50,'10.0','8.5','20','7',''); +INSERT INTO "Allocation__c" VALUES(51,'10.0','75.1','120','10',''); +INSERT INTO "Allocation__c" VALUES(52,'10.0','75.1','120','11',''); +INSERT INTO "Allocation__c" VALUES(53,'10.0','75.1','120','19',''); +INSERT INTO "Allocation__c" VALUES(54,'10.0','75.1','120','2',''); +INSERT INTO "Allocation__c" VALUES(55,'10.0','75.1','120','24',''); +INSERT INTO "Allocation__c" VALUES(56,'10.0','75.1','120','26',''); +INSERT INTO "Allocation__c" VALUES(57,'10.0','75.1','120','27',''); +INSERT INTO "Allocation__c" VALUES(58,'10.0','75.1','120','36',''); +INSERT INTO "Allocation__c" VALUES(59,'10.0','75.1','120','47',''); +INSERT INTO "Allocation__c" VALUES(60,'10.0','75.1','120','47',''); +INSERT INTO "Allocation__c" VALUES(61,'10.0','54.0','197','17',''); +INSERT INTO "Allocation__c" VALUES(62,'10.0','54.0','197','33',''); +INSERT INTO "Allocation__c" VALUES(63,'10.0','54.0','197','38',''); +INSERT INTO "Allocation__c" VALUES(64,'10.0','54.0','197','38',''); +INSERT INTO "Allocation__c" VALUES(65,'10.0','54.0','197','46',''); +INSERT INTO "Allocation__c" VALUES(66,'10.0','54.0','197','50',''); +INSERT INTO "Allocation__c" VALUES(67,'10.0','785.8','51','1',''); +INSERT INTO "Allocation__c" VALUES(68,'10.0','785.8','51','12',''); +INSERT INTO "Allocation__c" VALUES(69,'10.0','785.8','51','13',''); +INSERT INTO "Allocation__c" VALUES(70,'10.0','785.8','51','15',''); +INSERT INTO "Allocation__c" VALUES(71,'10.0','785.8','51','20',''); +INSERT INTO "Allocation__c" VALUES(72,'10.0','785.8','51','29',''); +INSERT INTO "Allocation__c" VALUES(73,'10.0','785.8','51','3',''); +INSERT INTO "Allocation__c" VALUES(74,'10.0','785.8','51','3',''); +INSERT INTO "Allocation__c" VALUES(75,'10.0','785.8','51','43',''); +INSERT INTO "Allocation__c" VALUES(76,'10.0','785.8','51','47',''); +INSERT INTO "Allocation__c" VALUES(77,'10.0','48.5','232','12',''); +INSERT INTO "Allocation__c" VALUES(78,'10.0','48.5','232','14',''); +INSERT INTO "Allocation__c" VALUES(79,'10.0','48.5','232','16',''); +INSERT INTO "Allocation__c" VALUES(80,'10.0','48.5','232','21',''); +INSERT INTO "Allocation__c" VALUES(81,'10.0','48.5','232','22',''); +INSERT INTO "Allocation__c" VALUES(82,'10.0','48.5','232','22',''); +INSERT INTO "Allocation__c" VALUES(83,'10.0','48.5','232','38',''); +INSERT INTO "Allocation__c" VALUES(84,'10.0','48.5','232','39',''); +INSERT INTO "Allocation__c" VALUES(85,'10.0','48.5','232','45',''); +INSERT INTO "Allocation__c" VALUES(86,'10.0','48.5','232','49',''); +INSERT INTO "Allocation__c" VALUES(87,'10.0','75.7','16','22',''); +INSERT INTO "Allocation__c" VALUES(88,'10.0','75.7','16','24',''); +INSERT INTO "Allocation__c" VALUES(89,'10.0','75.7','16','27',''); +INSERT INTO "Allocation__c" VALUES(90,'10.0','75.7','16','28',''); +INSERT INTO "Allocation__c" VALUES(91,'10.0','75.7','16','36',''); +INSERT INTO "Allocation__c" VALUES(92,'10.0','75.7','16','4',''); +INSERT INTO "Allocation__c" VALUES(93,'10.0','75.7','16','46',''); +INSERT INTO "Allocation__c" VALUES(94,'10.0','75.7','16','43',''); +INSERT INTO "Allocation__c" VALUES(95,'10.0','75.7','16','48',''); +INSERT INTO "Allocation__c" VALUES(96,'10.0','75.7','16','8',''); +INSERT INTO "Allocation__c" VALUES(97,'10.0','8.5','20','16',''); +INSERT INTO "Allocation__c" VALUES(98,'10.0','8.5','20','2',''); +INSERT INTO "Allocation__c" VALUES(99,'10.0','8.5','20','23',''); +INSERT INTO "Allocation__c" VALUES(100,'10.0','8.5','20','30',''); CREATE TABLE "Campaign" ( - id INTEGER NOT NULL, - name VARCHAR(255), + id INTEGER NOT NULL, + name VARCHAR(255), PRIMARY KEY (id) ); INSERT INTO "Campaign" VALUES(1,'Jan 2015 Meeting'); @@ -505,292 +593,294 @@ INSERT INTO "Campaign" VALUES(11,'Nov 2015 Meeting'); INSERT INTO "Campaign" VALUES(12,'Dec 2015 Meeting'); INSERT INTO "Campaign" VALUES(13,'All Contacts'); CREATE TABLE "CampaignMember" ( - id INTEGER NOT NULL, - status VARCHAR(255), - contact_id VARCHAR(255), - campaign_id VARCHAR(255), + id INTEGER NOT NULL, + status VARCHAR(255), + "ContactId" VARCHAR(255), + "CampaignId" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "CampaignMember" VALUES(1,'Sent','1','13'); -INSERT INTO "CampaignMember" VALUES(2,'Sent','100','13'); -INSERT INTO "CampaignMember" VALUES(3,'Sent','5','13'); -INSERT INTO "CampaignMember" VALUES(4,'Sent','2','13'); -INSERT INTO "CampaignMember" VALUES(5,'Sent','3','13'); -INSERT INTO "CampaignMember" VALUES(6,'Sent','4','13'); -INSERT INTO "CampaignMember" VALUES(7,'Sent','6','1'); -INSERT INTO "CampaignMember" VALUES(8,'Sent','6','10'); -INSERT INTO "CampaignMember" VALUES(9,'Sent','6','11'); -INSERT INTO "CampaignMember" VALUES(10,'Sent','6','12'); -INSERT INTO "CampaignMember" VALUES(11,'Sent','6','13'); -INSERT INTO "CampaignMember" VALUES(12,'Sent','6','3'); -INSERT INTO "CampaignMember" VALUES(13,'Sent','6','4'); -INSERT INTO "CampaignMember" VALUES(14,'Sent','6','5'); -INSERT INTO "CampaignMember" VALUES(15,'Sent','6','6'); -INSERT INTO "CampaignMember" VALUES(16,'Sent','6','7'); -INSERT INTO "CampaignMember" VALUES(17,'Sent','6','8'); -INSERT INTO "CampaignMember" VALUES(18,'Sent','6','9'); -INSERT INTO "CampaignMember" VALUES(19,'Sent','7','13'); -INSERT INTO "CampaignMember" VALUES(20,'Sent','8','1'); -INSERT INTO "CampaignMember" VALUES(21,'Sent','8','10'); -INSERT INTO "CampaignMember" VALUES(22,'Sent','8','11'); -INSERT INTO "CampaignMember" VALUES(23,'Sent','8','12'); -INSERT INTO "CampaignMember" VALUES(24,'Sent','8','13'); -INSERT INTO "CampaignMember" VALUES(25,'Sent','8','3'); -INSERT INTO "CampaignMember" VALUES(26,'Sent','8','4'); -INSERT INTO "CampaignMember" VALUES(27,'Sent','8','5'); -INSERT INTO "CampaignMember" VALUES(28,'Sent','8','6'); -INSERT INTO "CampaignMember" VALUES(29,'Sent','8','7'); -INSERT INTO "CampaignMember" VALUES(30,'Sent','8','8'); -INSERT INTO "CampaignMember" VALUES(31,'Sent','8','9'); -INSERT INTO "CampaignMember" VALUES(32,'Sent','9','13'); -INSERT INTO "CampaignMember" VALUES(33,'Sent','9','2'); -INSERT INTO "CampaignMember" VALUES(34,'Sent','10','13'); -INSERT INTO "CampaignMember" VALUES(35,'Sent','10','2'); -INSERT INTO "CampaignMember" VALUES(36,'Sent','11','13'); -INSERT INTO "CampaignMember" VALUES(37,'Sent','14','13'); -INSERT INTO "CampaignMember" VALUES(38,'Sent','12','13'); -INSERT INTO "CampaignMember" VALUES(39,'Sent','13','13'); -INSERT INTO "CampaignMember" VALUES(40,'Sent','15','13'); -INSERT INTO "CampaignMember" VALUES(41,'Sent','16','13'); -INSERT INTO "CampaignMember" VALUES(42,'Sent','17','1'); -INSERT INTO "CampaignMember" VALUES(43,'Sent','17','10'); -INSERT INTO "CampaignMember" VALUES(44,'Sent','17','11'); -INSERT INTO "CampaignMember" VALUES(45,'Sent','17','12'); -INSERT INTO "CampaignMember" VALUES(46,'Sent','17','13'); -INSERT INTO "CampaignMember" VALUES(47,'Sent','17','3'); -INSERT INTO "CampaignMember" VALUES(48,'Sent','17','4'); -INSERT INTO "CampaignMember" VALUES(49,'Sent','18','13'); -INSERT INTO "CampaignMember" VALUES(50,'Sent','19','13'); -INSERT INTO "CampaignMember" VALUES(51,'Sent','20','13'); -INSERT INTO "CampaignMember" VALUES(52,'Sent','21','13'); -INSERT INTO "CampaignMember" VALUES(53,'Sent','26','13'); -INSERT INTO "CampaignMember" VALUES(54,'Sent','25','13'); -INSERT INTO "CampaignMember" VALUES(55,'Sent','27','13'); -INSERT INTO "CampaignMember" VALUES(56,'Sent','28','13'); -INSERT INTO "CampaignMember" VALUES(57,'Sent','29','13'); -INSERT INTO "CampaignMember" VALUES(58,'Sent','30','13'); -INSERT INTO "CampaignMember" VALUES(59,'Sent','31','13'); -INSERT INTO "CampaignMember" VALUES(60,'Sent','31','2'); -INSERT INTO "CampaignMember" VALUES(61,'Sent','32','13'); -INSERT INTO "CampaignMember" VALUES(62,'Sent','33','13'); -INSERT INTO "CampaignMember" VALUES(63,'Sent','34','13'); -INSERT INTO "CampaignMember" VALUES(64,'Sent','35','13'); -INSERT INTO "CampaignMember" VALUES(65,'Sent','36','13'); -INSERT INTO "CampaignMember" VALUES(66,'Sent','37','13'); -INSERT INTO "CampaignMember" VALUES(67,'Sent','38','13'); -INSERT INTO "CampaignMember" VALUES(68,'Sent','39','13'); -INSERT INTO "CampaignMember" VALUES(69,'Sent','40','13'); -INSERT INTO "CampaignMember" VALUES(70,'Sent','41','13'); -INSERT INTO "CampaignMember" VALUES(71,'Sent','42','13'); -INSERT INTO "CampaignMember" VALUES(72,'Sent','42','2'); -INSERT INTO "CampaignMember" VALUES(73,'Sent','43','13'); -INSERT INTO "CampaignMember" VALUES(74,'Sent','44','13'); -INSERT INTO "CampaignMember" VALUES(75,'Sent','45','13'); -INSERT INTO "CampaignMember" VALUES(76,'Sent','46','13'); -INSERT INTO "CampaignMember" VALUES(77,'Sent','47','13'); -INSERT INTO "CampaignMember" VALUES(78,'Sent','57','13'); -INSERT INTO "CampaignMember" VALUES(79,'Sent','48','13'); -INSERT INTO "CampaignMember" VALUES(80,'Sent','22','13'); -INSERT INTO "CampaignMember" VALUES(81,'Sent','23','13'); -INSERT INTO "CampaignMember" VALUES(82,'Sent','24','13'); -INSERT INTO "CampaignMember" VALUES(83,'Sent','49','13'); -INSERT INTO "CampaignMember" VALUES(84,'Sent','50','13'); -INSERT INTO "CampaignMember" VALUES(85,'Sent','51','13'); -INSERT INTO "CampaignMember" VALUES(86,'Sent','52','13'); -INSERT INTO "CampaignMember" VALUES(87,'Sent','53','1'); -INSERT INTO "CampaignMember" VALUES(88,'Sent','53','10'); -INSERT INTO "CampaignMember" VALUES(89,'Sent','53','11'); -INSERT INTO "CampaignMember" VALUES(90,'Sent','53','12'); -INSERT INTO "CampaignMember" VALUES(91,'Sent','53','13'); -INSERT INTO "CampaignMember" VALUES(92,'Sent','17','5'); -INSERT INTO "CampaignMember" VALUES(93,'Sent','17','6'); -INSERT INTO "CampaignMember" VALUES(94,'Sent','17','7'); -INSERT INTO "CampaignMember" VALUES(95,'Sent','17','8'); -INSERT INTO "CampaignMember" VALUES(96,'Sent','17','9'); -INSERT INTO "CampaignMember" VALUES(97,'Sent','53','8'); -INSERT INTO "CampaignMember" VALUES(98,'Sent','53','9'); -INSERT INTO "CampaignMember" VALUES(99,'Sent','54','13'); -INSERT INTO "CampaignMember" VALUES(100,'Sent','66','13'); -INSERT INTO "CampaignMember" VALUES(101,'Sent','55','13'); -INSERT INTO "CampaignMember" VALUES(102,'Sent','56','13'); -INSERT INTO "CampaignMember" VALUES(103,'Sent','96','13'); -INSERT INTO "CampaignMember" VALUES(104,'Sent','97','13'); -INSERT INTO "CampaignMember" VALUES(105,'Sent','58','13'); -INSERT INTO "CampaignMember" VALUES(106,'Sent','59','13'); -INSERT INTO "CampaignMember" VALUES(107,'Sent','60','13'); -INSERT INTO "CampaignMember" VALUES(108,'Sent','61','13'); -INSERT INTO "CampaignMember" VALUES(109,'Sent','62','13'); -INSERT INTO "CampaignMember" VALUES(110,'Sent','67','13'); -INSERT INTO "CampaignMember" VALUES(111,'Sent','77','13'); -INSERT INTO "CampaignMember" VALUES(112,'Sent','68','13'); -INSERT INTO "CampaignMember" VALUES(113,'Sent','69','13'); -INSERT INTO "CampaignMember" VALUES(114,'Sent','69','2'); -INSERT INTO "CampaignMember" VALUES(115,'Sent','70','13'); -INSERT INTO "CampaignMember" VALUES(116,'Sent','71','13'); -INSERT INTO "CampaignMember" VALUES(117,'Sent','72','13'); -INSERT INTO "CampaignMember" VALUES(118,'Sent','73','13'); -INSERT INTO "CampaignMember" VALUES(119,'Sent','74','1'); -INSERT INTO "CampaignMember" VALUES(120,'Sent','74','10'); -INSERT INTO "CampaignMember" VALUES(121,'Sent','74','11'); -INSERT INTO "CampaignMember" VALUES(122,'Sent','74','12'); -INSERT INTO "CampaignMember" VALUES(123,'Sent','74','13'); -INSERT INTO "CampaignMember" VALUES(124,'Sent','74','2'); -INSERT INTO "CampaignMember" VALUES(125,'Sent','74','3'); -INSERT INTO "CampaignMember" VALUES(126,'Sent','74','4'); -INSERT INTO "CampaignMember" VALUES(127,'Sent','74','5'); -INSERT INTO "CampaignMember" VALUES(128,'Sent','74','6'); -INSERT INTO "CampaignMember" VALUES(129,'Sent','74','7'); -INSERT INTO "CampaignMember" VALUES(130,'Sent','74','8'); -INSERT INTO "CampaignMember" VALUES(131,'Sent','74','9'); -INSERT INTO "CampaignMember" VALUES(132,'Sent','75','13'); -INSERT INTO "CampaignMember" VALUES(133,'Sent','76','13'); -INSERT INTO "CampaignMember" VALUES(134,'Sent','78','13'); -INSERT INTO "CampaignMember" VALUES(135,'Sent','88','13'); -INSERT INTO "CampaignMember" VALUES(136,'Sent','79','13'); -INSERT INTO "CampaignMember" VALUES(137,'Sent','80','13'); -INSERT INTO "CampaignMember" VALUES(138,'Sent','80','2'); -INSERT INTO "CampaignMember" VALUES(139,'Sent','81','13'); -INSERT INTO "CampaignMember" VALUES(140,'Sent','53','3'); -INSERT INTO "CampaignMember" VALUES(141,'Sent','53','4'); -INSERT INTO "CampaignMember" VALUES(142,'Sent','53','5'); -INSERT INTO "CampaignMember" VALUES(143,'Sent','53','6'); -INSERT INTO "CampaignMember" VALUES(144,'Sent','53','7'); -INSERT INTO "CampaignMember" VALUES(145,'Sent','86','13'); -INSERT INTO "CampaignMember" VALUES(146,'Sent','87','13'); -INSERT INTO "CampaignMember" VALUES(147,'Sent','90','13'); -INSERT INTO "CampaignMember" VALUES(148,'Sent','90','2'); -INSERT INTO "CampaignMember" VALUES(149,'Sent','89','13'); -INSERT INTO "CampaignMember" VALUES(150,'Sent','91','13'); -INSERT INTO "CampaignMember" VALUES(151,'Sent','63','13'); -INSERT INTO "CampaignMember" VALUES(152,'Sent','64','13'); -INSERT INTO "CampaignMember" VALUES(153,'Sent','65','13'); -INSERT INTO "CampaignMember" VALUES(154,'Sent','92','13'); -INSERT INTO "CampaignMember" VALUES(155,'Sent','93','13'); -INSERT INTO "CampaignMember" VALUES(156,'Sent','93','2'); -INSERT INTO "CampaignMember" VALUES(157,'Sent','94','13'); -INSERT INTO "CampaignMember" VALUES(158,'Sent','95','13'); -INSERT INTO "CampaignMember" VALUES(159,'Sent','98','13'); -INSERT INTO "CampaignMember" VALUES(160,'Sent','99','13'); -INSERT INTO "CampaignMember" VALUES(161,'Sent','82','13'); -INSERT INTO "CampaignMember" VALUES(162,'Sent','82','2'); -INSERT INTO "CampaignMember" VALUES(163,'Sent','83','13'); -INSERT INTO "CampaignMember" VALUES(164,'Sent','84','13'); -INSERT INTO "CampaignMember" VALUES(165,'Sent','85','13'); +INSERT INTO "CampaignMember" VALUES(1,'Sent','61','13'); +INSERT INTO "CampaignMember" VALUES(2,'Sent','62','13'); +INSERT INTO "CampaignMember" VALUES(3,'Sent','63','13'); +INSERT INTO "CampaignMember" VALUES(4,'Sent','64','13'); +INSERT INTO "CampaignMember" VALUES(5,'Sent','68','13'); +INSERT INTO "CampaignMember" VALUES(6,'Sent','65','13'); +INSERT INTO "CampaignMember" VALUES(7,'Sent','79','13'); +INSERT INTO "CampaignMember" VALUES(8,'Sent','80','13'); +INSERT INTO "CampaignMember" VALUES(9,'Sent','80','2'); +INSERT INTO "CampaignMember" VALUES(10,'Sent','16','13'); +INSERT INTO "CampaignMember" VALUES(11,'Sent','81','13'); +INSERT INTO "CampaignMember" VALUES(12,'Sent','82','13'); +INSERT INTO "CampaignMember" VALUES(13,'Sent','83','13'); +INSERT INTO "CampaignMember" VALUES(14,'Sent','84','13'); +INSERT INTO "CampaignMember" VALUES(15,'Sent','85','1'); +INSERT INTO "CampaignMember" VALUES(16,'Sent','85','10'); +INSERT INTO "CampaignMember" VALUES(17,'Sent','85','11'); +INSERT INTO "CampaignMember" VALUES(18,'Sent','85','12'); +INSERT INTO "CampaignMember" VALUES(19,'Sent','85','13'); +INSERT INTO "CampaignMember" VALUES(20,'Sent','85','2'); +INSERT INTO "CampaignMember" VALUES(21,'Sent','85','3'); +INSERT INTO "CampaignMember" VALUES(22,'Sent','85','4'); +INSERT INTO "CampaignMember" VALUES(23,'Sent','85','5'); +INSERT INTO "CampaignMember" VALUES(24,'Sent','85','6'); +INSERT INTO "CampaignMember" VALUES(25,'Sent','85','7'); +INSERT INTO "CampaignMember" VALUES(26,'Sent','85','8'); +INSERT INTO "CampaignMember" VALUES(27,'Sent','85','9'); +INSERT INTO "CampaignMember" VALUES(28,'Sent','86','13'); +INSERT INTO "CampaignMember" VALUES(29,'Sent','87','13'); +INSERT INTO "CampaignMember" VALUES(30,'Sent','88','13'); +INSERT INTO "CampaignMember" VALUES(31,'Sent','89','13'); +INSERT INTO "CampaignMember" VALUES(32,'Sent','90','13'); +INSERT INTO "CampaignMember" VALUES(33,'Sent','17','1'); +INSERT INTO "CampaignMember" VALUES(34,'Sent','17','10'); +INSERT INTO "CampaignMember" VALUES(35,'Sent','17','11'); +INSERT INTO "CampaignMember" VALUES(36,'Sent','17','12'); +INSERT INTO "CampaignMember" VALUES(37,'Sent','17','13'); +INSERT INTO "CampaignMember" VALUES(38,'Sent','17','3'); +INSERT INTO "CampaignMember" VALUES(39,'Sent','17','4'); +INSERT INTO "CampaignMember" VALUES(40,'Sent','17','5'); +INSERT INTO "CampaignMember" VALUES(41,'Sent','17','6'); +INSERT INTO "CampaignMember" VALUES(42,'Sent','17','7'); +INSERT INTO "CampaignMember" VALUES(43,'Sent','17','8'); +INSERT INTO "CampaignMember" VALUES(44,'Sent','17','9'); +INSERT INTO "CampaignMember" VALUES(45,'Sent','91','13'); +INSERT INTO "CampaignMember" VALUES(46,'Sent','91','2'); +INSERT INTO "CampaignMember" VALUES(47,'Sent','92','13'); +INSERT INTO "CampaignMember" VALUES(48,'Sent','93','13'); +INSERT INTO "CampaignMember" VALUES(49,'Sent','93','2'); +INSERT INTO "CampaignMember" VALUES(50,'Sent','94','13'); +INSERT INTO "CampaignMember" VALUES(51,'Sent','95','13'); +INSERT INTO "CampaignMember" VALUES(52,'Sent','96','13'); +INSERT INTO "CampaignMember" VALUES(53,'Sent','97','13'); +INSERT INTO "CampaignMember" VALUES(54,'Sent','98','13'); +INSERT INTO "CampaignMember" VALUES(55,'Sent','99','13'); +INSERT INTO "CampaignMember" VALUES(56,'Sent','100','13'); +INSERT INTO "CampaignMember" VALUES(57,'Sent','18','13'); +INSERT INTO "CampaignMember" VALUES(58,'Sent','18','2'); +INSERT INTO "CampaignMember" VALUES(59,'Sent','101','13'); +INSERT INTO "CampaignMember" VALUES(60,'Sent','101','2'); +INSERT INTO "CampaignMember" VALUES(61,'Sent','102','13'); +INSERT INTO "CampaignMember" VALUES(62,'Sent','2','13'); +INSERT INTO "CampaignMember" VALUES(63,'Sent','3','13'); +INSERT INTO "CampaignMember" VALUES(64,'Sent','3','2'); +INSERT INTO "CampaignMember" VALUES(65,'Sent','4','13'); +INSERT INTO "CampaignMember" VALUES(66,'Sent','5','13'); +INSERT INTO "CampaignMember" VALUES(67,'Sent','54','13'); +INSERT INTO "CampaignMember" VALUES(68,'Sent','55','13'); +INSERT INTO "CampaignMember" VALUES(69,'Sent','6','13'); +INSERT INTO "CampaignMember" VALUES(70,'Sent','7','13'); +INSERT INTO "CampaignMember" VALUES(71,'Sent','10','13'); +INSERT INTO "CampaignMember" VALUES(72,'Sent','19','13'); +INSERT INTO "CampaignMember" VALUES(73,'Sent','19','2'); +INSERT INTO "CampaignMember" VALUES(74,'Sent','8','13'); +INSERT INTO "CampaignMember" VALUES(75,'Sent','20','13'); +INSERT INTO "CampaignMember" VALUES(76,'Sent','21','13'); +INSERT INTO "CampaignMember" VALUES(77,'Sent','22','13'); +INSERT INTO "CampaignMember" VALUES(78,'Sent','23','13'); +INSERT INTO "CampaignMember" VALUES(79,'Sent','24','13'); +INSERT INTO "CampaignMember" VALUES(80,'Sent','25','13'); +INSERT INTO "CampaignMember" VALUES(81,'Sent','26','1'); +INSERT INTO "CampaignMember" VALUES(82,'Sent','26','10'); +INSERT INTO "CampaignMember" VALUES(83,'Sent','26','11'); +INSERT INTO "CampaignMember" VALUES(84,'Sent','26','12'); +INSERT INTO "CampaignMember" VALUES(85,'Sent','26','13'); +INSERT INTO "CampaignMember" VALUES(86,'Sent','26','3'); +INSERT INTO "CampaignMember" VALUES(87,'Sent','26','4'); +INSERT INTO "CampaignMember" VALUES(88,'Sent','26','5'); +INSERT INTO "CampaignMember" VALUES(89,'Sent','26','6'); +INSERT INTO "CampaignMember" VALUES(90,'Sent','26','7'); +INSERT INTO "CampaignMember" VALUES(91,'Sent','26','8'); +INSERT INTO "CampaignMember" VALUES(92,'Sent','26','9'); +INSERT INTO "CampaignMember" VALUES(93,'Sent','27','13'); +INSERT INTO "CampaignMember" VALUES(94,'Sent','28','13'); +INSERT INTO "CampaignMember" VALUES(95,'Sent','11','13'); +INSERT INTO "CampaignMember" VALUES(96,'Sent','29','13'); +INSERT INTO "CampaignMember" VALUES(97,'Sent','30','13'); +INSERT INTO "CampaignMember" VALUES(98,'Sent','31','13'); +INSERT INTO "CampaignMember" VALUES(99,'Sent','32','13'); +INSERT INTO "CampaignMember" VALUES(100,'Sent','33','13'); +INSERT INTO "CampaignMember" VALUES(101,'Sent','34','13'); +INSERT INTO "CampaignMember" VALUES(102,'Sent','35','13'); +INSERT INTO "CampaignMember" VALUES(103,'Sent','36','13'); +INSERT INTO "CampaignMember" VALUES(104,'Sent','37','13'); +INSERT INTO "CampaignMember" VALUES(105,'Sent','38','13'); +INSERT INTO "CampaignMember" VALUES(106,'Sent','12','13'); +INSERT INTO "CampaignMember" VALUES(107,'Sent','39','13'); +INSERT INTO "CampaignMember" VALUES(108,'Sent','40','13'); +INSERT INTO "CampaignMember" VALUES(109,'Sent','40','2'); +INSERT INTO "CampaignMember" VALUES(110,'Sent','41','13'); +INSERT INTO "CampaignMember" VALUES(111,'Sent','42','13'); +INSERT INTO "CampaignMember" VALUES(112,'Sent','43','13'); +INSERT INTO "CampaignMember" VALUES(113,'Sent','44','13'); +INSERT INTO "CampaignMember" VALUES(114,'Sent','45','13'); +INSERT INTO "CampaignMember" VALUES(115,'Sent','46','13'); +INSERT INTO "CampaignMember" VALUES(116,'Sent','47','13'); +INSERT INTO "CampaignMember" VALUES(117,'Sent','48','13'); +INSERT INTO "CampaignMember" VALUES(118,'Sent','13','13'); +INSERT INTO "CampaignMember" VALUES(119,'Sent','66','13'); +INSERT INTO "CampaignMember" VALUES(120,'Sent','67','13'); +INSERT INTO "CampaignMember" VALUES(121,'Sent','69','13'); +INSERT INTO "CampaignMember" VALUES(122,'Sent','69','2'); +INSERT INTO "CampaignMember" VALUES(123,'Sent','70','13'); +INSERT INTO "CampaignMember" VALUES(124,'Sent','71','13'); +INSERT INTO "CampaignMember" VALUES(125,'Sent','72','13'); +INSERT INTO "CampaignMember" VALUES(126,'Sent','73','13'); +INSERT INTO "CampaignMember" VALUES(127,'Sent','74','13'); +INSERT INTO "CampaignMember" VALUES(128,'Sent','75','13'); +INSERT INTO "CampaignMember" VALUES(129,'Sent','76','13'); +INSERT INTO "CampaignMember" VALUES(130,'Sent','14','13'); +INSERT INTO "CampaignMember" VALUES(131,'Sent','77','13'); +INSERT INTO "CampaignMember" VALUES(132,'Sent','78','13'); +INSERT INTO "CampaignMember" VALUES(133,'Sent','49','13'); +INSERT INTO "CampaignMember" VALUES(134,'Sent','50','1'); +INSERT INTO "CampaignMember" VALUES(135,'Sent','50','10'); +INSERT INTO "CampaignMember" VALUES(136,'Sent','50','11'); +INSERT INTO "CampaignMember" VALUES(137,'Sent','50','12'); +INSERT INTO "CampaignMember" VALUES(138,'Sent','50','13'); +INSERT INTO "CampaignMember" VALUES(139,'Sent','50','3'); +INSERT INTO "CampaignMember" VALUES(140,'Sent','50','4'); +INSERT INTO "CampaignMember" VALUES(141,'Sent','50','5'); +INSERT INTO "CampaignMember" VALUES(142,'Sent','50','6'); +INSERT INTO "CampaignMember" VALUES(143,'Sent','50','7'); +INSERT INTO "CampaignMember" VALUES(144,'Sent','50','8'); +INSERT INTO "CampaignMember" VALUES(145,'Sent','50','9'); +INSERT INTO "CampaignMember" VALUES(146,'Sent','51','13'); +INSERT INTO "CampaignMember" VALUES(147,'Sent','52','13'); +INSERT INTO "CampaignMember" VALUES(148,'Sent','53','13'); +INSERT INTO "CampaignMember" VALUES(149,'Sent','57','13'); +INSERT INTO "CampaignMember" VALUES(150,'Sent','56','13'); +INSERT INTO "CampaignMember" VALUES(151,'Sent','58','13'); +INSERT INTO "CampaignMember" VALUES(152,'Sent','15','1'); +INSERT INTO "CampaignMember" VALUES(153,'Sent','15','10'); +INSERT INTO "CampaignMember" VALUES(154,'Sent','15','11'); +INSERT INTO "CampaignMember" VALUES(155,'Sent','15','12'); +INSERT INTO "CampaignMember" VALUES(156,'Sent','15','13'); +INSERT INTO "CampaignMember" VALUES(157,'Sent','15','3'); +INSERT INTO "CampaignMember" VALUES(158,'Sent','15','4'); +INSERT INTO "CampaignMember" VALUES(159,'Sent','15','5'); +INSERT INTO "CampaignMember" VALUES(160,'Sent','15','6'); +INSERT INTO "CampaignMember" VALUES(161,'Sent','15','7'); +INSERT INTO "CampaignMember" VALUES(162,'Sent','15','8'); +INSERT INTO "CampaignMember" VALUES(163,'Sent','15','9'); +INSERT INTO "CampaignMember" VALUES(164,'Sent','59','13'); +INSERT INTO "CampaignMember" VALUES(165,'Sent','60','13'); CREATE TABLE "Contact" ( - id INTEGER NOT NULL, - "Salutation" VARCHAR(255), - "FirstName" VARCHAR(255), - "LastName" VARCHAR(255), - "Email" VARCHAR(255), - "Phone" VARCHAR(255), - "Title" VARCHAR(255), - account_id VARCHAR(255), - primary_affiliation__c VARCHAR(255), + id INTEGER NOT NULL, + "Salutation" VARCHAR(255), + "FirstName" VARCHAR(255), + "LastName" VARCHAR(255), + "Email" VARCHAR(255), + "Phone" VARCHAR(255), + "Title" VARCHAR(255), + "AccountId" VARCHAR(255), + "Primary_Affiliation__c" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "Contact" VALUES(1,'Ms','Alice','Patterson','','1-(266)195-4611','Financial Analyst','1','98'); -INSERT INTO "Contact" VALUES(2,'Honorable','Kevin','Gilbert','','','Registered Nurse','10','108'); -INSERT INTO "Contact" VALUES(3,'Ms','Jean','Gardner','jgardner8@linkedin.com','2-(361)757-2109','Business Systems Development Analyst','11','146'); -INSERT INTO "Contact" VALUES(4,'Honorable','Mary','Ross','','','Recruiter','12','141'); -INSERT INTO "Contact" VALUES(5,'Honorable','Lois','Hernandez','','','Research Nurse','126',''); -INSERT INTO "Contact" VALUES(6,'Honorable','Janice','Black','jblack1s@goodreads.com','9-(505)154-9220','Operator','13','128'); -INSERT INTO "Contact" VALUES(7,'Ms','Helen','Frazier','hfrazier5@time.com','','Paralegal','14','115'); -INSERT INTO "Contact" VALUES(8,'','Phyllis','Nichols','','','','15','116'); -INSERT INTO "Contact" VALUES(9,'Mr','Lori','Murray','','','','15','121'); -INSERT INTO "Contact" VALUES(10,'','Jose','Flores','jfloresf@usgs.gov','','Administrative Officer','16','93'); -INSERT INTO "Contact" VALUES(11,'Dr','Pamela','Long','plong22@salon.com','0-(126)286-3604','','17','129'); -INSERT INTO "Contact" VALUES(12,'','Joyce','Fowler','jfowler1z@bbb.org','9-(390)341-9013','','18','114'); -INSERT INTO "Contact" VALUES(13,'Mr','Mary','Pierce','mpierce19@narod.ru','2-(217)899-8081','Human Resources Assistant I','19','97'); -INSERT INTO "Contact" VALUES(14,'Rev','Billy','Bradley','','5-(546)327-8049','Assistant Manager','2','148'); -INSERT INTO "Contact" VALUES(15,'Dr','Rebecca','Johnston','','','Chief Design Engineer','20','88'); -INSERT INTO "Contact" VALUES(16,'Rev','Ashley','Gomez','agomez6@guardian.co.uk','1-(509)186-1014','','21','94'); -INSERT INTO "Contact" VALUES(17,'Mrs','Debra','Knight','','','','22','106'); -INSERT INTO "Contact" VALUES(18,'Honorable','Ruth','Sanders','','','Administrative Assistant I','23','118'); -INSERT INTO "Contact" VALUES(19,'Ms','Lillian','Flores','','','','24','119'); -INSERT INTO "Contact" VALUES(20,'Honorable','Andrew','Fernandez','afernandez2n@comcast.net','6-(663)655-4163','','25','127'); -INSERT INTO "Contact" VALUES(21,'','Robin','Hicks','','9-(233)213-5568','','26','107'); -INSERT INTO "Contact" VALUES(22,'Mr','Marilyn','Chapman','','1-(487)342-6350','Programmer Analyst IV','27','123'); -INSERT INTO "Contact" VALUES(23,'Mr','Pamela','Mills','pmills1q@reverbnation.com','9-(851)323-6414','Senior Editor','28','150'); -INSERT INTO "Contact" VALUES(24,'Mr','Frances','Nelson','','2-(685)755-1269','Sales Representative','29','154'); -INSERT INTO "Contact" VALUES(25,'Mrs','Jerry','Harper','','','Account Representative II','3','111'); -INSERT INTO "Contact" VALUES(26,'Dr','Jessica','Grant','','1-(952)559-8782','','30','152'); -INSERT INTO "Contact" VALUES(27,'Honorable','Diane','Ward','','1-(271)207-4341','Payment Adjustment Coordinator','31','163'); -INSERT INTO "Contact" VALUES(28,'','Cheryl','James','','5-(045)740-1346','Information Systems Manager','32','147'); -INSERT INTO "Contact" VALUES(29,'Mr','Matthew','Wallace','','7-(656)377-5745','Biostatistician II','33','89'); -INSERT INTO "Contact" VALUES(30,'Mr','Ann','Chapman','','','Food Chemist','34','101'); -INSERT INTO "Contact" VALUES(31,'Honorable','Judith','Fuller','','9-(162)417-5819','','35','117'); -INSERT INTO "Contact" VALUES(32,'Mr','Catherine','Jones','','4-(912)166-3558','','36','149'); -INSERT INTO "Contact" VALUES(33,'','Jeremy','Banks','jbankso@edublogs.org','3-(121)796-1312','','37','141'); -INSERT INTO "Contact" VALUES(34,'Rev','Randy','Perkins','rperkins23@furl.net','5-(314)277-1500','','38','102'); -INSERT INTO "Contact" VALUES(35,'Ms','Joshua','Morales','jmorales2l@nbcnews.com','','Executive Secretary','39','120'); -INSERT INTO "Contact" VALUES(36,'Ms','Robert','Medina','rmedina2k@amazon.co.uk','2-(601)920-7404','Nurse Practicioner','39','122'); -INSERT INTO "Contact" VALUES(37,'Mrs','Jeffrey','Thompson','','1-(503)051-4278','','4','96'); -INSERT INTO "Contact" VALUES(38,'Honorable','Arthur','Burton','aburtonq@hostgator.com','4-(245)684-4705','','40','146'); -INSERT INTO "Contact" VALUES(39,'Ms','Evelyn','Hayes','','','','41','155'); -INSERT INTO "Contact" VALUES(40,'Honorable','Eugene','Hunt','ehuntz@toplist.cz','2-(568)222-2673','','58','153'); -INSERT INTO "Contact" VALUES(41,'Ms','Billy','Garrett','','0-(853)323-8778','Administrative Officer','59','98'); -INSERT INTO "Contact" VALUES(42,'Dr','Raymond','Alvarez','','','Research Associate','60','166'); -INSERT INTO "Contact" VALUES(43,'Dr','Stephanie','Bailey','','2-(434)350-2344','Programmer IV','60','166'); -INSERT INTO "Contact" VALUES(44,'','Samuel','Harrison','','4-(604)044-0141','Software Consultant','60','166'); -INSERT INTO "Contact" VALUES(45,'Honorable','David','Johnson','djohnson2b@scribd.com','2-(993)514-8842','','60','166'); -INSERT INTO "Contact" VALUES(46,'Mr','Angela','Kelly','akelly2i@miitbeian.gov.cn','','','60','166'); -INSERT INTO "Contact" VALUES(47,'Mrs','Chris','Medina','cmedina2a@networkadvertising.org','2-(128)916-8821','Quality Engineer','60','166'); -INSERT INTO "Contact" VALUES(48,'Ms','Barbara','Russell','','2-(745)944-4149','Registered Nurse','60','166'); -INSERT INTO "Contact" VALUES(49,'Mrs','Ernest','Smith','','','','60','166'); -INSERT INTO "Contact" VALUES(50,'','Albert','Stone','astone2g@dot.gov','','','60','166'); -INSERT INTO "Contact" VALUES(51,'Honorable','Gary','Taylor','','','','60','166'); -INSERT INTO "Contact" VALUES(52,'Mrs','Ruby','Oliver','','1-(926)682-2565','Staff Accountant I','42','158'); -INSERT INTO "Contact" VALUES(53,'','Phyllis','Alvarez','','4-(935)637-3713','','43','133'); -INSERT INTO "Contact" VALUES(54,'Mrs','Dorothy','Watson','dwatson13@virginia.edu','3-(843)452-8728','Senior Quality Engineer','44','130'); -INSERT INTO "Contact" VALUES(55,'Rev','Shawn','Wallace','swallace7@ftc.gov','','','45','98'); -INSERT INTO "Contact" VALUES(56,'Dr','Donna','Riley','drileyu@ed.gov','7-(161)939-6638','','46','112'); -INSERT INTO "Contact" VALUES(57,'','Kathryn','Williams','','','','5','138'); -INSERT INTO "Contact" VALUES(58,'Dr','Anthony','Fisher','afisher9@yale.edu','4-(323)334-2796','Human Resources Manager','49','139'); -INSERT INTO "Contact" VALUES(59,'Mr','Andrea','Torres','','8-(792)543-6916','','50','126'); -INSERT INTO "Contact" VALUES(60,'Rev','Brenda','Jenkins','bjenkins1d@sfgate.com','','','51','92'); -INSERT INTO "Contact" VALUES(61,'Honorable','Jason','Anderson','','','Social Worker','52','132'); -INSERT INTO "Contact" VALUES(62,'Mrs','Phillip','Perry','','','','53','99'); -INSERT INTO "Contact" VALUES(63,'Ms','Julia','Morrison','','','','54','140'); -INSERT INTO "Contact" VALUES(64,'Rev','Ryan','Ward','','','','55','145'); -INSERT INTO "Contact" VALUES(65,'Dr','Craig','Washington','cwashington1i@miitbeian.gov.cn','3-(227)090-2681','','56','159'); -INSERT INTO "Contact" VALUES(66,'Mr','Carl','Harvey','','5-(233)156-4659','','6','105'); -INSERT INTO "Contact" VALUES(67,'Mrs','Timothy','Franklin','tfranklin1j@seesaa.net','','','57','161'); -INSERT INTO "Contact" VALUES(68,'Dr','Joe','Bailey','','1-(495)461-4170','','61','113'); -INSERT INTO "Contact" VALUES(69,'Rev','Russell','Thomas','rthomas2m@e-recht24.de','','Web Developer IV','62','109'); -INSERT INTO "Contact" VALUES(70,'Ms','Joseph','Lane','','8-(234)380-1727','Paralegal','63','104'); -INSERT INTO "Contact" VALUES(71,'','George','Perry','','','','64','110'); -INSERT INTO "Contact" VALUES(72,'Honorable','Sara','Anderson','','2-(803)151-8520','','65','90'); -INSERT INTO "Contact" VALUES(73,'Ms','Martha','Hansen','mhanseny@t-online.de','','Software Engineer I','66','137'); -INSERT INTO "Contact" VALUES(74,'Ms','Mildred','Willis','','3-(474)705-1693','','67','95'); -INSERT INTO "Contact" VALUES(75,'Ms','Roy','Holmes','rholmes1r@digg.com','5-(595)114-1672','Legal Assistant','68','162'); -INSERT INTO "Contact" VALUES(76,'','Mildred','Kelly','','7-(629)659-4669','Occupational Therapist','69','162'); -INSERT INTO "Contact" VALUES(77,'Mr','Kathryn','Patterson','kpattersone@vkontakte.ru','5-(310)993-7204','','7','136'); -INSERT INTO "Contact" VALUES(78,'Rev','Ryan','Reyes','','','','70','144'); -INSERT INTO "Contact" VALUES(79,'Mrs','Howard','Burke','hburke1e@yandex.ru','','Chief Design Engineer','71','111'); -INSERT INTO "Contact" VALUES(80,'','Melissa','Webb','','0-(411)456-7376','Accountant I','72','126'); -INSERT INTO "Contact" VALUES(81,'Mrs','Kathy','Long','','8-(902)778-4158','','73','135'); -INSERT INTO "Contact" VALUES(82,'Rev','Walter','Powell','','4-(668)963-5868','','74','154'); -INSERT INTO "Contact" VALUES(83,'','Russell','Powell','rpowell1h@redcross.org','','Clinical Specialist','75','141'); -INSERT INTO "Contact" VALUES(84,'Mr','Harry','Jenkins','hjenkinsh@typepad.com','5-(956)554-4923','Automation Specialist IV','76','157'); -INSERT INTO "Contact" VALUES(85,'Rev','Rose','Gonzales','','','VP Product Management','77','134'); -INSERT INTO "Contact" VALUES(86,'','Matthew','Richards','','','','78','160'); -INSERT INTO "Contact" VALUES(87,'Mrs','Gerald','Sullivan','','9-(806)552-8308','','79','144'); -INSERT INTO "Contact" VALUES(88,'','George','Hawkins','ghawkins2q@typepad.com','6-(759)496-9987','Desktop Support Technician','8','124'); -INSERT INTO "Contact" VALUES(89,'Mr','Gloria','Hart','','7-(709)961-7856','','8','156'); -INSERT INTO "Contact" VALUES(90,'Ms','Nicole','Chapman','nchapman0@europa.eu','3-(472)603-5084','','80','91'); -INSERT INTO "Contact" VALUES(91,'','Kevin','Hudson','','','','81','151'); -INSERT INTO "Contact" VALUES(92,'Dr','Judith','Warren','jwarren26@linkedin.com','','Senior Editor','82','100'); -INSERT INTO "Contact" VALUES(93,'Mr','Nicholas','King','','1-(068)662-5765','','83','125'); -INSERT INTO "Contact" VALUES(94,'Honorable','Julie','Riley','','','','84','143'); -INSERT INTO "Contact" VALUES(95,'Dr','Rebecca','Porter','','4-(706)074-7847','Assistant Manager','85','165'); -INSERT INTO "Contact" VALUES(96,'Honorable','Jacqueline','Rivera','jrivera1n@wikispaces.com','','Design Engineer','47','164'); -INSERT INTO "Contact" VALUES(97,'Mrs','Eugene','Stephens','estephensx@bizjournals.com','','Administrative Officer','48','128'); -INSERT INTO "Contact" VALUES(98,'Dr','Christopher','Austin','','','','86','131'); -INSERT INTO "Contact" VALUES(99,'','Maria','Carr','','2-(078)409-0982','','87','142'); -INSERT INTO "Contact" VALUES(100,'Ms','Gloria','Howell','','','','9','103'); +INSERT INTO "Contact" VALUES(1,'','Josh','Patterson','','','','60',''); +INSERT INTO "Contact" VALUES(2,'Dr','Judith','Warren','jwarren26@linkedin.com','','Senior Editor','141','159'); +INSERT INTO "Contact" VALUES(3,'Mr','Nicholas','King','','1-(068)662-5765','','142','14'); +INSERT INTO "Contact" VALUES(4,'Honorable','Julie','Riley','','','','143','36'); +INSERT INTO "Contact" VALUES(5,'Dr','Rebecca','Porter','','4-(706)074-7847','Assistant Manager','144','58'); +INSERT INTO "Contact" VALUES(6,'Dr','Christopher','Austin','','','','145','24'); +INSERT INTO "Contact" VALUES(7,'','Maria','Carr','','2-(078)409-0982','','146','35'); +INSERT INTO "Contact" VALUES(8,'Ms','Gloria','Howell','','','','68','162'); +INSERT INTO "Contact" VALUES(9,'','Juliet','Chapman','','','','93','93'); +INSERT INTO "Contact" VALUES(10,'Ms','Alice','Patterson','','1-(266)195-4611','Financial Analyst','60','157'); +INSERT INTO "Contact" VALUES(11,'Honorable','Kevin','Gilbert','','','Registered Nurse','69','167'); +INSERT INTO "Contact" VALUES(12,'Ms','Jean','Gardner','jgardner8@linkedin.com','2-(361)757-2109','Business Systems Development Analyst','70','39'); +INSERT INTO "Contact" VALUES(13,'Honorable','Mary','Ross','','','Recruiter','71','34'); +INSERT INTO "Contact" VALUES(14,'Honorable','Lois','Hernandez','','','Research Nurse','15',''); +INSERT INTO "Contact" VALUES(15,'Honorable','Janice','Black','jblack1s@goodreads.com','9-(505)154-9220','Operator','72','17'); +INSERT INTO "Contact" VALUES(16,'Ms','Helen','Frazier','hfrazier5@time.com','','Paralegal','73','4'); +INSERT INTO "Contact" VALUES(17,'','Phyllis','Nichols','','','','74','5'); +INSERT INTO "Contact" VALUES(18,'Mr','Lori','Murray','','','','74','10'); +INSERT INTO "Contact" VALUES(19,'','Jose','Flores','jfloresf@usgs.gov','','Administrative Officer','75','152'); +INSERT INTO "Contact" VALUES(20,'Dr','Pamela','Long','plong22@salon.com','0-(126)286-3604','','76','18'); +INSERT INTO "Contact" VALUES(21,'','Joyce','Fowler','jfowler1z@bbb.org','9-(390)341-9013','','77','3'); +INSERT INTO "Contact" VALUES(22,'Mr','Mary','Pierce','mpierce19@narod.ru','2-(217)899-8081','Human Resources Assistant I','78','156'); +INSERT INTO "Contact" VALUES(23,'Rev','Billy','Bradley','','5-(546)327-8049','Assistant Manager','61','41'); +INSERT INTO "Contact" VALUES(24,'Dr','Rebecca','Johnston','','','Chief Design Engineer','79','147'); +INSERT INTO "Contact" VALUES(25,'Rev','Ashley','Gomez','agomez6@guardian.co.uk','1-(509)186-1014','','80','153'); +INSERT INTO "Contact" VALUES(26,'Mrs','Debra','Knight','','','','81','165'); +INSERT INTO "Contact" VALUES(27,'Honorable','Ruth','Sanders','','','Administrative Assistant I','82','7'); +INSERT INTO "Contact" VALUES(28,'Ms','Lillian','Flores','','','','83','8'); +INSERT INTO "Contact" VALUES(29,'Honorable','Andrew','Fernandez','afernandez2n@comcast.net','6-(663)655-4163','','84','16'); +INSERT INTO "Contact" VALUES(30,'','Robin','Hicks','','9-(233)213-5568','','85','166'); +INSERT INTO "Contact" VALUES(31,'Mr','Marilyn','Chapman','','1-(487)342-6350','Programmer Analyst IV','86','12'); +INSERT INTO "Contact" VALUES(32,'Mr','Pamela','Mills','pmills1q@reverbnation.com','9-(851)323-6414','Senior Editor','87','43'); +INSERT INTO "Contact" VALUES(33,'Mr','Frances','Nelson','','2-(685)755-1269','Sales Representative','88','47'); +INSERT INTO "Contact" VALUES(34,'Mrs','Jerry','Harper','','','Account Representative II','62','22'); +INSERT INTO "Contact" VALUES(35,'Dr','Jessica','Grant','','1-(952)559-8782','','89','45'); +INSERT INTO "Contact" VALUES(36,'Honorable','Diane','Ward','','1-(271)207-4341','Payment Adjustment Coordinator','90','56'); +INSERT INTO "Contact" VALUES(37,'','Cheryl','James','','5-(045)740-1346','Information Systems Manager','91','40'); +INSERT INTO "Contact" VALUES(38,'Mr','Matthew','Wallace','','7-(656)377-5745','Biostatistician II','92','148'); +INSERT INTO "Contact" VALUES(39,'Mr','Ann','Chapman','','','Food Chemist','93','160'); +INSERT INTO "Contact" VALUES(40,'Honorable','Judith','Fuller','','9-(162)417-5819','','94','6'); +INSERT INTO "Contact" VALUES(41,'Mr','Catherine','Jones','','4-(912)166-3558','','95','42'); +INSERT INTO "Contact" VALUES(42,'','Jeremy','Banks','jbankso@edublogs.org','3-(121)796-1312','','96','34'); +INSERT INTO "Contact" VALUES(43,'Rev','Randy','Perkins','rperkins23@furl.net','5-(314)277-1500','','97','161'); +INSERT INTO "Contact" VALUES(44,'Ms','Joshua','Morales','jmorales2l@nbcnews.com','','Executive Secretary','98','9'); +INSERT INTO "Contact" VALUES(45,'Ms','Robert','Medina','rmedina2k@amazon.co.uk','2-(601)920-7404','Nurse Practicioner','98','11'); +INSERT INTO "Contact" VALUES(46,'Mrs','Jeffrey','Thompson','','1-(503)051-4278','','63','155'); +INSERT INTO "Contact" VALUES(47,'Honorable','Arthur','Burton','aburtonq@hostgator.com','4-(245)684-4705','','99','39'); +INSERT INTO "Contact" VALUES(48,'Ms','Evelyn','Hayes','','','','100','48'); +INSERT INTO "Contact" VALUES(49,'Mrs','Ruby','Oliver','','1-(926)682-2565','Staff Accountant I','101','51'); +INSERT INTO "Contact" VALUES(50,'','Phyllis','Alvarez','','4-(935)637-3713','','102','26'); +INSERT INTO "Contact" VALUES(51,'Mrs','Dorothy','Watson','dwatson13@virginia.edu','3-(843)452-8728','Senior Quality Engineer','103','19'); +INSERT INTO "Contact" VALUES(52,'Rev','Shawn','Wallace','swallace7@ftc.gov','','','104','157'); +INSERT INTO "Contact" VALUES(53,'Dr','Donna','Riley','drileyu@ed.gov','7-(161)939-6638','','105','23'); +INSERT INTO "Contact" VALUES(54,'Honorable','Jacqueline','Rivera','jrivera1n@wikispaces.com','','Design Engineer','106','57'); +INSERT INTO "Contact" VALUES(55,'Mrs','Eugene','Stephens','estephensx@bizjournals.com','','Administrative Officer','107','17'); +INSERT INTO "Contact" VALUES(56,'Dr','Anthony','Fisher','afisher9@yale.edu','4-(323)334-2796','Human Resources Manager','108','32'); +INSERT INTO "Contact" VALUES(57,'','Kathryn','Williams','','','','64','31'); +INSERT INTO "Contact" VALUES(58,'Mr','Andrea','Torres','','8-(792)543-6916','','109','15'); +INSERT INTO "Contact" VALUES(59,'Rev','Brenda','Jenkins','bjenkins1d@sfgate.com','','','110','151'); +INSERT INTO "Contact" VALUES(60,'Honorable','Jason','Anderson','','','Social Worker','111','25'); +INSERT INTO "Contact" VALUES(61,'Mrs','Phillip','Perry','','','','112','158'); +INSERT INTO "Contact" VALUES(62,'Ms','Julia','Morrison','','','','113','33'); +INSERT INTO "Contact" VALUES(63,'Rev','Ryan','Ward','','','','114','38'); +INSERT INTO "Contact" VALUES(64,'Dr','Craig','Washington','cwashington1i@miitbeian.gov.cn','3-(227)090-2681','','115','52'); +INSERT INTO "Contact" VALUES(65,'Mrs','Timothy','Franklin','tfranklin1j@seesaa.net','','','116','54'); +INSERT INTO "Contact" VALUES(66,'Honorable','Eugene','Hunt','ehuntz@toplist.cz','2-(568)222-2673','','117','46'); +INSERT INTO "Contact" VALUES(67,'Ms','Billy','Garrett','','0-(853)323-8778','Administrative Officer','118','157'); +INSERT INTO "Contact" VALUES(68,'Mr','Carl','Harvey','','5-(233)156-4659','','65','164'); +INSERT INTO "Contact" VALUES(69,'Dr','Raymond','Alvarez','','','Research Associate','119','59'); +INSERT INTO "Contact" VALUES(70,'Dr','Stephanie','Bailey','','2-(434)350-2344','Programmer IV','119','59'); +INSERT INTO "Contact" VALUES(71,'','Samuel','Harrison','','4-(604)044-0141','Software Consultant','119','59'); +INSERT INTO "Contact" VALUES(72,'Honorable','David','Johnson','djohnson2b@scribd.com','2-(993)514-8842','','119','59'); +INSERT INTO "Contact" VALUES(73,'Mr','Angela','Kelly','akelly2i@miitbeian.gov.cn','','','119','59'); +INSERT INTO "Contact" VALUES(74,'Mrs','Chris','Medina','cmedina2a@networkadvertising.org','2-(128)916-8821','Quality Engineer','119','59'); +INSERT INTO "Contact" VALUES(75,'Ms','Barbara','Russell','','2-(745)944-4149','Registered Nurse','119','59'); +INSERT INTO "Contact" VALUES(76,'Mrs','Ernest','Smith','','','','119','59'); +INSERT INTO "Contact" VALUES(77,'','Albert','Stone','astone2g@dot.gov','','','119','59'); +INSERT INTO "Contact" VALUES(78,'Honorable','Gary','Taylor','','','','119','59'); +INSERT INTO "Contact" VALUES(79,'Dr','Joe','Bailey','','1-(495)461-4170','','120','2'); +INSERT INTO "Contact" VALUES(80,'Rev','Russell','Thomas','rthomas2m@e-recht24.de','','Web Developer IV','121','20'); +INSERT INTO "Contact" VALUES(81,'Ms','Joseph','Lane','','8-(234)380-1727','Paralegal','122','163'); +INSERT INTO "Contact" VALUES(82,'','George','Perry','','','','123','21'); +INSERT INTO "Contact" VALUES(83,'Honorable','Sara','Anderson','','2-(803)151-8520','','124','149'); +INSERT INTO "Contact" VALUES(84,'Ms','Martha','Hansen','mhanseny@t-online.de','','Software Engineer I','125','30'); +INSERT INTO "Contact" VALUES(85,'Ms','Mildred','Willis','','3-(474)705-1693','','126','154'); +INSERT INTO "Contact" VALUES(86,'Ms','Roy','Holmes','rholmes1r@digg.com','5-(595)114-1672','Legal Assistant','127','55'); +INSERT INTO "Contact" VALUES(87,'','Mildred','Kelly','','7-(629)659-4669','Occupational Therapist','128','55'); +INSERT INTO "Contact" VALUES(88,'Mr','Kathryn','Patterson','kpattersone@vkontakte.ru','5-(310)993-7204','','66','29'); +INSERT INTO "Contact" VALUES(89,'Rev','Ryan','Reyes','','','','129','37'); +INSERT INTO "Contact" VALUES(90,'Mrs','Howard','Burke','hburke1e@yandex.ru','','Chief Design Engineer','130','22'); +INSERT INTO "Contact" VALUES(91,'','Melissa','Webb','','0-(411)456-7376','Accountant I','131','15'); +INSERT INTO "Contact" VALUES(92,'Mrs','Kathy','Long','','8-(902)778-4158','','132','28'); +INSERT INTO "Contact" VALUES(93,'Rev','Walter','Powell','','4-(668)963-5868','','133','47'); +INSERT INTO "Contact" VALUES(94,'','Russell','Powell','rpowell1h@redcross.org','','Clinical Specialist','134','34'); +INSERT INTO "Contact" VALUES(95,'Mr','Harry','Jenkins','hjenkinsh@typepad.com','5-(956)554-4923','Automation Specialist IV','135','50'); +INSERT INTO "Contact" VALUES(96,'Rev','Rose','Gonzales','','','VP Product Management','136','27'); +INSERT INTO "Contact" VALUES(97,'','Matthew','Richards','','','','137','53'); +INSERT INTO "Contact" VALUES(98,'Mrs','Gerald','Sullivan','','9-(806)552-8308','','138','37'); +INSERT INTO "Contact" VALUES(99,'','George','Hawkins','ghawkins2q@typepad.com','6-(759)496-9987','Desktop Support Technician','67','13'); +INSERT INTO "Contact" VALUES(100,'Mr','Gloria','Hart','','7-(709)961-7856','','67','49'); +INSERT INTO "Contact" VALUES(101,'Ms','Nicole','Chapman','nchapman0@europa.eu','3-(472)603-5084','','139','150'); +INSERT INTO "Contact" VALUES(102,'','Kevin','Hudson','','','','140','44'); CREATE TABLE "General_Accounting_Unit__c" ( - id INTEGER NOT NULL, - name VARCHAR(255), + id INTEGER NOT NULL, + name VARCHAR(255), PRIMARY KEY (id) ); INSERT INTO "General_Accounting_Unit__c" VALUES(1,'The curabitur convallis Fund'); @@ -832,1330 +922,2443 @@ INSERT INTO "General_Accounting_Unit__c" VALUES(36,'The consequat Fund'); INSERT INTO "General_Accounting_Unit__c" VALUES(37,'The dapibus Fund'); INSERT INTO "General_Accounting_Unit__c" VALUES(38,'The ipsum Fund'); INSERT INTO "General_Accounting_Unit__c" VALUES(39,'The sit Fund'); -INSERT INTO "General_Accounting_Unit__c" VALUES(40,'The et Fund'); -INSERT INTO "General_Accounting_Unit__c" VALUES(41,'The nulla Fund'); -INSERT INTO "General_Accounting_Unit__c" VALUES(42,'The nullam Fund'); -INSERT INTO "General_Accounting_Unit__c" VALUES(43,'The proin eu Fund'); -INSERT INTO "General_Accounting_Unit__c" VALUES(44,'The erat Fund'); -INSERT INTO "General_Accounting_Unit__c" VALUES(45,'The eget Fund'); -INSERT INTO "General_Accounting_Unit__c" VALUES(46,'The eleifend pede Fund'); +INSERT INTO "General_Accounting_Unit__c" VALUES(40,'The nullam Fund'); +INSERT INTO "General_Accounting_Unit__c" VALUES(41,'The proin eu Fund'); +INSERT INTO "General_Accounting_Unit__c" VALUES(42,'The erat Fund'); +INSERT INTO "General_Accounting_Unit__c" VALUES(43,'The eget Fund'); +INSERT INTO "General_Accounting_Unit__c" VALUES(44,'The eleifend pede Fund'); +INSERT INTO "General_Accounting_Unit__c" VALUES(45,'The et Fund'); +INSERT INTO "General_Accounting_Unit__c" VALUES(46,'The nulla Fund'); INSERT INTO "General_Accounting_Unit__c" VALUES(47,'The felis eu Fund'); INSERT INTO "General_Accounting_Unit__c" VALUES(48,'The tempus sit Fund'); INSERT INTO "General_Accounting_Unit__c" VALUES(49,'The lobortis sapien Fund'); INSERT INTO "General_Accounting_Unit__c" VALUES(50,'The augue quam Fund'); CREATE TABLE "Opportunity" ( - id INTEGER NOT NULL, - name VARCHAR(255), - amount VARCHAR(255), - stage_name VARCHAR(255), - close_date VARCHAR(255), - dont_create_payments VARCHAR(255), - account_id VARCHAR(255), - primary_contact__c VARCHAR(255), - npe03__recurring_donation__c VARCHAR(255), - campaign_id VARCHAR(255), + id INTEGER NOT NULL, + name VARCHAR(255), + amount VARCHAR(255), + stage_name VARCHAR(255), + close_date VARCHAR(255), + dont_create_payments VARCHAR(255), + "AccountId" VARCHAR(255), + "Primary_Contact__c" VARCHAR(255), + "npe03__Recurring_Donation__c" VARCHAR(255), + "CampaignId" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "Opportunity" VALUES(1,'Ann Chapman: $502','502.0','Closed Won','2017-12-11','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(2,'Ann Chapman: $635','635.0','Closed Won','2018-05-04','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(3,'Alice Patterson: $177','177.0','Closed Won','2018-11-23','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(4,'Alice Patterson: $208','208.0','Closed Won','2018-01-20','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(5,'Alice Patterson: $553','553.0','Closed Won','2017-10-15','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(6,'Alice Patterson: $565','565.0','Closed Won','2018-09-06','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(7,'Alice Patterson: $310','310.0','Closed Won','2018-02-02','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(8,'Alice Patterson: $483','483.0','Closed Won','2016-06-11','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(9,'Alice Patterson: $151','151.0','Closed Won','2018-02-20','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(10,'Alice Patterson: $537','537.0','Closed Won','2016-05-31','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(11,'Alice Patterson: $218','218.0','Closed Won','2018-11-23','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(12,'Alice Patterson: $376','376.0','Closed Won','2018-10-13','false','1','1','',''); -INSERT INTO "Opportunity" VALUES(13,'Edgeify: $4965','4965.0','Closed Won','2017-11-02','true','105','','',''); -INSERT INTO "Opportunity" VALUES(14,'Edgeify: $5297','5297.0','Closed Won','2017-11-27','false','105','','',''); -INSERT INTO "Opportunity" VALUES(15,'Edgeify: $6258','6258.0','Closed Won','2016-07-24','false','105','','',''); -INSERT INTO "Opportunity" VALUES(16,'Edgeify: $2804','2804.0','Closed Won','2016-03-12','false','105','','',''); -INSERT INTO "Opportunity" VALUES(17,'Edgeify: $7164','7164.0','Closed Won','2016-03-09','false','105','','',''); -INSERT INTO "Opportunity" VALUES(18,'Edgeify: $6989','6989.0','Closed Won','2018-08-29','false','105','','',''); -INSERT INTO "Opportunity" VALUES(19,'Edgeify: $7858','7858.0','Closed Won','2017-01-04','true','105','','',''); -INSERT INTO "Opportunity" VALUES(20,'Edgeify: $181','181.0','Closed Won','2016-12-04','false','105','','',''); -INSERT INTO "Opportunity" VALUES(21,'Edgeify: $3422','3422.0','Closed Won','2017-01-31','false','105','','',''); -INSERT INTO "Opportunity" VALUES(22,'Edgeify: $2434','2434.0','Closed Won','2016-11-02','false','105','','',''); -INSERT INTO "Opportunity" VALUES(23,'Jean Gardner: $457','457.0','Closed Won','2017-08-27','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(24,'Jean Gardner: $13','13.0','Closed Won','2017-01-19','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(25,'Jean Gardner: $234','234.0','Closed Won','2017-11-04','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(26,'Jean Gardner: $84','84.0','Closed Won','2016-05-05','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(27,'Jean Gardner: $952','952.0','Closed Won','2018-11-16','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(28,'Jean Gardner: $3','3.0','Closed Won','2017-04-20','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(29,'Jean Gardner: $521','521.0','Closed Won','2016-09-22','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(30,'Jean Gardner: $567','567.0','Closed Won','2016-02-26','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(31,'Jean Gardner: $744','744.0','Closed Won','2018-04-26','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(32,'Jean Gardner: $619','619.0','Closed Won','2016-09-11','false','11','3','',''); -INSERT INTO "Opportunity" VALUES(33,'Skidoo: $3738','3738.0','Closed Won','2017-08-02','false','140','','',''); -INSERT INTO "Opportunity" VALUES(34,'Skidoo: $6351','6351.0','Closed Won','2017-12-29','false','140','','',''); -INSERT INTO "Opportunity" VALUES(35,'Skidoo: $2233','2233.0','Closed Won','2017-05-18','false','140','','',''); -INSERT INTO "Opportunity" VALUES(36,'Skidoo: $3361','3361.0','Closed Won','2016-09-08','false','140','','',''); -INSERT INTO "Opportunity" VALUES(37,'Skidoo: $3124','3124.0','Closed Won','2018-05-15','false','140','','',''); -INSERT INTO "Opportunity" VALUES(38,'Skidoo: $2909','2909.0','Closed Won','2018-05-16','false','140','','',''); -INSERT INTO "Opportunity" VALUES(39,'Skidoo: $1824','1824.0','Closed Won','2017-07-30','false','140','','',''); -INSERT INTO "Opportunity" VALUES(40,'Skidoo: $4670','4670.0','Closed Won','2016-09-17','false','140','','',''); -INSERT INTO "Opportunity" VALUES(41,'Skidoo: $5400','5400.0','Closed Won','2017-03-22','false','140','','',''); -INSERT INTO "Opportunity" VALUES(42,'Skidoo: $9404','9404.0','Closed Won','2016-02-15','false','140','','',''); -INSERT INTO "Opportunity" VALUES(43,'Lori Murray: $504','504.0','Closed Won','2017-03-01','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(44,'Lori Murray: $109','109.0','Closed Won','2019-02-07','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(45,'Lori Murray: $479','479.0','Closed Won','2017-08-07','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(46,'Lori Murray: $270','270.0','Closed Won','2016-10-28','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(47,'Lori Murray: $31','31.0','Closed Won','2019-01-20','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(48,'Lori Murray: $157','157.0','Closed Won','2018-04-24','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(49,'Lori Murray: $31','31.0','Closed Won','2017-09-28','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(50,'Lori Murray: $836','836.0','Closed Won','2019-01-12','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(51,'Lori Murray: $984','984.0','Closed Won','2016-03-20','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(52,'Lori Murray: $862','862.0','Closed Won','2017-08-04','false','15','9','',''); -INSERT INTO "Opportunity" VALUES(53,'Robin Hicks: $433','433.0','Closed Won','2018-01-22','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(54,'Robin Hicks: $794','794.0','Closed Won','2018-05-02','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(55,'Robin Hicks: $6','6.0','Closed Won','2018-04-27','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(56,'Robin Hicks: $87','87.0','Closed Won','2017-07-30','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(57,'Robin Hicks: $641','641.0','Closed Won','2018-10-05','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(58,'Robin Hicks: $29','29.0','Closed Won','2017-04-01','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(59,'Robin Hicks: $915','915.0','Closed Won','2016-07-21','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(60,'Robin Hicks: $907','907.0','Closed Won','2018-12-19','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(61,'Robin Hicks: $905','905.0','Closed Won','2017-09-13','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(62,'Robin Hicks: $957','957.0','Closed Won','2016-11-01','false','26','21','',''); -INSERT INTO "Opportunity" VALUES(63,'Ann Chapman: $547','547.0','Closed Won','2017-09-16','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(64,'Ann Chapman: $235','235.0','Closed Won','2017-07-08','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(65,'Ann Chapman: $514','514.0','Closed Won','2017-08-28','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(66,'Ann Chapman: $70','70.0','Closed Won','2018-07-06','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(67,'Ann Chapman: $401','401.0','Closed Won','2016-04-13','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(68,'Carl Harvey: $409','409.0','Closed Won','2017-03-29','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(69,'Carl Harvey: $858','858.0','Closed Won','2018-03-05','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(70,'Ann Chapman: $466','466.0','Closed Won','2016-12-10','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(71,'Ann Chapman: $344','344.0','Closed Won','2018-01-07','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(72,'Ann Chapman: $110','110.0','Closed Won','2017-05-08','false','34','30','',''); -INSERT INTO "Opportunity" VALUES(73,'Raymond Alvarez: $820','820.0','Closed Won','2018-01-26','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(74,'Raymond Alvarez: $492','492.0','Closed Won','2018-09-09','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(75,'Raymond Alvarez: $892','892.0','Closed Won','2018-10-25','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(76,'Raymond Alvarez: $798','798.0','Closed Won','2017-05-13','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(77,'Raymond Alvarez: $265','265.0','Closed Won','2016-04-17','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(78,'Raymond Alvarez: $934','934.0','Closed Won','2018-04-23','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(79,'Raymond Alvarez: $599','599.0','Closed Won','2016-11-23','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(80,'Raymond Alvarez: $538','538.0','Closed Won','2018-10-03','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(81,'Raymond Alvarez: $993','993.0','Closed Won','2016-04-21','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(82,'Raymond Alvarez: $4','4.0','Closed Won','2017-04-22','false','60','42','',''); -INSERT INTO "Opportunity" VALUES(83,'Eugene Stephens: $782','782.0','Closed Won','2017-03-21','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(84,'Eugene Stephens: $314','314.0','Closed Won','2018-12-27','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(85,'Eugene Stephens: $436','436.0','Closed Won','2019-01-04','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(86,'Eugene Stephens: $157','157.0','Closed Won','2016-12-28','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(87,'Eugene Stephens: $685','685.0','Closed Won','2017-08-07','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(88,'Eugene Stephens: $974','974.0','Closed Won','2017-07-18','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(89,'Eugene Stephens: $751','751.0','Closed Won','2019-01-30','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(90,'Eugene Stephens: $449','449.0','Closed Won','2016-10-17','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(91,'Eugene Stephens: $643','643.0','Closed Won','2017-09-26','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(92,'Eugene Stephens: $127','127.0','Closed Won','2018-12-13','false','48','97','',''); -INSERT INTO "Opportunity" VALUES(93,'Anthony Fisher: $377','377.0','Closed Won','2018-06-16','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(94,'Anthony Fisher: $654','654.0','Closed Won','2016-03-02','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(95,'Anthony Fisher: $926','926.0','Closed Won','2017-01-18','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(96,'Anthony Fisher: $838','838.0','Closed Won','2017-07-29','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(97,'Anthony Fisher: $46','46.0','Closed Won','2018-07-03','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(98,'Anthony Fisher: $440','440.0','Closed Won','2016-12-12','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(99,'Anthony Fisher: $353','353.0','Closed Won','2018-07-09','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(100,'Anthony Fisher: $338','338.0','Closed Won','2016-08-31','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(101,'Anthony Fisher: $546','546.0','Closed Won','2017-04-10','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(102,'Anthony Fisher: $316','316.0','Closed Won','2018-10-07','false','49','58','',''); -INSERT INTO "Opportunity" VALUES(103,'Carl Harvey: $750','750.0','Closed Won','2017-09-03','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(104,'Carl Harvey: $976','976.0','Closed Won','2016-06-01','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(105,'Carl Harvey: $22','22.0','Closed Won','2016-03-06','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(106,'Carl Harvey: $627','627.0','Closed Won','2018-10-08','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(107,'Carl Harvey: $965','965.0','Closed Won','2018-01-01','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(108,'Carl Harvey: $116','116.0','Closed Won','2018-05-15','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(109,'Carl Harvey: $260','260.0','Closed Won','2018-12-06','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(110,'Carl Harvey: $365','365.0','Closed Won','2018-08-07','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(111,'Carl Harvey: $550','550.0','Closed Won','2018-06-25','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(112,'Carl Harvey: $131','131.0','Closed Won','2019-06-02','true','6','66','',''); -INSERT INTO "Opportunity" VALUES(113,'Carl Harvey: $631','631.0','Closed Won','2016-07-21','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(114,'Carl Harvey: $970','970.0','Closed Won','2018-09-15','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(115,'Carl Harvey: $59','59.0','Closed Won','2018-06-07','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(116,'Carl Harvey: $253','253.0','Closed Won','2018-11-20','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(117,'Carl Harvey: $421','421.0','Closed Won','2016-04-23','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(118,'Carl Harvey: $743','743.0','Closed Won','2018-09-03','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(119,'Carl Harvey: $37','37.0','Closed Won','2017-11-19','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(120,'Carl Harvey: $146','146.0','Closed Won','2017-11-27','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(121,'Carl Harvey: $657','657.0','Closed Won','2016-09-08','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(122,'Carl Harvey: $36','36.0','Closed Won','2016-09-02','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(123,'Carl Harvey: $393','393.0','Closed Won','2018-03-18','true','6','66','',''); -INSERT INTO "Opportunity" VALUES(124,'Carl Harvey: $643','643.0','Closed Won','2019-11-24','true','6','66','',''); -INSERT INTO "Opportunity" VALUES(125,'Carl Harvey: $813','813.0','Closed Won','2017-04-10','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(126,'Carl Harvey: $557','557.0','Closed Won','2017-12-02','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(127,'Carl Harvey: $926','926.0','Closed Won','2018-11-10','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(128,'Carl Harvey: $13','13.0','Closed Won','2018-02-28','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(129,'Carl Harvey: $609','609.0','Closed Won','2016-12-18','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(130,'Carl Harvey: $962','962.0','Closed Won','2018-01-28','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(131,'Carl Harvey: $204','204.0','Closed Won','2017-12-25','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(132,'Carl Harvey: $153','153.0','Closed Won','2017-06-01','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(133,'Carl Harvey: $89','89.0','Closed Won','2018-01-23','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(134,'Carl Harvey: $331','331.0','Closed Won','2018-01-26','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(135,'Carl Harvey: $842','842.0','Closed Won','2017-07-29','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(136,'Carl Harvey: $736','736.0','Closed Won','2016-09-14','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(137,'Carl Harvey: $978','978.0','Closed Won','2016-03-20','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(138,'Carl Harvey: $927','927.0','Closed Won','2017-04-24','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(139,'Carl Harvey: $321','321.0','Closed Won','2016-05-17','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(140,'Carl Harvey: $282','282.0','Closed Won','2016-02-04','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(141,'Carl Harvey: $750','750.0','Closed Won','2016-02-21','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(142,'Carl Harvey: $996','996.0','Closed Won','2016-06-16','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(143,'Carl Harvey: $536','536.0','Closed Won','2019-01-14','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(144,'Carl Harvey: $756','756.0','Closed Won','2018-11-10','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(145,'Carl Harvey: $119','119.0','Closed Won','2018-04-03','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(146,'Carl Harvey: $309','309.0','Closed Won','2016-11-01','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(147,'Carl Harvey: $230','230.0','Closed Won','2017-10-06','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(148,'Carl Harvey: $118','118.0','Closed Won','2017-04-18','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(149,'Carl Harvey: $327','327.0','Closed Won','2017-01-23','true','6','66','',''); -INSERT INTO "Opportunity" VALUES(150,'Carl Harvey: $557','557.0','Closed Won','2018-01-07','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(151,'Carl Harvey: $642','642.0','Closed Won','2018-02-08','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(152,'Carl Harvey: $782','782.0','Closed Won','2017-04-19','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(153,'Carl Harvey: $409','409.0','Closed Won','2017-05-21','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(154,'Carl Harvey: $501','501.0','Closed Won','2016-11-30','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(155,'Carl Harvey: $998','998.0','Closed Won','2017-08-18','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(156,'Carl Harvey: $338','338.0','Closed Won','2017-01-01','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(157,'Carl Harvey: $737','737.0','Closed Won','2018-12-15','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(158,'Carl Harvey: $410','410.0','Closed Won','2018-04-12','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(159,'Carl Harvey: $202','202.0','Closed Won','2018-10-16','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(160,'Carl Harvey: $997','997.0','Closed Won','2019-01-29','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(161,'Carl Harvey: $609','609.0','Closed Won','2016-06-09','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(162,'Carl Harvey: $952','952.0','Closed Won','2018-06-15','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(163,'Carl Harvey: $734','734.0','Closed Won','2017-02-01','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(164,'Carl Harvey: $540','540.0','Closed Won','2019-06-28','true','6','66','',''); -INSERT INTO "Opportunity" VALUES(165,'Carl Harvey: $507','507.0','Closed Won','2017-04-23','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(166,'Carl Harvey: $976','976.0','Closed Won','2017-11-07','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(167,'Carl Harvey: $973','973.0','Closed Won','2017-11-20','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(168,'Carl Harvey: $837','837.0','Closed Won','2018-03-22','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(169,'Carl Harvey: $871','871.0','Closed Won','2018-09-25','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(170,'Carl Harvey: $504','504.0','Closed Won','2017-06-15','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(171,'Carl Harvey: $234','234.0','Closed Won','2017-12-20','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(172,'Carl Harvey: $762','762.0','Closed Won','2016-03-31','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(173,'Carl Harvey: $357','357.0','Closed Won','2017-10-24','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(174,'Carl Harvey: $992','992.0','Closed Won','2018-02-28','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(175,'Carl Harvey: $666','666.0','Closed Won','2016-07-19','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(176,'Carl Harvey: $128','128.0','Closed Won','2017-04-22','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(177,'Carl Harvey: $111','111.0','Closed Won','2016-10-31','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(178,'Carl Harvey: $719','719.0','Closed Won','2016-06-10','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(179,'Carl Harvey: $22','22.0','Closed Won','2018-06-07','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(180,'Carl Harvey: $844','844.0','Closed Won','2017-05-30','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(181,'Carl Harvey: $989','989.0','Closed Won','2017-10-02','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(182,'Carl Harvey: $368','368.0','Closed Won','2017-09-04','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(183,'Carl Harvey: $896','896.0','Closed Won','2016-03-07','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(184,'Carl Harvey: $108','108.0','Closed Won','2018-07-01','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(185,'Carl Harvey: $911','911.0','Closed Won','2017-08-17','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(186,'Carl Harvey: $834','834.0','Closed Won','2017-04-08','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(187,'Carl Harvey: $45','45.0','Closed Won','2016-12-31','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(188,'Carl Harvey: $99','99.0','Closed Won','2017-11-25','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(189,'Carl Harvey: $971','971.0','Closed Won','2016-10-07','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(190,'Carl Harvey: $639','639.0','Closed Won','2018-08-19','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(191,'Carl Harvey: $919','919.0','Closed Won','2018-01-13','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(192,'Carl Harvey: $476','476.0','Closed Won','2016-03-22','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(193,'Carl Harvey: $321','321.0','Closed Won','2017-03-19','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(194,'Carl Harvey: $839','839.0','Closed Won','2018-11-11','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(195,'Carl Harvey: $451','451.0','Closed Won','2018-06-22','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(196,'Carl Harvey: $236','236.0','Closed Won','2019-01-18','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(197,'Carl Harvey: $585','585.0','Closed Won','2018-08-25','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(198,'Carl Harvey: $546','546.0','Closed Won','2017-08-29','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(199,'Carl Harvey: $485','485.0','Closed Won','2017-04-29','true','6','66','',''); -INSERT INTO "Opportunity" VALUES(200,'Carl Harvey: $329','329.0','Closed Won','2018-09-18','false','6','66','',''); -INSERT INTO "Opportunity" VALUES(201,'Joseph Lane: $272','272.0','Closed Won','2016-12-10','false','63','70','',''); -INSERT INTO "Opportunity" VALUES(202,'Joseph Lane: $261','261.0','Closed Won','2018-06-06','false','63','70','',''); -INSERT INTO "Opportunity" VALUES(203,'Joseph Lane: $921','921.0','Closed Won','2018-05-04','false','63','70','',''); -INSERT INTO "Opportunity" VALUES(204,'Joseph Lane: $757','757.0','Closed Won','2017-06-28','true','63','70','',''); -INSERT INTO "Opportunity" VALUES(205,'Joseph Lane: $375','375.0','Closed Won','2016-09-04','false','63','70','',''); -INSERT INTO "Opportunity" VALUES(206,'Joseph Lane: $698','698.0','Closed Won','2016-03-23','false','63','70','',''); -INSERT INTO "Opportunity" VALUES(207,'Joseph Lane: $892','892.0','Closed Won','2017-04-20','false','63','70','',''); -INSERT INTO "Opportunity" VALUES(208,'Joseph Lane: $85','85.0','Closed Won','2018-06-30','true','63','70','',''); -INSERT INTO "Opportunity" VALUES(209,'Joseph Lane: $830','830.0','Closed Won','2017-01-05','false','63','70','',''); -INSERT INTO "Opportunity" VALUES(210,'Joseph Lane: $656','656.0','Closed Won','2018-01-15','false','63','70','',''); -INSERT INTO "Opportunity" VALUES(211,'Carl Harvey Donation (1) 1/15/2019','75.0','Closed Won','2019-01-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(212,'Carl Harvey Donation (2) 2/15/2019','75.0','Closed Won','2019-02-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(213,'Carl Harvey Donation (3) 3/15/2019','75.0','Closed Won','2019-03-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(214,'Carl Harvey Donation (4) 4/15/2019','75.0','Closed Won','2019-04-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(219,'Carl Harvey Donation (5) 5/15/2019','75.0','Closed Won','2019-05-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(220,'Carl Harvey Donation (6) 6/15/2019','75.0','Closed Won','2019-06-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(221,'Carl Harvey Donation (7) 7/15/2019','75.0','Closed Won','2019-07-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(215,'Carl Harvey Donation (8) 8/15/2019','75.0','Closed Won','2019-08-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(216,'Carl Harvey Donation (9) 9/15/2019','75.0','Closed Won','2019-09-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(217,'Carl Harvey Donation (10) 10/15/2019','75.0','Closed Won','2019-10-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(218,'Carl Harvey Donation (11) 11/15/2019','75.0','Closed Won','2019-11-15','false','6','66','1',''); -INSERT INTO "Opportunity" VALUES(222,'Carl Harvey Donation (12) 12/15/2019','75.0','Closed Won','2019-12-15','false','6','66','1',''); +INSERT INTO "Opportunity" VALUES(1,'Carl Harvey Donation (7) 7/15/2019','75.0','Closed Won','2019-07-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(2,'Carl Harvey Donation (12) 12/15/2019','75.0','Closed Won','2019-12-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(3,'Raymond Alvarez: $820','820.0','Closed Won','2018-01-26','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(4,'Raymond Alvarez: $492','492.0','Closed Won','2018-09-09','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(5,'Raymond Alvarez: $892','892.0','Closed Won','2018-10-25','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(6,'Raymond Alvarez: $798','798.0','Closed Won','2017-05-13','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(7,'Raymond Alvarez: $265','265.0','Closed Won','2016-04-17','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(8,'Raymond Alvarez: $934','934.0','Closed Won','2018-04-23','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(9,'Raymond Alvarez: $599','599.0','Closed Won','2016-11-23','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(10,'Raymond Alvarez: $538','538.0','Closed Won','2018-10-03','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(11,'Raymond Alvarez: $993','993.0','Closed Won','2016-04-21','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(12,'Raymond Alvarez: $4','4.0','Closed Won','2017-04-22','False','119','69','',''); +INSERT INTO "Opportunity" VALUES(13,'Joseph Lane: $272','272.0','Closed Won','2016-12-10','False','122','81','',''); +INSERT INTO "Opportunity" VALUES(14,'Joseph Lane: $261','261.0','Closed Won','2018-06-06','False','122','81','',''); +INSERT INTO "Opportunity" VALUES(15,'Joseph Lane: $921','921.0','Closed Won','2018-05-04','False','122','81','',''); +INSERT INTO "Opportunity" VALUES(16,'Joseph Lane: $757','757.0','Closed Won','2017-06-28','True','122','81','',''); +INSERT INTO "Opportunity" VALUES(17,'Joseph Lane: $375','375.0','Closed Won','2016-09-04','False','122','81','',''); +INSERT INTO "Opportunity" VALUES(18,'Joseph Lane: $698','698.0','Closed Won','2016-03-23','False','122','81','',''); +INSERT INTO "Opportunity" VALUES(19,'Joseph Lane: $892','892.0','Closed Won','2017-04-20','False','122','81','',''); +INSERT INTO "Opportunity" VALUES(20,'Joseph Lane: $85','85.0','Closed Won','2018-06-30','True','122','81','',''); +INSERT INTO "Opportunity" VALUES(21,'Joseph Lane: $830','830.0','Closed Won','2017-01-05','False','122','81','',''); +INSERT INTO "Opportunity" VALUES(22,'Joseph Lane: $656','656.0','Closed Won','2018-01-15','False','122','81','',''); +INSERT INTO "Opportunity" VALUES(23,'Alice Patterson - Soft Credit 7','44.0','Closed Won','2021-12-23','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(24,'Alice Patterson - Hard Credit 2','165.0','Closed Won','2021-11-01','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(25,'Alice Patterson - Soft Credit 19','190.0','Closed Won','2021-09-11','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(26,'Alice Patterson - Hard Credit 3','790.0','Closed Won','2021-11-11','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(27,'Alice Patterson - Hard Credit 12','33.0','Closed Won','2021-11-30','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(28,'Alice Patterson - Partial Soft Credit 6','330.0','Closed Won','2021-05-22','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(29,'Patterson - Soft Credit 2','250.0','Closed Won','2021-11-10','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(30,'Alice Patterson - Hard Credit 17','543.0','Closed Won','2021-12-04','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(31,'Alice Patterson - Partial Soft Credit 7','666.0','Closed Won','2021-11-19','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(32,'Alice Patterson - Partial Soft Credit 2','130.0','Closed Won','2021-11-20','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(33,'Alice Patterson - Non Closed/Won Opp 7','40.0','Value Proposition','2021-11-19','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(34,'Alice Patterson - Non Closed/Won Opp 10','436.0','Id. Decision Makers','2021-11-05','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(35,'Alice Patterson: $177','177.0','Closed Won','2018-11-23','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(36,'Alice Patterson: $208','208.0','Closed Won','2018-01-20','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(37,'Alice Patterson: $553','553.0','Closed Won','2017-10-15','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(38,'Alice Patterson: $565','565.0','Closed Won','2018-09-06','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(39,'Alice Patterson: $310','310.0','Closed Won','2018-02-02','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(40,'Alice Patterson: $483','483.0','Closed Won','2016-06-11','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(41,'Alice Patterson: $151','151.0','Closed Won','2018-02-20','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(42,'Alice Patterson: $537','537.0','Closed Won','2016-05-31','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(43,'Alice Patterson: $218','218.0','Closed Won','2018-11-23','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(44,'Alice Patterson: $376','376.0','Closed Won','2018-10-13','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(45,'Edgeify: $4965','4965.0','Closed Won','2017-11-02','True','164','','',''); +INSERT INTO "Opportunity" VALUES(46,'Edgeify: $5297','5297.0','Closed Won','2017-11-27','False','164','','',''); +INSERT INTO "Opportunity" VALUES(47,'Edgeify: $6258','6258.0','Closed Won','2016-07-24','False','164','','',''); +INSERT INTO "Opportunity" VALUES(48,'Edgeify: $2804','2804.0','Closed Won','2016-03-12','False','164','','',''); +INSERT INTO "Opportunity" VALUES(49,'Edgeify: $7164','7164.0','Closed Won','2016-03-09','False','164','','',''); +INSERT INTO "Opportunity" VALUES(50,'Edgeify: $6989','6989.0','Closed Won','2018-08-29','False','164','','',''); +INSERT INTO "Opportunity" VALUES(51,'Edgeify: $7858','7858.0','Closed Won','2017-01-04','True','164','','',''); +INSERT INTO "Opportunity" VALUES(52,'Edgeify: $181','181.0','Closed Won','2016-12-04','False','164','','',''); +INSERT INTO "Opportunity" VALUES(53,'Edgeify: $3422','3422.0','Closed Won','2017-01-31','False','164','','',''); +INSERT INTO "Opportunity" VALUES(54,'Edgeify: $2434','2434.0','Closed Won','2016-11-02','False','164','','',''); +INSERT INTO "Opportunity" VALUES(55,'Jean Gardner: $457','457.0','Closed Won','2017-08-27','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(56,'Jean Gardner: $13','13.0','Closed Won','2017-01-19','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(57,'Jean Gardner: $234','234.0','Closed Won','2017-11-04','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(58,'Jean Gardner: $84','84.0','Closed Won','2016-05-05','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(59,'Carl Harvey Donation (4) 4/15/2019','75.0','Closed Won','2019-04-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(60,'Carl Harvey Donation (8) 8/15/2019','75.0','Closed Won','2019-08-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(61,'Carl Harvey Donation (9) 9/15/2019','75.0','Closed Won','2019-09-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(62,'Carl Harvey Donation (10) 10/15/2019','75.0','Closed Won','2019-10-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(63,'Carl Harvey Donation (11) 11/15/2019','75.0','Closed Won','2019-11-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(64,'Carl Harvey Donation (5) 5/15/2019','75.0','Closed Won','2019-05-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(65,'Carl Harvey Donation (6) 6/15/2019','75.0','Closed Won','2019-06-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(66,'Patterson - Partial Soft Credit 2','575.0','Closed Won','2021-11-10','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(67,'Patterson - Non-Closed Opp 1','100.0','Closed Lost','2021-11-10','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(68,'Jean Gardner: $952','952.0','Closed Won','2018-11-16','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(69,'Jean Gardner: $3','3.0','Closed Won','2017-04-20','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(70,'Jean Gardner: $521','521.0','Closed Won','2016-09-22','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(71,'Jean Gardner: $567','567.0','Closed Won','2016-02-26','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(72,'Jean Gardner: $744','744.0','Closed Won','2018-04-26','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(73,'Jean Gardner: $619','619.0','Closed Won','2016-09-11','False','70','12','',''); +INSERT INTO "Opportunity" VALUES(74,'Skidoo: $3738','3738.0','Closed Won','2017-08-02','False','33','','',''); +INSERT INTO "Opportunity" VALUES(75,'Skidoo: $6351','6351.0','Closed Won','2017-12-29','False','33','','',''); +INSERT INTO "Opportunity" VALUES(76,'Skidoo: $2233','2233.0','Closed Won','2017-05-18','False','33','','',''); +INSERT INTO "Opportunity" VALUES(77,'Skidoo: $3361','3361.0','Closed Won','2016-09-08','False','33','','',''); +INSERT INTO "Opportunity" VALUES(78,'Skidoo: $3124','3124.0','Closed Won','2018-05-15','False','33','','',''); +INSERT INTO "Opportunity" VALUES(79,'Skidoo: $2909','2909.0','Closed Won','2018-05-16','False','33','','',''); +INSERT INTO "Opportunity" VALUES(80,'Skidoo: $1824','1824.0','Closed Won','2017-07-30','False','33','','',''); +INSERT INTO "Opportunity" VALUES(81,'Skidoo: $4670','4670.0','Closed Won','2016-09-17','False','33','','',''); +INSERT INTO "Opportunity" VALUES(82,'Skidoo: $5400','5400.0','Closed Won','2017-03-22','False','33','','',''); +INSERT INTO "Opportunity" VALUES(83,'Skidoo: $9404','9404.0','Closed Won','2016-02-15','False','33','','',''); +INSERT INTO "Opportunity" VALUES(84,'Lori Murray: $504','504.0','Closed Won','2017-03-01','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(85,'Lori Murray: $109','109.0','Closed Won','2019-02-07','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(86,'Lori Murray: $479','479.0','Closed Won','2017-08-07','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(87,'Lori Murray: $270','270.0','Closed Won','2016-10-28','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(88,'Lori Murray: $31','31.0','Closed Won','2019-01-20','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(89,'Lori Murray: $157','157.0','Closed Won','2018-04-24','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(90,'Lori Murray: $31','31.0','Closed Won','2017-09-28','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(91,'Lori Murray: $836','836.0','Closed Won','2019-01-12','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(92,'Lori Murray: $984','984.0','Closed Won','2016-03-20','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(93,'Lori Murray: $862','862.0','Closed Won','2017-08-04','False','74','18','',''); +INSERT INTO "Opportunity" VALUES(94,'Robin Hicks: $433','433.0','Closed Won','2018-01-22','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(95,'Robin Hicks: $794','794.0','Closed Won','2018-05-02','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(96,'Robin Hicks: $6','6.0','Closed Won','2018-04-27','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(97,'Robin Hicks: $87','87.0','Closed Won','2017-07-30','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(98,'Robin Hicks: $641','641.0','Closed Won','2018-10-05','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(99,'Robin Hicks: $29','29.0','Closed Won','2017-04-01','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(100,'Robin Hicks: $915','915.0','Closed Won','2016-07-21','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(101,'Robin Hicks: $907','907.0','Closed Won','2018-12-19','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(102,'Robin Hicks: $905','905.0','Closed Won','2017-09-13','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(103,'Robin Hicks: $957','957.0','Closed Won','2016-11-01','False','85','30','',''); +INSERT INTO "Opportunity" VALUES(104,'Ann Chapman: $502','502.0','Closed Won','2017-12-11','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(105,'Ann Chapman: $635','635.0','Closed Won','2018-05-04','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(106,'Ann Chapman: $547','547.0','Closed Won','2017-09-16','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(107,'Ann Chapman: $235','235.0','Closed Won','2017-07-08','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(108,'Ann Chapman: $514','514.0','Closed Won','2017-08-28','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(109,'Ann Chapman: $70','70.0','Closed Won','2018-07-06','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(110,'Ann Chapman: $401','401.0','Closed Won','2016-04-13','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(111,'Ann Chapman: $466','466.0','Closed Won','2016-12-10','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(112,'Ann Chapman: $344','344.0','Closed Won','2018-01-07','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(113,'Ann Chapman: $110','110.0','Closed Won','2017-05-08','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(114,'Eugene Stephens: $782','782.0','Closed Won','2017-03-21','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(115,'Eugene Stephens: $314','314.0','Closed Won','2018-12-27','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(116,'Eugene Stephens: $436','436.0','Closed Won','2019-01-04','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(117,'Eugene Stephens: $157','157.0','Closed Won','2016-12-28','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(118,'Eugene Stephens: $685','685.0','Closed Won','2017-08-07','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(119,'Eugene Stephens: $974','974.0','Closed Won','2017-07-18','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(120,'Eugene Stephens: $751','751.0','Closed Won','2019-01-30','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(121,'Eugene Stephens: $449','449.0','Closed Won','2016-10-17','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(122,'Eugene Stephens: $643','643.0','Closed Won','2017-09-26','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(123,'Eugene Stephens: $127','127.0','Closed Won','2018-12-13','False','107','55','',''); +INSERT INTO "Opportunity" VALUES(124,'Anthony Fisher: $377','377.0','Closed Won','2018-06-16','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(125,'Anthony Fisher: $654','654.0','Closed Won','2016-03-02','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(126,'Anthony Fisher: $926','926.0','Closed Won','2017-01-18','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(127,'Anthony Fisher: $838','838.0','Closed Won','2017-07-29','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(128,'Anthony Fisher: $46','46.0','Closed Won','2018-07-03','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(129,'Anthony Fisher: $440','440.0','Closed Won','2016-12-12','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(130,'Anthony Fisher: $353','353.0','Closed Won','2018-07-09','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(131,'Anthony Fisher: $338','338.0','Closed Won','2016-08-31','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(132,'Anthony Fisher: $546','546.0','Closed Won','2017-04-10','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(133,'Anthony Fisher: $316','316.0','Closed Won','2018-10-07','False','108','56','',''); +INSERT INTO "Opportunity" VALUES(134,'Carl Harvey: $409','409.0','Closed Won','2017-03-29','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(135,'Carl Harvey: $858','858.0','Closed Won','2018-03-05','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(136,'Carl Harvey: $750','750.0','Closed Won','2017-09-03','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(137,'Carl Harvey: $976','976.0','Closed Won','2016-06-01','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(138,'Carl Harvey: $22','22.0','Closed Won','2016-03-06','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(139,'Carl Harvey: $627','627.0','Closed Won','2018-10-08','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(140,'Carl Harvey: $965','965.0','Closed Won','2018-01-01','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(141,'Carl Harvey: $116','116.0','Closed Won','2018-05-15','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(142,'Carl Harvey: $260','260.0','Closed Won','2018-12-06','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(143,'Carl Harvey: $365','365.0','Closed Won','2018-08-07','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(144,'Carl Harvey: $550','550.0','Closed Won','2018-06-25','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(145,'Carl Harvey: $131','131.0','Closed Won','2019-06-02','True','65','68','',''); +INSERT INTO "Opportunity" VALUES(146,'Carl Harvey: $631','631.0','Closed Won','2016-07-21','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(147,'Carl Harvey: $970','970.0','Closed Won','2018-09-15','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(148,'Carl Harvey: $59','59.0','Closed Won','2018-06-07','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(149,'Carl Harvey: $253','253.0','Closed Won','2018-11-20','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(150,'Carl Harvey: $421','421.0','Closed Won','2016-04-23','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(151,'Carl Harvey: $743','743.0','Closed Won','2018-09-03','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(152,'Carl Harvey: $37','37.0','Closed Won','2017-11-19','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(153,'Carl Harvey: $146','146.0','Closed Won','2017-11-27','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(154,'Carl Harvey: $657','657.0','Closed Won','2016-09-08','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(155,'Carl Harvey: $36','36.0','Closed Won','2016-09-02','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(156,'Carl Harvey: $393','393.0','Closed Won','2018-03-18','True','65','68','',''); +INSERT INTO "Opportunity" VALUES(157,'Carl Harvey: $643','643.0','Closed Won','2019-11-24','True','65','68','',''); +INSERT INTO "Opportunity" VALUES(158,'Carl Harvey: $813','813.0','Closed Won','2017-04-10','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(159,'Carl Harvey: $557','557.0','Closed Won','2017-12-02','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(160,'Carl Harvey: $926','926.0','Closed Won','2018-11-10','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(161,'Carl Harvey: $13','13.0','Closed Won','2018-02-28','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(162,'Carl Harvey: $609','609.0','Closed Won','2016-12-18','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(163,'Carl Harvey: $962','962.0','Closed Won','2018-01-28','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(164,'Carl Harvey: $204','204.0','Closed Won','2017-12-25','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(165,'Carl Harvey: $153','153.0','Closed Won','2017-06-01','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(166,'Carl Harvey: $89','89.0','Closed Won','2018-01-23','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(167,'Carl Harvey: $331','331.0','Closed Won','2018-01-26','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(168,'Carl Harvey: $842','842.0','Closed Won','2017-07-29','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(169,'Carl Harvey: $736','736.0','Closed Won','2016-09-14','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(170,'Carl Harvey: $978','978.0','Closed Won','2016-03-20','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(171,'Carl Harvey: $927','927.0','Closed Won','2017-04-24','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(172,'Carl Harvey: $321','321.0','Closed Won','2016-05-17','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(173,'Carl Harvey: $282','282.0','Closed Won','2016-02-04','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(174,'Carl Harvey: $750','750.0','Closed Won','2016-02-21','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(175,'Carl Harvey: $996','996.0','Closed Won','2016-06-16','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(176,'Carl Harvey: $536','536.0','Closed Won','2019-01-14','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(177,'Carl Harvey: $756','756.0','Closed Won','2018-11-10','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(178,'Carl Harvey: $119','119.0','Closed Won','2018-04-03','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(179,'Carl Harvey: $309','309.0','Closed Won','2016-11-01','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(180,'Carl Harvey: $230','230.0','Closed Won','2017-10-06','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(181,'Carl Harvey: $118','118.0','Closed Won','2017-04-18','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(182,'Carl Harvey: $327','327.0','Closed Won','2017-01-23','True','65','68','',''); +INSERT INTO "Opportunity" VALUES(183,'Carl Harvey: $557','557.0','Closed Won','2018-01-07','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(184,'Carl Harvey: $642','642.0','Closed Won','2018-02-08','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(185,'Carl Harvey: $782','782.0','Closed Won','2017-04-19','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(186,'Carl Harvey: $409','409.0','Closed Won','2017-05-21','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(187,'Carl Harvey: $501','501.0','Closed Won','2016-11-30','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(188,'Carl Harvey: $998','998.0','Closed Won','2017-08-18','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(189,'Carl Harvey: $338','338.0','Closed Won','2017-01-01','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(190,'Carl Harvey: $737','737.0','Closed Won','2018-12-15','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(191,'Carl Harvey: $410','410.0','Closed Won','2018-04-12','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(192,'Carl Harvey: $202','202.0','Closed Won','2018-10-16','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(193,'Carl Harvey: $997','997.0','Closed Won','2019-01-29','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(194,'Carl Harvey: $609','609.0','Closed Won','2016-06-09','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(195,'Carl Harvey: $952','952.0','Closed Won','2018-06-15','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(196,'Carl Harvey: $734','734.0','Closed Won','2017-02-01','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(197,'Carl Harvey: $540','540.0','Closed Won','2019-06-28','True','65','68','',''); +INSERT INTO "Opportunity" VALUES(198,'Carl Harvey: $507','507.0','Closed Won','2017-04-23','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(199,'Carl Harvey: $976','976.0','Closed Won','2017-11-07','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(200,'Carl Harvey: $973','973.0','Closed Won','2017-11-20','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(201,'Carl Harvey: $837','837.0','Closed Won','2018-03-22','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(202,'Carl Harvey: $871','871.0','Closed Won','2018-09-25','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(203,'Carl Harvey: $504','504.0','Closed Won','2017-06-15','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(204,'Carl Harvey: $234','234.0','Closed Won','2017-12-20','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(205,'Carl Harvey: $762','762.0','Closed Won','2016-03-31','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(206,'Carl Harvey: $357','357.0','Closed Won','2017-10-24','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(207,'Carl Harvey: $992','992.0','Closed Won','2018-02-28','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(208,'Carl Harvey: $666','666.0','Closed Won','2016-07-19','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(209,'Carl Harvey: $128','128.0','Closed Won','2017-04-22','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(210,'Carl Harvey: $111','111.0','Closed Won','2016-10-31','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(211,'Carl Harvey: $719','719.0','Closed Won','2016-06-10','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(212,'Carl Harvey: $22','22.0','Closed Won','2018-06-07','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(213,'Carl Harvey: $844','844.0','Closed Won','2017-05-30','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(214,'Carl Harvey: $989','989.0','Closed Won','2017-10-02','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(215,'Carl Harvey: $368','368.0','Closed Won','2017-09-04','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(216,'Carl Harvey: $896','896.0','Closed Won','2016-03-07','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(217,'Carl Harvey: $108','108.0','Closed Won','2018-07-01','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(218,'Carl Harvey: $911','911.0','Closed Won','2017-08-17','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(219,'Carl Harvey: $834','834.0','Closed Won','2017-04-08','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(220,'Carl Harvey: $45','45.0','Closed Won','2016-12-31','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(221,'Carl Harvey: $99','99.0','Closed Won','2017-11-25','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(222,'Carl Harvey: $971','971.0','Closed Won','2016-10-07','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(223,'Carl Harvey: $639','639.0','Closed Won','2018-08-19','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(224,'Carl Harvey: $919','919.0','Closed Won','2018-01-13','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(225,'Carl Harvey: $476','476.0','Closed Won','2016-03-22','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(226,'Carl Harvey: $321','321.0','Closed Won','2017-03-19','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(227,'Carl Harvey: $839','839.0','Closed Won','2018-11-11','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(228,'Carl Harvey: $451','451.0','Closed Won','2018-06-22','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(229,'Carl Harvey: $236','236.0','Closed Won','2019-01-18','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(230,'Carl Harvey: $585','585.0','Closed Won','2018-08-25','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(231,'Carl Harvey: $546','546.0','Closed Won','2017-08-29','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(232,'Carl Harvey: $485','485.0','Closed Won','2017-04-29','True','65','68','',''); +INSERT INTO "Opportunity" VALUES(233,'Carl Harvey: $329','329.0','Closed Won','2018-09-18','False','65','68','',''); +INSERT INTO "Opportunity" VALUES(234,'Carl Harvey Donation (1) 1/15/2019','75.0','Closed Won','2019-01-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(235,'Carl Harvey Donation (2) 2/15/2019','75.0','Closed Won','2019-02-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(236,'Carl Harvey Donation (3) 3/15/2019','75.0','Closed Won','2019-03-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(237,'Chapman - Soft Credit 2','4000.0','Closed Won','2021-11-12','False','93','9','',''); +INSERT INTO "Opportunity" VALUES(238,'Alice Patterson - Soft Credit 11','233.0','Closed Won','2021-10-12','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(239,'Alice Patterson - Soft Credit 12','290.0','Closed Won','2021-09-08','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(240,'Alice Patterson - Soft Credit 14','867.0','Closed Won','2021-09-16','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(241,'Chapman - Hard Credit 1','600.0','Closed Won','2021-11-12','False','93','9','',''); +INSERT INTO "Opportunity" VALUES(242,'Alice Patterson - Hard Credit 18','111.0','Closed Won','2021-12-04','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(243,'Alice Patterson - Non Closed/Won Opp 2','289.0','Proposal/Price Quote','2021-11-10','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(244,'Alice Patterson - Non Closed/Won Opp 4','55.0','Closed Lost','2021-11-26','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(245,'Alice Patterson - Non Closed/Won Opp 6','80.0','Prospecting','2021-11-09','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(246,'Patterson - Hard Credit 1','1000.0','Closed Won','2021-11-12','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(247,'Alice Patterson - Hard Credit 19','23.0','Closed Won','2021-11-10','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(248,'Alice Patterson - Hard Credit 20','77.0','Closed Won','2021-12-01','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(249,'Alice Patterson - Soft Credit 17','71.0','Closed Won','2021-09-03','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(250,'Alice Patterson - Soft Credit 15','46.0','Closed Won','2021-07-06','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(251,'Alice Patterson - Soft Credit 16','16.0','Closed Won','2021-09-01','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(252,'Patterson - Soft Credit 1','280.0','Closed Won','2021-11-17','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(253,'Alice Patterson - Soft Credit 20','20.0','Closed Won','2021-10-02','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(254,'Alice Patterson - Partial Soft Credit 8','655.0','Closed Won','2021-11-18','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(255,'Alice Patterson - Partial Soft Credit 11','1080.0','Closed Won','2021-11-11','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(256,'Alice Patterson - Hard Credit 9','69.0','Closed Won','2021-11-27','False','93','10','',''); +INSERT INTO "Opportunity" VALUES(257,'Alice Patterson - Hard Credit 11','66.0','Closed Won','2021-12-08','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(258,'Alice Patterson - Hard Credit 15','88.0','Closed Won','2021-12-03','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(259,'Alice Patterson - Soft Credit 1','433.0','Closed Won','2021-12-08','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(260,'Patterson - Partial Soft Credit 1','1050.0','Closed Won','2021-11-11','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(261,'Chapman - Soft Credit 1','190.0','Closed Won','2021-11-12','False','93','9','',''); +INSERT INTO "Opportunity" VALUES(262,'Chapman - Non-Closed Opp 1','45.0','Closed Lost','2021-11-12','False','93','9','',''); +INSERT INTO "Opportunity" VALUES(263,'Alice Patterson - Hard Credit 1','100.0','Closed Won','2021-11-11','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(264,'Alice Patterson - Partial Soft Credit 4','55.0','Closed Won','2021-11-27','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(265,'Alice Patterson - Partial Soft Credit 5','960.0','Closed Won','2021-11-27','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(266,'Alice Patterson - Soft Credit 4','321.0','Closed Won','2021-11-19','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(267,'Alice Patterson - Partial Soft Credit 1','350.0','Closed Won','2021-11-12','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(268,'Alice Patterson - Partial Soft Credit 12','498.0','Closed Won','2021-11-17','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(269,'Alice Patterson - Partial Soft Credit 13','111.0','Closed Won','2021-11-17','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(270,'Alice Patterson - Hard Credit 4','89.0','Closed Won','2021-11-26','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(271,'Alice Patterson - Hard Credit 8','34.0','Closed Won','2021-11-26','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(272,'Alice Patterson - Soft Credit 13','502.0','Closed Won','2021-08-04','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(273,'Alice Patterson - Partial Soft Credit 3','312.0','Closed Won','2021-07-03','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(274,'Alice Patterson - Partial Soft Credit 18','25.0','Closed Won','2021-11-25','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(275,'Alice Patterson - Partial Soft Credit 20','22.0','Closed Won','2021-12-02','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(276,'Alice Patterson - Non Closed/Won Opp 8','235.0','Closed Lost','2021-11-11','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(277,'Carl Harvey Donation (1) 1/15/2020','75.0','Pledged','2020-01-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(278,'Carl Harvey Donation (2) 2/15/2020','75.0','Pledged','2020-02-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(279,'Carl Harvey Donation (3) 3/15/2020','75.0','Pledged','2020-03-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(280,'Carl Harvey Donation (4) 4/15/2020','75.0','Pledged','2020-04-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(281,'Carl Harvey Donation (5) 5/15/2020','75.0','Pledged','2020-05-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(282,'Carl Harvey Donation (6) 6/15/2020','75.0','Pledged','2020-06-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(283,'Carl Harvey Donation (7) 7/15/2020','75.0','Pledged','2020-07-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(284,'Carl Harvey Donation (8) 8/15/2020','75.0','Pledged','2020-08-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(285,'Carl Harvey Donation (9) 9/15/2020','75.0','Pledged','2020-09-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(286,'Carl Harvey Donation (10) 10/15/2020','75.0','Pledged','2020-10-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(287,'Carl Harvey Donation (11) 11/15/2020','75.0','Pledged','2020-11-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(288,'Carl Harvey Donation (12) 12/15/2020','75.0','Pledged','2020-12-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(289,'Carl Harvey Donation (13) 1/15/2021','75.0','Pledged','2021-01-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(290,'Carl Harvey Donation (14) 2/15/2021','75.0','Pledged','2021-02-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(291,'Carl Harvey Donation (15) 3/15/2021','75.0','Pledged','2021-03-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(292,'Carl Harvey Donation (16) 4/15/2021','75.0','Pledged','2021-04-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(293,'Carl Harvey Donation (17) 5/15/2021','75.0','Pledged','2021-05-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(294,'Carl Harvey Donation (18) 6/15/2021','75.0','Pledged','2021-06-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(295,'Carl Harvey Donation (19) 7/15/2021','75.0','Pledged','2021-07-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(296,'Carl Harvey Donation (20) 8/15/2021','75.0','Pledged','2021-08-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(297,'Carl Harvey Donation (21) 9/15/2021','75.0','Pledged','2021-09-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(298,'Carl Harvey Donation (22) 10/15/2021','75.0','Pledged','2021-10-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(299,'Carl Harvey Donation (23) 11/15/2021','75.0','Pledged','2021-11-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(300,'Carl Harvey Donation (24) 12/15/2021','75.0','Pledged','2021-12-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(301,'Carl Harvey Donation (25) 1/15/2022','75.0','Pledged','2022-01-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(302,'Carl Harvey Donation (26) 2/15/2022','75.0','Pledged','2022-02-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(303,'Carl Harvey Donation (27) 3/15/2022','75.0','Pledged','2022-03-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(304,'Carl Harvey Donation (28) 4/15/2022','75.0','Pledged','2022-04-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(305,'Carl Harvey Donation (29) 5/15/2022','75.0','Pledged','2022-05-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(306,'Carl Harvey Donation (30) 6/15/2022','75.0','Pledged','2022-06-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(307,'Carl Harvey Donation (31) 7/15/2022','75.0','Pledged','2022-07-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(308,'Carl Harvey Donation (32) 8/15/2022','75.0','Pledged','2022-08-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(309,'Carl Harvey Donation (33) 9/15/2022','75.0','Pledged','2022-09-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(310,'Carl Harvey Donation (34) 10/15/2022','75.0','Pledged','2022-10-15','False','65','68','1',''); +INSERT INTO "Opportunity" VALUES(311,'Chapman - Partial Soft Credit 2','675.0','Closed Won','2021-11-03','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(312,'Alice Patterson - Non Closed/Won Opp 1','100.0','Closed Lost','2021-11-11','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(313,'Alice Patterson - Soft Credit 6','70.0','Closed Won','2021-12-31','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(314,'Alice Patterson - Partial Soft Credit 14','534.0','Closed Won','2021-11-20','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(315,'Alice Patterson - Non Closed/Won Opp 9','568.0','Qualification','2021-11-02','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(316,'Alice Patterson - Soft Credit 5','785.0','Closed Won','2021-11-30','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(317,'Alice Patterson - Non Closed/Won Opp 3','60.0','Needs Analysis','2021-11-11','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(318,'Alice Patterson - Hard Credit 5','509.0','Closed Won','2021-10-31','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(319,'Alice Patterson - Hard Credit 13','11.0','Closed Won','2021-11-19','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(320,'Chapman - Hard Credit 2','680.0','Closed Won','2021-11-10','False','93','39','',''); +INSERT INTO "Opportunity" VALUES(321,'Alice Patterson - Partial Soft Credit 17','2345.0','Closed Won','2021-12-11','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(322,'Alice Patterson - Partial Soft Credit 10','156.0','Closed Won','2021-09-28','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(323,'Alice Patterson - Soft Credit 9','88.0','Closed Won','2021-12-11','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(324,'Alice Patterson - Soft Credit 3','123.0','Closed Won','2021-11-18','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(325,'Patterson - Hard Credit 2','120.0','Closed Won','2021-11-25','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(326,'Chapman - Non-Closed Opp 2','150.0','Needs Analysis','2021-11-11','False','93','9','',''); +INSERT INTO "Opportunity" VALUES(327,'Patterson - Non-Closed Opp 2','400.0','Needs Analysis','2021-11-26','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(328,'Alice Patterson - Soft Credit 8','56.0','Closed Won','2021-12-13','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(329,'Alice Patterson - Hard Credit 6','700.0','Closed Won','2021-11-26','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(330,'Alice Patterson - Hard Credit 14','14.0','Closed Won','2021-10-31','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(331,'Alice Patterson - Hard Credit 10','99.0','Closed Won','2021-11-03','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(332,'Alice Patterson - Soft Credit 2','89.0','Closed Won','2021-11-07','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(333,'Alice Patterson - Soft Credit 18','445.0','Closed Won','2021-09-14','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(334,'Alice Patterson - Partial Soft Credit 16','667.0','Closed Won','2021-10-19','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(335,'Chapman - Partial Soft Credit 1','500.0','Closed Won','2021-11-24','False','93','9','',''); +INSERT INTO "Opportunity" VALUES(336,'Alice Patterson - Partial Soft Credit 15','434.0','Closed Won','2021-11-22','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(337,'Alice Patterson - Hard Credit 7','45.0','Closed Won','2021-11-17','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(338,'Alice Patterson - Hard Credit 16','73.0','Closed Won','2021-11-12','False','60','10','',''); +INSERT INTO "Opportunity" VALUES(339,'Alice Patterson - Partial Soft Credit 9','77.0','Closed Won','2021-09-30','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(340,'Alice Patterson - Partial Soft Credit 19','508.0','Closed Won','2021-08-30','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(341,'Alice Patterson - Non Closed/Won Opp 5','400.0','Closed Lost','2021-11-12','False','60','1','',''); +INSERT INTO "Opportunity" VALUES(342,'Alice Patterson - Soft Credit 10','444.0','Closed Won','2021-12-25','False','60','1','',''); CREATE TABLE "OpportunityContactRole" ( - id INTEGER NOT NULL, - "Role" VARCHAR(255), - "IsPrimary" VARCHAR(255), - contact_id VARCHAR(255), - opportunity_id VARCHAR(255), + id INTEGER NOT NULL, + "Role" VARCHAR(255), + "IsPrimary" VARCHAR(255), + "ContactId" VARCHAR(255), + "OpportunityId" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "OpportunityContactRole" VALUES(1,'Soft Credit','true','66','211'); -INSERT INTO "OpportunityContactRole" VALUES(2,'Soft Credit','true','66','212'); -INSERT INTO "OpportunityContactRole" VALUES(3,'Soft Credit','true','66','213'); -INSERT INTO "OpportunityContactRole" VALUES(4,'Soft Credit','true','66','214'); -INSERT INTO "OpportunityContactRole" VALUES(5,'Soft Credit','true','66','219'); -INSERT INTO "OpportunityContactRole" VALUES(6,'Soft Credit','true','66','220'); -INSERT INTO "OpportunityContactRole" VALUES(7,'Soft Credit','true','66','221'); -INSERT INTO "OpportunityContactRole" VALUES(8,'Soft Credit','true','66','215'); -INSERT INTO "OpportunityContactRole" VALUES(9,'Soft Credit','true','66','216'); -INSERT INTO "OpportunityContactRole" VALUES(10,'Soft Credit','true','66','217'); -INSERT INTO "OpportunityContactRole" VALUES(11,'Soft Credit','true','66','218'); -INSERT INTO "OpportunityContactRole" VALUES(12,'Donor','true','66','222'); -INSERT INTO "OpportunityContactRole" VALUES(13,'Donor','true','1','3'); -INSERT INTO "OpportunityContactRole" VALUES(14,'Donor','true','1','4'); -INSERT INTO "OpportunityContactRole" VALUES(15,'Donor','true','1','5'); -INSERT INTO "OpportunityContactRole" VALUES(16,'Donor','true','1','6'); -INSERT INTO "OpportunityContactRole" VALUES(17,'Donor','true','1','7'); -INSERT INTO "OpportunityContactRole" VALUES(18,'Donor','true','1','8'); -INSERT INTO "OpportunityContactRole" VALUES(19,'Donor','true','1','9'); -INSERT INTO "OpportunityContactRole" VALUES(20,'Donor','true','1','10'); -INSERT INTO "OpportunityContactRole" VALUES(21,'Donor','true','1','11'); -INSERT INTO "OpportunityContactRole" VALUES(22,'Donor','true','1','12'); -INSERT INTO "OpportunityContactRole" VALUES(23,'Soft Credit','false','1','94'); -INSERT INTO "OpportunityContactRole" VALUES(24,'Influencer','false','100','124'); -INSERT INTO "OpportunityContactRole" VALUES(25,'Soft Credit','false','100','94'); -INSERT INTO "OpportunityContactRole" VALUES(26,'Influencer','false','5','204'); -INSERT INTO "OpportunityContactRole" VALUES(27,'Soft Credit','false','5','94'); -INSERT INTO "OpportunityContactRole" VALUES(28,'Influencer','false','2','164'); -INSERT INTO "OpportunityContactRole" VALUES(29,'Soft Credit','false','2','94'); -INSERT INTO "OpportunityContactRole" VALUES(30,'Donor','true','3','31'); -INSERT INTO "OpportunityContactRole" VALUES(31,'Donor','true','3','32'); -INSERT INTO "OpportunityContactRole" VALUES(32,'Influencer','false','3','164'); -INSERT INTO "OpportunityContactRole" VALUES(33,'Donor','true','3','23'); -INSERT INTO "OpportunityContactRole" VALUES(34,'Donor','true','3','24'); -INSERT INTO "OpportunityContactRole" VALUES(35,'Donor','true','3','25'); -INSERT INTO "OpportunityContactRole" VALUES(36,'Donor','true','3','26'); -INSERT INTO "OpportunityContactRole" VALUES(37,'Donor','true','3','27'); -INSERT INTO "OpportunityContactRole" VALUES(38,'Donor','true','3','28'); -INSERT INTO "OpportunityContactRole" VALUES(39,'Soft Credit','false','3','94'); -INSERT INTO "OpportunityContactRole" VALUES(40,'Donor','true','3','29'); -INSERT INTO "OpportunityContactRole" VALUES(41,'Donor','true','3','30'); -INSERT INTO "OpportunityContactRole" VALUES(42,'Influencer','false','4','199'); -INSERT INTO "OpportunityContactRole" VALUES(43,'Soft Credit','false','4','94'); -INSERT INTO "OpportunityContactRole" VALUES(44,'Influencer','false','6','123'); -INSERT INTO "OpportunityContactRole" VALUES(45,'Soft Credit','false','6','94'); -INSERT INTO "OpportunityContactRole" VALUES(46,'Influencer','false','7','164'); -INSERT INTO "OpportunityContactRole" VALUES(47,'Soft Credit','false','7','94'); -INSERT INTO "OpportunityContactRole" VALUES(48,'Soft Credit','false','8','94'); -INSERT INTO "OpportunityContactRole" VALUES(49,'Household Member','false','8','43'); -INSERT INTO "OpportunityContactRole" VALUES(50,'Household Member','false','8','44'); -INSERT INTO "OpportunityContactRole" VALUES(51,'Household Member','false','8','45'); -INSERT INTO "OpportunityContactRole" VALUES(52,'Household Member','false','8','46'); -INSERT INTO "OpportunityContactRole" VALUES(53,'Household Member','false','8','47'); -INSERT INTO "OpportunityContactRole" VALUES(54,'Household Member','false','8','48'); -INSERT INTO "OpportunityContactRole" VALUES(55,'Household Member','false','8','49'); -INSERT INTO "OpportunityContactRole" VALUES(56,'Household Member','false','8','50'); -INSERT INTO "OpportunityContactRole" VALUES(57,'Household Member','false','8','51'); -INSERT INTO "OpportunityContactRole" VALUES(58,'Household Member','false','8','52'); -INSERT INTO "OpportunityContactRole" VALUES(59,'Soft Credit','false','9','94'); -INSERT INTO "OpportunityContactRole" VALUES(60,'Donor','true','9','43'); -INSERT INTO "OpportunityContactRole" VALUES(61,'Donor','true','9','44'); -INSERT INTO "OpportunityContactRole" VALUES(62,'Donor','true','9','45'); -INSERT INTO "OpportunityContactRole" VALUES(63,'Donor','true','9','46'); -INSERT INTO "OpportunityContactRole" VALUES(64,'Donor','true','9','47'); -INSERT INTO "OpportunityContactRole" VALUES(65,'Donor','true','9','48'); -INSERT INTO "OpportunityContactRole" VALUES(66,'Donor','true','9','49'); -INSERT INTO "OpportunityContactRole" VALUES(67,'Donor','true','9','50'); -INSERT INTO "OpportunityContactRole" VALUES(68,'Donor','true','9','51'); -INSERT INTO "OpportunityContactRole" VALUES(69,'Donor','true','9','52'); -INSERT INTO "OpportunityContactRole" VALUES(70,'Influencer','false','10','13'); -INSERT INTO "OpportunityContactRole" VALUES(71,'Soft Credit','false','10','94'); -INSERT INTO "OpportunityContactRole" VALUES(72,'Influencer','false','11','149'); -INSERT INTO "OpportunityContactRole" VALUES(73,'Soft Credit','false','11','94'); -INSERT INTO "OpportunityContactRole" VALUES(74,'Influencer','false','14','123'); -INSERT INTO "OpportunityContactRole" VALUES(75,'Soft Credit','false','14','94'); -INSERT INTO "OpportunityContactRole" VALUES(76,'Influencer','false','12','164'); -INSERT INTO "OpportunityContactRole" VALUES(77,'Soft Credit','false','12','94'); -INSERT INTO "OpportunityContactRole" VALUES(78,'Influencer','false','13','199'); -INSERT INTO "OpportunityContactRole" VALUES(79,'Soft Credit','false','13','94'); -INSERT INTO "OpportunityContactRole" VALUES(80,'Influencer','false','15','124'); -INSERT INTO "OpportunityContactRole" VALUES(81,'Soft Credit','false','15','94'); -INSERT INTO "OpportunityContactRole" VALUES(82,'Influencer','false','16','164'); -INSERT INTO "OpportunityContactRole" VALUES(83,'Soft Credit','false','16','94'); -INSERT INTO "OpportunityContactRole" VALUES(84,'Influencer','false','17','149'); -INSERT INTO "OpportunityContactRole" VALUES(85,'Soft Credit','false','17','94'); -INSERT INTO "OpportunityContactRole" VALUES(86,'Influencer','false','18','19'); -INSERT INTO "OpportunityContactRole" VALUES(87,'Soft Credit','false','18','94'); -INSERT INTO "OpportunityContactRole" VALUES(88,'Influencer','false','19','13'); -INSERT INTO "OpportunityContactRole" VALUES(89,'Soft Credit','false','19','94'); -INSERT INTO "OpportunityContactRole" VALUES(90,'Influencer','false','20','13'); -INSERT INTO "OpportunityContactRole" VALUES(91,'Soft Credit','false','20','94'); -INSERT INTO "OpportunityContactRole" VALUES(92,'Influencer','false','21','204'); -INSERT INTO "OpportunityContactRole" VALUES(93,'Soft Credit','false','21','94'); -INSERT INTO "OpportunityContactRole" VALUES(94,'Donor','true','21','53'); -INSERT INTO "OpportunityContactRole" VALUES(95,'Donor','true','21','54'); -INSERT INTO "OpportunityContactRole" VALUES(96,'Donor','true','21','55'); -INSERT INTO "OpportunityContactRole" VALUES(97,'Donor','true','21','56'); -INSERT INTO "OpportunityContactRole" VALUES(98,'Donor','true','21','57'); -INSERT INTO "OpportunityContactRole" VALUES(99,'Donor','true','21','58'); -INSERT INTO "OpportunityContactRole" VALUES(100,'Donor','true','21','59'); -INSERT INTO "OpportunityContactRole" VALUES(101,'Donor','true','21','60'); -INSERT INTO "OpportunityContactRole" VALUES(102,'Donor','true','21','61'); -INSERT INTO "OpportunityContactRole" VALUES(103,'Donor','true','21','62'); -INSERT INTO "OpportunityContactRole" VALUES(104,'Influencer','false','26','164'); -INSERT INTO "OpportunityContactRole" VALUES(105,'Soft Credit','false','26','94'); -INSERT INTO "OpportunityContactRole" VALUES(106,'Influencer','false','25','204'); -INSERT INTO "OpportunityContactRole" VALUES(107,'Soft Credit','false','25','94'); -INSERT INTO "OpportunityContactRole" VALUES(108,'Influencer','false','27','208'); -INSERT INTO "OpportunityContactRole" VALUES(109,'Soft Credit','false','27','94'); -INSERT INTO "OpportunityContactRole" VALUES(110,'Influencer','false','28','124'); -INSERT INTO "OpportunityContactRole" VALUES(111,'Soft Credit','false','28','94'); -INSERT INTO "OpportunityContactRole" VALUES(112,'Influencer','false','29','208'); -INSERT INTO "OpportunityContactRole" VALUES(113,'Soft Credit','false','29','94'); -INSERT INTO "OpportunityContactRole" VALUES(114,'Influencer','false','30','13'); -INSERT INTO "OpportunityContactRole" VALUES(115,'Soft Credit','false','30','94'); -INSERT INTO "OpportunityContactRole" VALUES(116,'Donor','true','30','63'); -INSERT INTO "OpportunityContactRole" VALUES(117,'Donor','true','30','64'); -INSERT INTO "OpportunityContactRole" VALUES(118,'Donor','true','30','1'); -INSERT INTO "OpportunityContactRole" VALUES(119,'Donor','true','30','2'); -INSERT INTO "OpportunityContactRole" VALUES(120,'Donor','true','30','65'); -INSERT INTO "OpportunityContactRole" VALUES(121,'Donor','true','30','66'); -INSERT INTO "OpportunityContactRole" VALUES(122,'Donor','true','30','67'); -INSERT INTO "OpportunityContactRole" VALUES(123,'Donor','true','30','70'); -INSERT INTO "OpportunityContactRole" VALUES(124,'Donor','true','30','71'); -INSERT INTO "OpportunityContactRole" VALUES(125,'Donor','true','30','72'); -INSERT INTO "OpportunityContactRole" VALUES(126,'Influencer','false','31','164'); -INSERT INTO "OpportunityContactRole" VALUES(127,'Soft Credit','false','31','94'); -INSERT INTO "OpportunityContactRole" VALUES(128,'Influencer','false','32','124'); -INSERT INTO "OpportunityContactRole" VALUES(129,'Soft Credit','false','32','94'); -INSERT INTO "OpportunityContactRole" VALUES(130,'Influencer','false','33','123'); -INSERT INTO "OpportunityContactRole" VALUES(131,'Soft Credit','false','33','94'); -INSERT INTO "OpportunityContactRole" VALUES(132,'Soft Credit','false','34','94'); -INSERT INTO "OpportunityContactRole" VALUES(133,'Influencer','false','35','149'); -INSERT INTO "OpportunityContactRole" VALUES(134,'Soft Credit','false','35','94'); -INSERT INTO "OpportunityContactRole" VALUES(135,'Influencer','false','36','149'); -INSERT INTO "OpportunityContactRole" VALUES(136,'Soft Credit','false','36','94'); -INSERT INTO "OpportunityContactRole" VALUES(137,'Influencer','false','37','19'); -INSERT INTO "OpportunityContactRole" VALUES(138,'Soft Credit','false','37','94'); -INSERT INTO "OpportunityContactRole" VALUES(139,'Influencer','false','38','13'); -INSERT INTO "OpportunityContactRole" VALUES(140,'Soft Credit','false','38','94'); -INSERT INTO "OpportunityContactRole" VALUES(141,'Influencer','false','39','204'); -INSERT INTO "OpportunityContactRole" VALUES(142,'Soft Credit','false','39','94'); -INSERT INTO "OpportunityContactRole" VALUES(143,'Influencer','false','40','124'); -INSERT INTO "OpportunityContactRole" VALUES(144,'Soft Credit','false','40','94'); -INSERT INTO "OpportunityContactRole" VALUES(145,'Influencer','false','41','164'); -INSERT INTO "OpportunityContactRole" VALUES(146,'Soft Credit','false','41','94'); -INSERT INTO "OpportunityContactRole" VALUES(147,'Donor','true','42','81'); -INSERT INTO "OpportunityContactRole" VALUES(148,'Donor','true','42','82'); -INSERT INTO "OpportunityContactRole" VALUES(149,'Influencer','false','42','123'); -INSERT INTO "OpportunityContactRole" VALUES(150,'Soft Credit','false','42','94'); -INSERT INTO "OpportunityContactRole" VALUES(151,'Donor','true','42','73'); -INSERT INTO "OpportunityContactRole" VALUES(152,'Donor','true','42','74'); -INSERT INTO "OpportunityContactRole" VALUES(153,'Donor','true','42','75'); -INSERT INTO "OpportunityContactRole" VALUES(154,'Donor','true','42','76'); -INSERT INTO "OpportunityContactRole" VALUES(155,'Donor','true','42','77'); -INSERT INTO "OpportunityContactRole" VALUES(156,'Donor','true','42','78'); -INSERT INTO "OpportunityContactRole" VALUES(157,'Donor','true','42','79'); -INSERT INTO "OpportunityContactRole" VALUES(158,'Donor','true','42','80'); -INSERT INTO "OpportunityContactRole" VALUES(159,'Household Member','false','43','81'); -INSERT INTO "OpportunityContactRole" VALUES(160,'Household Member','false','43','82'); -INSERT INTO "OpportunityContactRole" VALUES(161,'Influencer','false','43','123'); -INSERT INTO "OpportunityContactRole" VALUES(162,'Soft Credit','false','43','94'); -INSERT INTO "OpportunityContactRole" VALUES(163,'Household Member','false','43','73'); -INSERT INTO "OpportunityContactRole" VALUES(164,'Household Member','false','43','74'); -INSERT INTO "OpportunityContactRole" VALUES(165,'Household Member','false','43','75'); -INSERT INTO "OpportunityContactRole" VALUES(166,'Household Member','false','43','76'); -INSERT INTO "OpportunityContactRole" VALUES(167,'Household Member','false','43','77'); -INSERT INTO "OpportunityContactRole" VALUES(168,'Household Member','false','43','78'); -INSERT INTO "OpportunityContactRole" VALUES(169,'Household Member','false','43','79'); -INSERT INTO "OpportunityContactRole" VALUES(170,'Household Member','false','43','80'); -INSERT INTO "OpportunityContactRole" VALUES(171,'Household Member','false','44','81'); -INSERT INTO "OpportunityContactRole" VALUES(172,'Household Member','false','44','82'); -INSERT INTO "OpportunityContactRole" VALUES(173,'Influencer','false','44','204'); -INSERT INTO "OpportunityContactRole" VALUES(174,'Soft Credit','false','44','94'); -INSERT INTO "OpportunityContactRole" VALUES(175,'Household Member','false','44','73'); -INSERT INTO "OpportunityContactRole" VALUES(176,'Household Member','false','44','74'); -INSERT INTO "OpportunityContactRole" VALUES(177,'Household Member','false','44','75'); -INSERT INTO "OpportunityContactRole" VALUES(178,'Household Member','false','44','76'); -INSERT INTO "OpportunityContactRole" VALUES(179,'Household Member','false','44','77'); -INSERT INTO "OpportunityContactRole" VALUES(180,'Household Member','false','44','78'); -INSERT INTO "OpportunityContactRole" VALUES(181,'Household Member','false','44','79'); -INSERT INTO "OpportunityContactRole" VALUES(182,'Household Member','false','44','80'); -INSERT INTO "OpportunityContactRole" VALUES(183,'Household Member','false','45','81'); -INSERT INTO "OpportunityContactRole" VALUES(184,'Household Member','false','45','82'); -INSERT INTO "OpportunityContactRole" VALUES(185,'Influencer','false','45','124'); -INSERT INTO "OpportunityContactRole" VALUES(186,'Soft Credit','false','45','94'); -INSERT INTO "OpportunityContactRole" VALUES(187,'Household Member','false','45','73'); -INSERT INTO "OpportunityContactRole" VALUES(188,'Household Member','false','45','74'); -INSERT INTO "OpportunityContactRole" VALUES(189,'Household Member','false','45','75'); -INSERT INTO "OpportunityContactRole" VALUES(190,'Household Member','false','45','76'); -INSERT INTO "OpportunityContactRole" VALUES(191,'Household Member','false','45','77'); -INSERT INTO "OpportunityContactRole" VALUES(192,'Household Member','false','45','78'); -INSERT INTO "OpportunityContactRole" VALUES(193,'Household Member','false','45','79'); -INSERT INTO "OpportunityContactRole" VALUES(194,'Household Member','false','45','80'); -INSERT INTO "OpportunityContactRole" VALUES(195,'Household Member','false','46','81'); -INSERT INTO "OpportunityContactRole" VALUES(196,'Household Member','false','46','82'); -INSERT INTO "OpportunityContactRole" VALUES(197,'Influencer','false','46','149'); -INSERT INTO "OpportunityContactRole" VALUES(198,'Soft Credit','false','46','94'); -INSERT INTO "OpportunityContactRole" VALUES(199,'Household Member','false','46','73'); -INSERT INTO "OpportunityContactRole" VALUES(200,'Household Member','false','46','74'); -INSERT INTO "OpportunityContactRole" VALUES(201,'Household Member','false','46','75'); -INSERT INTO "OpportunityContactRole" VALUES(202,'Household Member','false','46','76'); -INSERT INTO "OpportunityContactRole" VALUES(203,'Household Member','false','46','77'); -INSERT INTO "OpportunityContactRole" VALUES(204,'Household Member','false','46','78'); -INSERT INTO "OpportunityContactRole" VALUES(205,'Household Member','false','46','79'); -INSERT INTO "OpportunityContactRole" VALUES(206,'Household Member','false','46','80'); -INSERT INTO "OpportunityContactRole" VALUES(207,'Household Member','false','47','81'); -INSERT INTO "OpportunityContactRole" VALUES(208,'Household Member','false','47','82'); -INSERT INTO "OpportunityContactRole" VALUES(209,'Influencer','false','47','149'); -INSERT INTO "OpportunityContactRole" VALUES(210,'Soft Credit','false','47','94'); -INSERT INTO "OpportunityContactRole" VALUES(211,'Household Member','false','47','73'); -INSERT INTO "OpportunityContactRole" VALUES(212,'Household Member','false','47','74'); -INSERT INTO "OpportunityContactRole" VALUES(213,'Household Member','false','47','75'); -INSERT INTO "OpportunityContactRole" VALUES(214,'Household Member','false','47','76'); -INSERT INTO "OpportunityContactRole" VALUES(215,'Household Member','false','47','77'); -INSERT INTO "OpportunityContactRole" VALUES(216,'Household Member','false','47','78'); -INSERT INTO "OpportunityContactRole" VALUES(217,'Household Member','false','47','79'); -INSERT INTO "OpportunityContactRole" VALUES(218,'Household Member','false','47','80'); -INSERT INTO "OpportunityContactRole" VALUES(219,'Influencer','false','57','208'); -INSERT INTO "OpportunityContactRole" VALUES(220,'Soft Credit','false','57','94'); -INSERT INTO "OpportunityContactRole" VALUES(221,'Household Member','false','48','81'); -INSERT INTO "OpportunityContactRole" VALUES(222,'Household Member','false','48','82'); -INSERT INTO "OpportunityContactRole" VALUES(223,'Influencer','false','48','19'); -INSERT INTO "OpportunityContactRole" VALUES(224,'Soft Credit','false','48','94'); -INSERT INTO "OpportunityContactRole" VALUES(225,'Household Member','false','48','73'); -INSERT INTO "OpportunityContactRole" VALUES(226,'Household Member','false','48','74'); -INSERT INTO "OpportunityContactRole" VALUES(227,'Household Member','false','48','75'); -INSERT INTO "OpportunityContactRole" VALUES(228,'Household Member','false','48','76'); -INSERT INTO "OpportunityContactRole" VALUES(229,'Household Member','false','48','77'); -INSERT INTO "OpportunityContactRole" VALUES(230,'Household Member','false','48','78'); -INSERT INTO "OpportunityContactRole" VALUES(231,'Household Member','false','48','79'); -INSERT INTO "OpportunityContactRole" VALUES(232,'Household Member','false','48','80'); -INSERT INTO "OpportunityContactRole" VALUES(233,'Influencer','false','22','13'); -INSERT INTO "OpportunityContactRole" VALUES(234,'Soft Credit','false','22','94'); -INSERT INTO "OpportunityContactRole" VALUES(235,'Influencer','false','23','149'); -INSERT INTO "OpportunityContactRole" VALUES(236,'Soft Credit','false','23','94'); -INSERT INTO "OpportunityContactRole" VALUES(237,'Soft Credit','false','24','94'); -INSERT INTO "OpportunityContactRole" VALUES(238,'Household Member','false','49','81'); -INSERT INTO "OpportunityContactRole" VALUES(239,'Household Member','false','49','82'); -INSERT INTO "OpportunityContactRole" VALUES(240,'Influencer','false','49','19'); -INSERT INTO "OpportunityContactRole" VALUES(241,'Soft Credit','false','49','94'); -INSERT INTO "OpportunityContactRole" VALUES(242,'Household Member','false','49','73'); -INSERT INTO "OpportunityContactRole" VALUES(243,'Household Member','false','49','74'); -INSERT INTO "OpportunityContactRole" VALUES(244,'Household Member','false','49','75'); -INSERT INTO "OpportunityContactRole" VALUES(245,'Household Member','false','49','76'); -INSERT INTO "OpportunityContactRole" VALUES(246,'Household Member','false','49','77'); -INSERT INTO "OpportunityContactRole" VALUES(247,'Household Member','false','49','78'); -INSERT INTO "OpportunityContactRole" VALUES(248,'Household Member','false','49','79'); -INSERT INTO "OpportunityContactRole" VALUES(249,'Household Member','false','49','80'); -INSERT INTO "OpportunityContactRole" VALUES(250,'Household Member','false','50','81'); -INSERT INTO "OpportunityContactRole" VALUES(251,'Household Member','false','50','82'); -INSERT INTO "OpportunityContactRole" VALUES(252,'Influencer','false','50','19'); -INSERT INTO "OpportunityContactRole" VALUES(253,'Soft Credit','false','50','94'); -INSERT INTO "OpportunityContactRole" VALUES(254,'Household Member','false','50','73'); -INSERT INTO "OpportunityContactRole" VALUES(255,'Household Member','false','50','74'); -INSERT INTO "OpportunityContactRole" VALUES(256,'Household Member','false','50','75'); -INSERT INTO "OpportunityContactRole" VALUES(257,'Household Member','false','50','76'); -INSERT INTO "OpportunityContactRole" VALUES(258,'Household Member','false','50','77'); -INSERT INTO "OpportunityContactRole" VALUES(259,'Household Member','false','50','78'); -INSERT INTO "OpportunityContactRole" VALUES(260,'Household Member','false','50','79'); -INSERT INTO "OpportunityContactRole" VALUES(261,'Household Member','false','50','80'); -INSERT INTO "OpportunityContactRole" VALUES(262,'Household Member','false','51','81'); -INSERT INTO "OpportunityContactRole" VALUES(263,'Household Member','false','51','82'); -INSERT INTO "OpportunityContactRole" VALUES(264,'Influencer','false','51','19'); -INSERT INTO "OpportunityContactRole" VALUES(265,'Soft Credit','false','51','94'); -INSERT INTO "OpportunityContactRole" VALUES(266,'Household Member','false','51','73'); -INSERT INTO "OpportunityContactRole" VALUES(267,'Household Member','false','51','74'); -INSERT INTO "OpportunityContactRole" VALUES(268,'Household Member','false','51','75'); -INSERT INTO "OpportunityContactRole" VALUES(269,'Household Member','false','51','76'); -INSERT INTO "OpportunityContactRole" VALUES(270,'Household Member','false','51','77'); -INSERT INTO "OpportunityContactRole" VALUES(271,'Household Member','false','51','78'); -INSERT INTO "OpportunityContactRole" VALUES(272,'Household Member','false','51','79'); -INSERT INTO "OpportunityContactRole" VALUES(273,'Household Member','false','51','80'); -INSERT INTO "OpportunityContactRole" VALUES(274,'Soft Credit','false','52','94'); -INSERT INTO "OpportunityContactRole" VALUES(275,'Influencer','false','53','123'); -INSERT INTO "OpportunityContactRole" VALUES(276,'Soft Credit','false','53','94'); -INSERT INTO "OpportunityContactRole" VALUES(277,'Influencer','false','54','208'); -INSERT INTO "OpportunityContactRole" VALUES(278,'Soft Credit','false','54','94'); -INSERT INTO "OpportunityContactRole" VALUES(279,'Donor','true','66','128'); -INSERT INTO "OpportunityContactRole" VALUES(280,'Donor','true','66','129'); -INSERT INTO "OpportunityContactRole" VALUES(281,'Donor','true','66','130'); -INSERT INTO "OpportunityContactRole" VALUES(282,'Donor','true','66','131'); -INSERT INTO "OpportunityContactRole" VALUES(283,'Donor','true','66','132'); -INSERT INTO "OpportunityContactRole" VALUES(284,'Donor','true','66','133'); -INSERT INTO "OpportunityContactRole" VALUES(285,'Donor','true','66','134'); -INSERT INTO "OpportunityContactRole" VALUES(286,'Influencer','false','66','204'); -INSERT INTO "OpportunityContactRole" VALUES(287,'Donor','true','66','135'); -INSERT INTO "OpportunityContactRole" VALUES(288,'Donor','true','66','136'); -INSERT INTO "OpportunityContactRole" VALUES(289,'Donor','true','66','137'); -INSERT INTO "OpportunityContactRole" VALUES(290,'Donor','true','66','138'); -INSERT INTO "OpportunityContactRole" VALUES(291,'Donor','true','66','139'); -INSERT INTO "OpportunityContactRole" VALUES(292,'Donor','true','66','140'); -INSERT INTO "OpportunityContactRole" VALUES(293,'Donor','true','66','141'); -INSERT INTO "OpportunityContactRole" VALUES(294,'Donor','true','66','142'); -INSERT INTO "OpportunityContactRole" VALUES(295,'Donor','true','66','143'); -INSERT INTO "OpportunityContactRole" VALUES(296,'Donor','true','66','144'); -INSERT INTO "OpportunityContactRole" VALUES(297,'Donor','true','66','145'); -INSERT INTO "OpportunityContactRole" VALUES(298,'Donor','true','66','146'); -INSERT INTO "OpportunityContactRole" VALUES(299,'Donor','true','66','147'); -INSERT INTO "OpportunityContactRole" VALUES(300,'Donor','true','66','148'); -INSERT INTO "OpportunityContactRole" VALUES(301,'Donor','true','66','149'); -INSERT INTO "OpportunityContactRole" VALUES(302,'Donor','true','66','150'); -INSERT INTO "OpportunityContactRole" VALUES(303,'Donor','true','66','151'); -INSERT INTO "OpportunityContactRole" VALUES(304,'Donor','true','66','152'); -INSERT INTO "OpportunityContactRole" VALUES(305,'Donor','true','66','153'); -INSERT INTO "OpportunityContactRole" VALUES(306,'Donor','true','66','154'); -INSERT INTO "OpportunityContactRole" VALUES(307,'Donor','true','66','155'); -INSERT INTO "OpportunityContactRole" VALUES(308,'Donor','true','66','156'); -INSERT INTO "OpportunityContactRole" VALUES(309,'Donor','true','66','157'); -INSERT INTO "OpportunityContactRole" VALUES(310,'Donor','true','66','158'); -INSERT INTO "OpportunityContactRole" VALUES(311,'Donor','true','66','159'); -INSERT INTO "OpportunityContactRole" VALUES(312,'Donor','true','66','160'); -INSERT INTO "OpportunityContactRole" VALUES(313,'Donor','true','66','161'); -INSERT INTO "OpportunityContactRole" VALUES(314,'Donor','true','66','162'); -INSERT INTO "OpportunityContactRole" VALUES(315,'Donor','true','66','163'); -INSERT INTO "OpportunityContactRole" VALUES(316,'Donor','true','66','164'); -INSERT INTO "OpportunityContactRole" VALUES(317,'Donor','true','66','165'); -INSERT INTO "OpportunityContactRole" VALUES(318,'Donor','true','66','166'); -INSERT INTO "OpportunityContactRole" VALUES(319,'Donor','true','66','167'); -INSERT INTO "OpportunityContactRole" VALUES(320,'Donor','true','66','168'); -INSERT INTO "OpportunityContactRole" VALUES(321,'Donor','true','66','169'); -INSERT INTO "OpportunityContactRole" VALUES(322,'Donor','true','66','194'); -INSERT INTO "OpportunityContactRole" VALUES(323,'Donor','true','66','195'); -INSERT INTO "OpportunityContactRole" VALUES(324,'Donor','true','66','196'); -INSERT INTO "OpportunityContactRole" VALUES(325,'Donor','true','66','170'); -INSERT INTO "OpportunityContactRole" VALUES(326,'Donor','true','66','171'); -INSERT INTO "OpportunityContactRole" VALUES(327,'Donor','true','66','172'); -INSERT INTO "OpportunityContactRole" VALUES(328,'Donor','true','66','173'); -INSERT INTO "OpportunityContactRole" VALUES(329,'Donor','true','66','174'); -INSERT INTO "OpportunityContactRole" VALUES(330,'Donor','true','66','175'); -INSERT INTO "OpportunityContactRole" VALUES(331,'Donor','true','66','176'); -INSERT INTO "OpportunityContactRole" VALUES(332,'Donor','true','66','177'); -INSERT INTO "OpportunityContactRole" VALUES(333,'Donor','true','66','178'); -INSERT INTO "OpportunityContactRole" VALUES(334,'Donor','true','66','179'); -INSERT INTO "OpportunityContactRole" VALUES(335,'Donor','true','66','180'); -INSERT INTO "OpportunityContactRole" VALUES(336,'Donor','true','66','181'); -INSERT INTO "OpportunityContactRole" VALUES(337,'Donor','true','66','182'); -INSERT INTO "OpportunityContactRole" VALUES(338,'Donor','true','66','183'); -INSERT INTO "OpportunityContactRole" VALUES(339,'Donor','true','66','184'); -INSERT INTO "OpportunityContactRole" VALUES(340,'Donor','true','66','185'); -INSERT INTO "OpportunityContactRole" VALUES(341,'Donor','true','66','186'); -INSERT INTO "OpportunityContactRole" VALUES(342,'Donor','true','66','187'); -INSERT INTO "OpportunityContactRole" VALUES(343,'Donor','true','66','188'); -INSERT INTO "OpportunityContactRole" VALUES(344,'Donor','true','66','189'); -INSERT INTO "OpportunityContactRole" VALUES(345,'Donor','true','66','190'); -INSERT INTO "OpportunityContactRole" VALUES(346,'Donor','true','66','191'); -INSERT INTO "OpportunityContactRole" VALUES(347,'Donor','true','66','192'); -INSERT INTO "OpportunityContactRole" VALUES(348,'Donor','true','66','193'); -INSERT INTO "OpportunityContactRole" VALUES(349,'Donor','true','66','197'); -INSERT INTO "OpportunityContactRole" VALUES(350,'Donor','true','66','198'); -INSERT INTO "OpportunityContactRole" VALUES(351,'Donor','true','66','199'); -INSERT INTO "OpportunityContactRole" VALUES(352,'Donor','true','66','200'); -INSERT INTO "OpportunityContactRole" VALUES(353,'Donor','true','66','103'); -INSERT INTO "OpportunityContactRole" VALUES(354,'Donor','true','66','104'); -INSERT INTO "OpportunityContactRole" VALUES(355,'Donor','true','66','105'); -INSERT INTO "OpportunityContactRole" VALUES(356,'Donor','true','66','106'); -INSERT INTO "OpportunityContactRole" VALUES(357,'Donor','true','66','107'); -INSERT INTO "OpportunityContactRole" VALUES(358,'Donor','true','66','108'); -INSERT INTO "OpportunityContactRole" VALUES(359,'Donor','true','66','109'); -INSERT INTO "OpportunityContactRole" VALUES(360,'Donor','true','66','110'); -INSERT INTO "OpportunityContactRole" VALUES(361,'Donor','true','66','111'); -INSERT INTO "OpportunityContactRole" VALUES(362,'Donor','true','66','112'); -INSERT INTO "OpportunityContactRole" VALUES(363,'Donor','true','66','113'); -INSERT INTO "OpportunityContactRole" VALUES(364,'Donor','true','66','114'); -INSERT INTO "OpportunityContactRole" VALUES(365,'Donor','true','66','115'); -INSERT INTO "OpportunityContactRole" VALUES(366,'Donor','true','66','116'); -INSERT INTO "OpportunityContactRole" VALUES(367,'Donor','true','66','117'); -INSERT INTO "OpportunityContactRole" VALUES(368,'Donor','true','66','118'); -INSERT INTO "OpportunityContactRole" VALUES(369,'Donor','true','66','119'); -INSERT INTO "OpportunityContactRole" VALUES(370,'Donor','true','66','68'); -INSERT INTO "OpportunityContactRole" VALUES(371,'Donor','true','66','69'); -INSERT INTO "OpportunityContactRole" VALUES(372,'Donor','true','66','120'); -INSERT INTO "OpportunityContactRole" VALUES(373,'Donor','true','66','121'); -INSERT INTO "OpportunityContactRole" VALUES(374,'Donor','true','66','122'); -INSERT INTO "OpportunityContactRole" VALUES(375,'Donor','true','66','123'); -INSERT INTO "OpportunityContactRole" VALUES(376,'Donor','true','66','124'); -INSERT INTO "OpportunityContactRole" VALUES(377,'Donor','true','66','125'); -INSERT INTO "OpportunityContactRole" VALUES(378,'Donor','true','66','126'); -INSERT INTO "OpportunityContactRole" VALUES(379,'Donor','true','66','127'); -INSERT INTO "OpportunityContactRole" VALUES(380,'Soft Credit','false','66','94'); -INSERT INTO "OpportunityContactRole" VALUES(381,'Influencer','false','55','208'); -INSERT INTO "OpportunityContactRole" VALUES(382,'Soft Credit','false','55','94'); -INSERT INTO "OpportunityContactRole" VALUES(383,'Influencer','false','56','199'); -INSERT INTO "OpportunityContactRole" VALUES(384,'Soft Credit','false','56','94'); -INSERT INTO "OpportunityContactRole" VALUES(385,'Influencer','false','96','199'); -INSERT INTO "OpportunityContactRole" VALUES(386,'Soft Credit','false','96','94'); -INSERT INTO "OpportunityContactRole" VALUES(387,'Donor','true','97','83'); -INSERT INTO "OpportunityContactRole" VALUES(388,'Donor','true','97','87'); -INSERT INTO "OpportunityContactRole" VALUES(389,'Donor','true','97','88'); -INSERT INTO "OpportunityContactRole" VALUES(390,'Donor','true','97','89'); -INSERT INTO "OpportunityContactRole" VALUES(391,'Donor','true','97','90'); -INSERT INTO "OpportunityContactRole" VALUES(392,'Donor','true','97','91'); -INSERT INTO "OpportunityContactRole" VALUES(393,'Donor','true','97','92'); -INSERT INTO "OpportunityContactRole" VALUES(394,'Influencer','false','97','19'); -INSERT INTO "OpportunityContactRole" VALUES(395,'Donor','true','97','84'); -INSERT INTO "OpportunityContactRole" VALUES(396,'Donor','true','97','85'); -INSERT INTO "OpportunityContactRole" VALUES(397,'Donor','true','97','86'); -INSERT INTO "OpportunityContactRole" VALUES(398,'Soft Credit','false','97','94'); -INSERT INTO "OpportunityContactRole" VALUES(399,'Donor','true','58','98'); -INSERT INTO "OpportunityContactRole" VALUES(400,'Donor','true','58','99'); -INSERT INTO "OpportunityContactRole" VALUES(401,'Influencer','false','58','13'); -INSERT INTO "OpportunityContactRole" VALUES(402,'Donor','true','58','100'); -INSERT INTO "OpportunityContactRole" VALUES(403,'Donor','true','58','101'); -INSERT INTO "OpportunityContactRole" VALUES(404,'Donor','true','58','102'); -INSERT INTO "OpportunityContactRole" VALUES(405,'Donor','true','58','93'); -INSERT INTO "OpportunityContactRole" VALUES(406,'Donor','true','58','94'); -INSERT INTO "OpportunityContactRole" VALUES(407,'Donor','true','58','95'); -INSERT INTO "OpportunityContactRole" VALUES(408,'Donor','true','58','96'); -INSERT INTO "OpportunityContactRole" VALUES(409,'Donor','true','58','97'); -INSERT INTO "OpportunityContactRole" VALUES(410,'Influencer','false','59','19'); -INSERT INTO "OpportunityContactRole" VALUES(411,'Soft Credit','false','59','94'); -INSERT INTO "OpportunityContactRole" VALUES(412,'Influencer','false','60','124'); -INSERT INTO "OpportunityContactRole" VALUES(413,'Soft Credit','false','60','94'); -INSERT INTO "OpportunityContactRole" VALUES(414,'Influencer','false','61','123'); -INSERT INTO "OpportunityContactRole" VALUES(415,'Soft Credit','false','61','94'); -INSERT INTO "OpportunityContactRole" VALUES(416,'Soft Credit','false','62','94'); -INSERT INTO "OpportunityContactRole" VALUES(417,'Influencer','false','67','164'); -INSERT INTO "OpportunityContactRole" VALUES(418,'Soft Credit','false','67','94'); -INSERT INTO "OpportunityContactRole" VALUES(419,'Soft Credit','false','77','94'); -INSERT INTO "OpportunityContactRole" VALUES(420,'Influencer','false','68','123'); -INSERT INTO "OpportunityContactRole" VALUES(421,'Soft Credit','false','68','94'); -INSERT INTO "OpportunityContactRole" VALUES(422,'Influencer','false','69','19'); -INSERT INTO "OpportunityContactRole" VALUES(423,'Soft Credit','false','69','94'); -INSERT INTO "OpportunityContactRole" VALUES(424,'Donor','true','70','201'); -INSERT INTO "OpportunityContactRole" VALUES(425,'Donor','true','70','202'); -INSERT INTO "OpportunityContactRole" VALUES(426,'Donor','true','70','203'); -INSERT INTO "OpportunityContactRole" VALUES(427,'Donor','true','70','204'); -INSERT INTO "OpportunityContactRole" VALUES(428,'Donor','true','70','205'); -INSERT INTO "OpportunityContactRole" VALUES(429,'Donor','true','70','206'); -INSERT INTO "OpportunityContactRole" VALUES(430,'Donor','true','70','207'); -INSERT INTO "OpportunityContactRole" VALUES(431,'Donor','true','70','208'); -INSERT INTO "OpportunityContactRole" VALUES(432,'Donor','true','70','209'); -INSERT INTO "OpportunityContactRole" VALUES(433,'Donor','true','70','210'); -INSERT INTO "OpportunityContactRole" VALUES(434,'Influencer','false','70','149'); -INSERT INTO "OpportunityContactRole" VALUES(435,'Soft Credit','false','70','94'); -INSERT INTO "OpportunityContactRole" VALUES(436,'Soft Credit','false','71','94'); -INSERT INTO "OpportunityContactRole" VALUES(437,'Influencer','false','72','123'); -INSERT INTO "OpportunityContactRole" VALUES(438,'Soft Credit','false','72','94'); -INSERT INTO "OpportunityContactRole" VALUES(439,'Influencer','false','73','204'); -INSERT INTO "OpportunityContactRole" VALUES(440,'Soft Credit','false','73','94'); -INSERT INTO "OpportunityContactRole" VALUES(441,'Influencer','false','74','208'); -INSERT INTO "OpportunityContactRole" VALUES(442,'Soft Credit','false','74','94'); -INSERT INTO "OpportunityContactRole" VALUES(443,'Influencer','false','75','204'); -INSERT INTO "OpportunityContactRole" VALUES(444,'Soft Credit','false','75','94'); -INSERT INTO "OpportunityContactRole" VALUES(445,'Influencer','false','76','124'); -INSERT INTO "OpportunityContactRole" VALUES(446,'Soft Credit','false','76','94'); -INSERT INTO "OpportunityContactRole" VALUES(447,'Influencer','false','78','199'); -INSERT INTO "OpportunityContactRole" VALUES(448,'Soft Credit','false','78','94'); -INSERT INTO "OpportunityContactRole" VALUES(449,'Influencer','false','88','204'); -INSERT INTO "OpportunityContactRole" VALUES(450,'Soft Credit','false','88','94'); -INSERT INTO "OpportunityContactRole" VALUES(451,'Influencer','false','79','13'); -INSERT INTO "OpportunityContactRole" VALUES(452,'Soft Credit','false','79','94'); -INSERT INTO "OpportunityContactRole" VALUES(453,'Influencer','false','80','208'); -INSERT INTO "OpportunityContactRole" VALUES(454,'Soft Credit','false','80','94'); -INSERT INTO "OpportunityContactRole" VALUES(455,'Influencer','false','81','149'); -INSERT INTO "OpportunityContactRole" VALUES(456,'Soft Credit','false','81','94'); -INSERT INTO "OpportunityContactRole" VALUES(457,'Influencer','false','82','199'); -INSERT INTO "OpportunityContactRole" VALUES(458,'Soft Credit','false','82','94'); -INSERT INTO "OpportunityContactRole" VALUES(459,'Influencer','false','83','199'); -INSERT INTO "OpportunityContactRole" VALUES(460,'Soft Credit','false','83','94'); -INSERT INTO "OpportunityContactRole" VALUES(461,'Influencer','false','84','124'); -INSERT INTO "OpportunityContactRole" VALUES(462,'Soft Credit','false','84','94'); -INSERT INTO "OpportunityContactRole" VALUES(463,'Influencer','false','85','164'); -INSERT INTO "OpportunityContactRole" VALUES(464,'Soft Credit','false','85','94'); -INSERT INTO "OpportunityContactRole" VALUES(465,'Influencer','false','86','199'); -INSERT INTO "OpportunityContactRole" VALUES(466,'Soft Credit','false','86','94'); -INSERT INTO "OpportunityContactRole" VALUES(467,'Influencer','false','87','19'); -INSERT INTO "OpportunityContactRole" VALUES(468,'Soft Credit','false','87','94'); -INSERT INTO "OpportunityContactRole" VALUES(469,'Influencer','false','90','13'); -INSERT INTO "OpportunityContactRole" VALUES(470,'Soft Credit','false','90','94'); -INSERT INTO "OpportunityContactRole" VALUES(471,'Influencer','false','89','204'); -INSERT INTO "OpportunityContactRole" VALUES(472,'Soft Credit','false','89','94'); -INSERT INTO "OpportunityContactRole" VALUES(473,'Influencer','false','91','124'); -INSERT INTO "OpportunityContactRole" VALUES(474,'Soft Credit','false','91','94'); -INSERT INTO "OpportunityContactRole" VALUES(475,'Soft Credit','false','63','94'); -INSERT INTO "OpportunityContactRole" VALUES(476,'Influencer','false','64','208'); -INSERT INTO "OpportunityContactRole" VALUES(477,'Soft Credit','false','64','94'); -INSERT INTO "OpportunityContactRole" VALUES(478,'Influencer','false','65','208'); -INSERT INTO "OpportunityContactRole" VALUES(479,'Soft Credit','false','65','94'); -INSERT INTO "OpportunityContactRole" VALUES(480,'Influencer','false','92','208'); -INSERT INTO "OpportunityContactRole" VALUES(481,'Soft Credit','false','92','94'); -INSERT INTO "OpportunityContactRole" VALUES(482,'Influencer','false','93','149'); -INSERT INTO "OpportunityContactRole" VALUES(483,'Soft Credit','false','93','94'); -INSERT INTO "OpportunityContactRole" VALUES(484,'Influencer','false','94','199'); -INSERT INTO "OpportunityContactRole" VALUES(485,'Soft Credit','false','94','94'); -INSERT INTO "OpportunityContactRole" VALUES(486,'Influencer','false','95','199'); -INSERT INTO "OpportunityContactRole" VALUES(487,'Soft Credit','false','95','94'); -INSERT INTO "OpportunityContactRole" VALUES(488,'Influencer','false','98','123'); -INSERT INTO "OpportunityContactRole" VALUES(489,'Soft Credit','false','98','94'); -INSERT INTO "OpportunityContactRole" VALUES(490,'Influencer','false','99','13'); -INSERT INTO "OpportunityContactRole" VALUES(491,'Soft Credit','false','99','94'); +INSERT INTO "OpportunityContactRole" VALUES(1,'Donor','True','1','252'); +INSERT INTO "OpportunityContactRole" VALUES(2,'Soft Credit','False','10','252'); +INSERT INTO "OpportunityContactRole" VALUES(3,'Donor','True','39','110'); +INSERT INTO "OpportunityContactRole" VALUES(4,'Donor','True','39','111'); +INSERT INTO "OpportunityContactRole" VALUES(5,'Donor','True','10','35'); +INSERT INTO "OpportunityContactRole" VALUES(6,'Donor','True','10','36'); +INSERT INTO "OpportunityContactRole" VALUES(7,'Donor','True','10','37'); +INSERT INTO "OpportunityContactRole" VALUES(8,'Donor','True','10','38'); +INSERT INTO "OpportunityContactRole" VALUES(9,'Donor','True','10','39'); +INSERT INTO "OpportunityContactRole" VALUES(10,'Donor','True','10','40'); +INSERT INTO "OpportunityContactRole" VALUES(11,'Donor','True','10','41'); +INSERT INTO "OpportunityContactRole" VALUES(12,'Donor','True','10','42'); +INSERT INTO "OpportunityContactRole" VALUES(13,'Donor','True','10','43'); +INSERT INTO "OpportunityContactRole" VALUES(14,'Donor','True','10','44'); +INSERT INTO "OpportunityContactRole" VALUES(15,'Donor','True','12','55'); +INSERT INTO "OpportunityContactRole" VALUES(16,'Donor','True','12','56'); +INSERT INTO "OpportunityContactRole" VALUES(17,'Donor','True','12','57'); +INSERT INTO "OpportunityContactRole" VALUES(18,'Donor','True','12','58'); +INSERT INTO "OpportunityContactRole" VALUES(19,'Donor','True','12','68'); +INSERT INTO "OpportunityContactRole" VALUES(20,'Donor','True','12','69'); +INSERT INTO "OpportunityContactRole" VALUES(21,'Donor','True','12','70'); +INSERT INTO "OpportunityContactRole" VALUES(22,'Donor','True','12','71'); +INSERT INTO "OpportunityContactRole" VALUES(23,'Donor','True','12','72'); +INSERT INTO "OpportunityContactRole" VALUES(24,'Donor','True','12','73'); +INSERT INTO "OpportunityContactRole" VALUES(25,'Donor','True','18','84'); +INSERT INTO "OpportunityContactRole" VALUES(26,'Donor','True','18','85'); +INSERT INTO "OpportunityContactRole" VALUES(27,'Donor','True','18','86'); +INSERT INTO "OpportunityContactRole" VALUES(28,'Donor','True','18','87'); +INSERT INTO "OpportunityContactRole" VALUES(29,'Donor','True','18','88'); +INSERT INTO "OpportunityContactRole" VALUES(30,'Donor','True','18','89'); +INSERT INTO "OpportunityContactRole" VALUES(31,'Donor','True','18','90'); +INSERT INTO "OpportunityContactRole" VALUES(32,'Donor','True','18','91'); +INSERT INTO "OpportunityContactRole" VALUES(33,'Donor','True','18','92'); +INSERT INTO "OpportunityContactRole" VALUES(34,'Donor','True','18','93'); +INSERT INTO "OpportunityContactRole" VALUES(35,'Donor','True','30','94'); +INSERT INTO "OpportunityContactRole" VALUES(36,'Donor','True','30','95'); +INSERT INTO "OpportunityContactRole" VALUES(37,'Donor','True','30','96'); +INSERT INTO "OpportunityContactRole" VALUES(38,'Donor','True','30','97'); +INSERT INTO "OpportunityContactRole" VALUES(39,'Donor','True','30','98'); +INSERT INTO "OpportunityContactRole" VALUES(40,'Donor','True','30','99'); +INSERT INTO "OpportunityContactRole" VALUES(41,'Donor','True','30','100'); +INSERT INTO "OpportunityContactRole" VALUES(42,'Donor','True','30','101'); +INSERT INTO "OpportunityContactRole" VALUES(43,'Donor','True','30','102'); +INSERT INTO "OpportunityContactRole" VALUES(44,'Donor','True','30','103'); +INSERT INTO "OpportunityContactRole" VALUES(45,'Donor','True','39','104'); +INSERT INTO "OpportunityContactRole" VALUES(46,'Donor','True','39','105'); +INSERT INTO "OpportunityContactRole" VALUES(47,'Donor','True','39','106'); +INSERT INTO "OpportunityContactRole" VALUES(48,'Donor','True','39','107'); +INSERT INTO "OpportunityContactRole" VALUES(49,'Donor','True','39','108'); +INSERT INTO "OpportunityContactRole" VALUES(50,'Donor','True','39','109'); +INSERT INTO "OpportunityContactRole" VALUES(51,'Donor','True','39','112'); +INSERT INTO "OpportunityContactRole" VALUES(52,'Donor','True','39','113'); +INSERT INTO "OpportunityContactRole" VALUES(53,'Donor','True','55','114'); +INSERT INTO "OpportunityContactRole" VALUES(54,'Donor','True','55','115'); +INSERT INTO "OpportunityContactRole" VALUES(55,'Donor','True','55','116'); +INSERT INTO "OpportunityContactRole" VALUES(56,'Donor','True','55','117'); +INSERT INTO "OpportunityContactRole" VALUES(57,'Donor','True','55','118'); +INSERT INTO "OpportunityContactRole" VALUES(58,'Donor','True','55','119'); +INSERT INTO "OpportunityContactRole" VALUES(59,'Donor','True','55','120'); +INSERT INTO "OpportunityContactRole" VALUES(60,'Donor','True','55','121'); +INSERT INTO "OpportunityContactRole" VALUES(61,'Donor','True','55','122'); +INSERT INTO "OpportunityContactRole" VALUES(62,'Donor','True','55','123'); +INSERT INTO "OpportunityContactRole" VALUES(63,'Donor','True','56','124'); +INSERT INTO "OpportunityContactRole" VALUES(64,'Donor','True','56','125'); +INSERT INTO "OpportunityContactRole" VALUES(65,'Donor','True','56','126'); +INSERT INTO "OpportunityContactRole" VALUES(66,'Donor','True','56','127'); +INSERT INTO "OpportunityContactRole" VALUES(67,'Donor','True','56','128'); +INSERT INTO "OpportunityContactRole" VALUES(68,'Donor','True','56','129'); +INSERT INTO "OpportunityContactRole" VALUES(69,'Donor','True','56','130'); +INSERT INTO "OpportunityContactRole" VALUES(70,'Donor','True','56','131'); +INSERT INTO "OpportunityContactRole" VALUES(71,'Donor','True','56','132'); +INSERT INTO "OpportunityContactRole" VALUES(72,'Donor','True','56','133'); +INSERT INTO "OpportunityContactRole" VALUES(73,'Donor','True','68','134'); +INSERT INTO "OpportunityContactRole" VALUES(74,'Donor','True','68','135'); +INSERT INTO "OpportunityContactRole" VALUES(75,'Donor','True','68','136'); +INSERT INTO "OpportunityContactRole" VALUES(76,'Donor','True','68','137'); +INSERT INTO "OpportunityContactRole" VALUES(77,'Donor','True','68','138'); +INSERT INTO "OpportunityContactRole" VALUES(78,'Donor','True','68','139'); +INSERT INTO "OpportunityContactRole" VALUES(79,'Donor','True','68','140'); +INSERT INTO "OpportunityContactRole" VALUES(80,'Donor','True','68','141'); +INSERT INTO "OpportunityContactRole" VALUES(81,'Donor','True','68','142'); +INSERT INTO "OpportunityContactRole" VALUES(82,'Donor','True','68','143'); +INSERT INTO "OpportunityContactRole" VALUES(83,'Donor','True','68','144'); +INSERT INTO "OpportunityContactRole" VALUES(84,'Donor','True','68','145'); +INSERT INTO "OpportunityContactRole" VALUES(85,'Donor','True','68','146'); +INSERT INTO "OpportunityContactRole" VALUES(86,'Donor','True','68','147'); +INSERT INTO "OpportunityContactRole" VALUES(87,'Donor','True','68','148'); +INSERT INTO "OpportunityContactRole" VALUES(88,'Donor','True','68','149'); +INSERT INTO "OpportunityContactRole" VALUES(89,'Donor','True','68','150'); +INSERT INTO "OpportunityContactRole" VALUES(90,'Donor','True','68','151'); +INSERT INTO "OpportunityContactRole" VALUES(91,'Donor','True','68','152'); +INSERT INTO "OpportunityContactRole" VALUES(92,'Donor','True','68','153'); +INSERT INTO "OpportunityContactRole" VALUES(93,'Donor','True','68','154'); +INSERT INTO "OpportunityContactRole" VALUES(94,'Donor','True','68','155'); +INSERT INTO "OpportunityContactRole" VALUES(95,'Donor','True','68','156'); +INSERT INTO "OpportunityContactRole" VALUES(96,'Donor','True','68','157'); +INSERT INTO "OpportunityContactRole" VALUES(97,'Donor','True','68','158'); +INSERT INTO "OpportunityContactRole" VALUES(98,'Donor','True','68','159'); +INSERT INTO "OpportunityContactRole" VALUES(99,'Donor','True','68','160'); +INSERT INTO "OpportunityContactRole" VALUES(100,'Donor','True','68','161'); +INSERT INTO "OpportunityContactRole" VALUES(101,'Donor','True','68','162'); +INSERT INTO "OpportunityContactRole" VALUES(102,'Donor','True','68','163'); +INSERT INTO "OpportunityContactRole" VALUES(103,'Donor','True','68','164'); +INSERT INTO "OpportunityContactRole" VALUES(104,'Donor','True','68','165'); +INSERT INTO "OpportunityContactRole" VALUES(105,'Donor','True','68','166'); +INSERT INTO "OpportunityContactRole" VALUES(106,'Donor','True','68','167'); +INSERT INTO "OpportunityContactRole" VALUES(107,'Donor','True','68','168'); +INSERT INTO "OpportunityContactRole" VALUES(108,'Donor','True','68','169'); +INSERT INTO "OpportunityContactRole" VALUES(109,'Donor','True','68','170'); +INSERT INTO "OpportunityContactRole" VALUES(110,'Donor','True','68','171'); +INSERT INTO "OpportunityContactRole" VALUES(111,'Donor','True','68','172'); +INSERT INTO "OpportunityContactRole" VALUES(112,'Donor','True','68','173'); +INSERT INTO "OpportunityContactRole" VALUES(113,'Donor','True','68','174'); +INSERT INTO "OpportunityContactRole" VALUES(114,'Donor','True','68','175'); +INSERT INTO "OpportunityContactRole" VALUES(115,'Donor','True','68','176'); +INSERT INTO "OpportunityContactRole" VALUES(116,'Donor','True','68','177'); +INSERT INTO "OpportunityContactRole" VALUES(117,'Donor','True','68','178'); +INSERT INTO "OpportunityContactRole" VALUES(118,'Donor','True','68','179'); +INSERT INTO "OpportunityContactRole" VALUES(119,'Donor','True','68','180'); +INSERT INTO "OpportunityContactRole" VALUES(120,'Donor','True','68','181'); +INSERT INTO "OpportunityContactRole" VALUES(121,'Donor','True','68','182'); +INSERT INTO "OpportunityContactRole" VALUES(122,'Donor','True','68','183'); +INSERT INTO "OpportunityContactRole" VALUES(123,'Donor','True','68','184'); +INSERT INTO "OpportunityContactRole" VALUES(124,'Donor','True','68','185'); +INSERT INTO "OpportunityContactRole" VALUES(125,'Donor','True','68','186'); +INSERT INTO "OpportunityContactRole" VALUES(126,'Donor','True','68','187'); +INSERT INTO "OpportunityContactRole" VALUES(127,'Donor','True','68','188'); +INSERT INTO "OpportunityContactRole" VALUES(128,'Donor','True','68','189'); +INSERT INTO "OpportunityContactRole" VALUES(129,'Donor','True','68','190'); +INSERT INTO "OpportunityContactRole" VALUES(130,'Donor','True','68','191'); +INSERT INTO "OpportunityContactRole" VALUES(131,'Donor','True','68','192'); +INSERT INTO "OpportunityContactRole" VALUES(132,'Donor','True','68','193'); +INSERT INTO "OpportunityContactRole" VALUES(133,'Donor','True','68','194'); +INSERT INTO "OpportunityContactRole" VALUES(134,'Donor','True','68','195'); +INSERT INTO "OpportunityContactRole" VALUES(135,'Donor','True','68','196'); +INSERT INTO "OpportunityContactRole" VALUES(136,'Donor','True','68','197'); +INSERT INTO "OpportunityContactRole" VALUES(137,'Donor','True','68','198'); +INSERT INTO "OpportunityContactRole" VALUES(138,'Donor','True','68','199'); +INSERT INTO "OpportunityContactRole" VALUES(139,'Donor','True','68','200'); +INSERT INTO "OpportunityContactRole" VALUES(140,'Donor','True','68','201'); +INSERT INTO "OpportunityContactRole" VALUES(141,'Donor','True','68','202'); +INSERT INTO "OpportunityContactRole" VALUES(142,'Donor','True','68','203'); +INSERT INTO "OpportunityContactRole" VALUES(143,'Donor','True','68','204'); +INSERT INTO "OpportunityContactRole" VALUES(144,'Donor','True','68','205'); +INSERT INTO "OpportunityContactRole" VALUES(145,'Donor','True','68','206'); +INSERT INTO "OpportunityContactRole" VALUES(146,'Donor','True','68','207'); +INSERT INTO "OpportunityContactRole" VALUES(147,'Donor','True','68','208'); +INSERT INTO "OpportunityContactRole" VALUES(148,'Donor','True','68','209'); +INSERT INTO "OpportunityContactRole" VALUES(149,'Donor','True','68','210'); +INSERT INTO "OpportunityContactRole" VALUES(150,'Donor','True','68','211'); +INSERT INTO "OpportunityContactRole" VALUES(151,'Donor','True','68','212'); +INSERT INTO "OpportunityContactRole" VALUES(152,'Donor','True','68','213'); +INSERT INTO "OpportunityContactRole" VALUES(153,'Donor','True','68','214'); +INSERT INTO "OpportunityContactRole" VALUES(154,'Donor','True','68','215'); +INSERT INTO "OpportunityContactRole" VALUES(155,'Donor','True','68','216'); +INSERT INTO "OpportunityContactRole" VALUES(156,'Donor','True','68','217'); +INSERT INTO "OpportunityContactRole" VALUES(157,'Donor','True','68','218'); +INSERT INTO "OpportunityContactRole" VALUES(158,'Donor','True','68','219'); +INSERT INTO "OpportunityContactRole" VALUES(159,'Donor','True','68','220'); +INSERT INTO "OpportunityContactRole" VALUES(160,'Donor','True','68','221'); +INSERT INTO "OpportunityContactRole" VALUES(161,'Donor','True','68','222'); +INSERT INTO "OpportunityContactRole" VALUES(162,'Donor','True','68','223'); +INSERT INTO "OpportunityContactRole" VALUES(163,'Donor','True','68','224'); +INSERT INTO "OpportunityContactRole" VALUES(164,'Donor','True','68','225'); +INSERT INTO "OpportunityContactRole" VALUES(165,'Donor','True','68','226'); +INSERT INTO "OpportunityContactRole" VALUES(166,'Donor','True','68','227'); +INSERT INTO "OpportunityContactRole" VALUES(167,'Donor','True','68','228'); +INSERT INTO "OpportunityContactRole" VALUES(168,'Donor','True','68','229'); +INSERT INTO "OpportunityContactRole" VALUES(169,'Donor','True','68','230'); +INSERT INTO "OpportunityContactRole" VALUES(170,'Donor','True','68','231'); +INSERT INTO "OpportunityContactRole" VALUES(171,'Donor','True','68','232'); +INSERT INTO "OpportunityContactRole" VALUES(172,'Donor','True','68','233'); +INSERT INTO "OpportunityContactRole" VALUES(173,'Donor','True','68','234'); +INSERT INTO "OpportunityContactRole" VALUES(174,'Donor','True','68','235'); +INSERT INTO "OpportunityContactRole" VALUES(175,'Donor','True','68','236'); +INSERT INTO "OpportunityContactRole" VALUES(176,'Donor','True','68','59'); +INSERT INTO "OpportunityContactRole" VALUES(177,'Donor','True','68','60'); +INSERT INTO "OpportunityContactRole" VALUES(178,'Donor','True','68','61'); +INSERT INTO "OpportunityContactRole" VALUES(179,'Donor','True','68','62'); +INSERT INTO "OpportunityContactRole" VALUES(180,'Donor','True','68','63'); +INSERT INTO "OpportunityContactRole" VALUES(181,'Donor','True','68','64'); +INSERT INTO "OpportunityContactRole" VALUES(182,'Donor','True','68','65'); +INSERT INTO "OpportunityContactRole" VALUES(183,'Household Member','False','17','84'); +INSERT INTO "OpportunityContactRole" VALUES(184,'Household Member','False','17','85'); +INSERT INTO "OpportunityContactRole" VALUES(185,'Household Member','False','17','86'); +INSERT INTO "OpportunityContactRole" VALUES(186,'Household Member','False','17','87'); +INSERT INTO "OpportunityContactRole" VALUES(187,'Household Member','False','17','88'); +INSERT INTO "OpportunityContactRole" VALUES(188,'Household Member','False','17','89'); +INSERT INTO "OpportunityContactRole" VALUES(189,'Household Member','False','17','90'); +INSERT INTO "OpportunityContactRole" VALUES(190,'Household Member','False','17','91'); +INSERT INTO "OpportunityContactRole" VALUES(191,'Household Member','False','17','92'); +INSERT INTO "OpportunityContactRole" VALUES(192,'Household Member','False','17','93'); +INSERT INTO "OpportunityContactRole" VALUES(193,'Donor','True','10','67'); +INSERT INTO "OpportunityContactRole" VALUES(194,'Household Member','False','1','67'); +INSERT INTO "OpportunityContactRole" VALUES(195,'Donor','True','1','66'); +INSERT INTO "OpportunityContactRole" VALUES(196,'Soft Credit','False','10','66'); +INSERT INTO "OpportunityContactRole" VALUES(197,'Donor','True','1','238'); +INSERT INTO "OpportunityContactRole" VALUES(198,'Soft Credit','False','10','238'); +INSERT INTO "OpportunityContactRole" VALUES(199,'Donor','True','1','239'); +INSERT INTO "OpportunityContactRole" VALUES(200,'Soft Credit','False','10','239'); +INSERT INTO "OpportunityContactRole" VALUES(201,'Donor','True','1','240'); +INSERT INTO "OpportunityContactRole" VALUES(202,'Soft Credit','False','10','240'); +INSERT INTO "OpportunityContactRole" VALUES(203,'Donor','True','10','246'); +INSERT INTO "OpportunityContactRole" VALUES(204,'Household Member','False','1','246'); +INSERT INTO "OpportunityContactRole" VALUES(205,'Donor','True','10','42'); +INSERT INTO "OpportunityContactRole" VALUES(206,'Donor','True','10','43'); +INSERT INTO "OpportunityContactRole" VALUES(207,'Donor','True','10','44'); +INSERT INTO "OpportunityContactRole" VALUES(208,'Donor','True','10','35'); +INSERT INTO "OpportunityContactRole" VALUES(209,'Donor','True','10','36'); +INSERT INTO "OpportunityContactRole" VALUES(210,'Donor','True','10','37'); +INSERT INTO "OpportunityContactRole" VALUES(211,'Donor','True','10','38'); +INSERT INTO "OpportunityContactRole" VALUES(212,'Donor','True','10','39'); +INSERT INTO "OpportunityContactRole" VALUES(213,'Donor','True','10','40'); +INSERT INTO "OpportunityContactRole" VALUES(214,'Donor','True','10','41'); +INSERT INTO "OpportunityContactRole" VALUES(215,'Soft Credit','False','10','125'); +INSERT INTO "OpportunityContactRole" VALUES(216,'Influencer','False','19','45'); +INSERT INTO "OpportunityContactRole" VALUES(217,'Soft Credit','False','19','125'); +INSERT INTO "OpportunityContactRole" VALUES(218,'Influencer','False','8','157'); +INSERT INTO "OpportunityContactRole" VALUES(219,'Soft Credit','False','8','125'); +INSERT INTO "OpportunityContactRole" VALUES(220,'Influencer','False','20','182'); +INSERT INTO "OpportunityContactRole" VALUES(221,'Soft Credit','False','20','125'); +INSERT INTO "OpportunityContactRole" VALUES(222,'Influencer','False','21','197'); +INSERT INTO "OpportunityContactRole" VALUES(223,'Soft Credit','False','21','125'); +INSERT INTO "OpportunityContactRole" VALUES(224,'Influencer','False','22','232'); +INSERT INTO "OpportunityContactRole" VALUES(225,'Soft Credit','False','22','125'); +INSERT INTO "OpportunityContactRole" VALUES(226,'Influencer','False','23','156'); +INSERT INTO "OpportunityContactRole" VALUES(227,'Soft Credit','False','23','125'); +INSERT INTO "OpportunityContactRole" VALUES(228,'Influencer','False','24','157'); +INSERT INTO "OpportunityContactRole" VALUES(229,'Soft Credit','False','24','125'); +INSERT INTO "OpportunityContactRole" VALUES(230,'Influencer','False','25','197'); +INSERT INTO "OpportunityContactRole" VALUES(231,'Soft Credit','False','25','125'); +INSERT INTO "OpportunityContactRole" VALUES(232,'Influencer','False','26','182'); +INSERT INTO "OpportunityContactRole" VALUES(233,'Soft Credit','False','26','125'); +INSERT INTO "OpportunityContactRole" VALUES(234,'Influencer','False','27','51'); +INSERT INTO "OpportunityContactRole" VALUES(235,'Soft Credit','False','27','125'); +INSERT INTO "OpportunityContactRole" VALUES(236,'Influencer','False','28','45'); +INSERT INTO "OpportunityContactRole" VALUES(237,'Soft Credit','False','28','125'); +INSERT INTO "OpportunityContactRole" VALUES(238,'Influencer','False','11','197'); +INSERT INTO "OpportunityContactRole" VALUES(239,'Soft Credit','False','11','125'); +INSERT INTO "OpportunityContactRole" VALUES(240,'Influencer','False','29','45'); +INSERT INTO "OpportunityContactRole" VALUES(241,'Soft Credit','False','29','125'); +INSERT INTO "OpportunityContactRole" VALUES(242,'Influencer','False','30','16'); +INSERT INTO "OpportunityContactRole" VALUES(243,'Donor','True','30','94'); +INSERT INTO "OpportunityContactRole" VALUES(244,'Donor','True','30','95'); +INSERT INTO "OpportunityContactRole" VALUES(245,'Donor','True','30','96'); +INSERT INTO "OpportunityContactRole" VALUES(246,'Donor','True','30','97'); +INSERT INTO "OpportunityContactRole" VALUES(247,'Donor','True','30','98'); +INSERT INTO "OpportunityContactRole" VALUES(248,'Donor','True','30','99'); +INSERT INTO "OpportunityContactRole" VALUES(249,'Donor','True','30','100'); +INSERT INTO "OpportunityContactRole" VALUES(250,'Donor','True','30','101'); +INSERT INTO "OpportunityContactRole" VALUES(251,'Donor','True','30','102'); +INSERT INTO "OpportunityContactRole" VALUES(252,'Donor','True','30','103'); +INSERT INTO "OpportunityContactRole" VALUES(253,'Soft Credit','False','30','125'); +INSERT INTO "OpportunityContactRole" VALUES(254,'Influencer','False','31','45'); +INSERT INTO "OpportunityContactRole" VALUES(255,'Soft Credit','False','31','125'); +INSERT INTO "OpportunityContactRole" VALUES(256,'Influencer','False','32','182'); +INSERT INTO "OpportunityContactRole" VALUES(257,'Soft Credit','False','32','125'); +INSERT INTO "OpportunityContactRole" VALUES(258,'Soft Credit','False','33','125'); +INSERT INTO "OpportunityContactRole" VALUES(259,'Influencer','False','34','16'); +INSERT INTO "OpportunityContactRole" VALUES(260,'Soft Credit','False','34','125'); +INSERT INTO "OpportunityContactRole" VALUES(261,'Influencer','False','35','197'); +INSERT INTO "OpportunityContactRole" VALUES(262,'Soft Credit','False','35','125'); +INSERT INTO "OpportunityContactRole" VALUES(263,'Influencer','False','36','20'); +INSERT INTO "OpportunityContactRole" VALUES(264,'Soft Credit','False','36','125'); +INSERT INTO "OpportunityContactRole" VALUES(265,'Influencer','False','37','157'); +INSERT INTO "OpportunityContactRole" VALUES(266,'Soft Credit','False','37','125'); +INSERT INTO "OpportunityContactRole" VALUES(267,'Influencer','False','38','20'); +INSERT INTO "OpportunityContactRole" VALUES(268,'Soft Credit','False','38','125'); +INSERT INTO "OpportunityContactRole" VALUES(269,'Influencer','False','12','197'); +INSERT INTO "OpportunityContactRole" VALUES(270,'Donor','True','12','55'); +INSERT INTO "OpportunityContactRole" VALUES(271,'Donor','True','12','56'); +INSERT INTO "OpportunityContactRole" VALUES(272,'Donor','True','12','57'); +INSERT INTO "OpportunityContactRole" VALUES(273,'Donor','True','12','58'); +INSERT INTO "OpportunityContactRole" VALUES(274,'Donor','True','12','68'); +INSERT INTO "OpportunityContactRole" VALUES(275,'Donor','True','12','69'); +INSERT INTO "OpportunityContactRole" VALUES(276,'Donor','True','12','70'); +INSERT INTO "OpportunityContactRole" VALUES(277,'Donor','True','12','71'); +INSERT INTO "OpportunityContactRole" VALUES(278,'Donor','True','12','72'); +INSERT INTO "OpportunityContactRole" VALUES(279,'Donor','True','12','73'); +INSERT INTO "OpportunityContactRole" VALUES(280,'Soft Credit','False','12','125'); +INSERT INTO "OpportunityContactRole" VALUES(281,'Donor','True','39','104'); +INSERT INTO "OpportunityContactRole" VALUES(282,'Influencer','False','39','45'); +INSERT INTO "OpportunityContactRole" VALUES(283,'Donor','True','39','105'); +INSERT INTO "OpportunityContactRole" VALUES(284,'Donor','True','39','106'); +INSERT INTO "OpportunityContactRole" VALUES(285,'Donor','True','39','107'); +INSERT INTO "OpportunityContactRole" VALUES(286,'Donor','True','39','108'); +INSERT INTO "OpportunityContactRole" VALUES(287,'Donor','True','39','109'); +INSERT INTO "OpportunityContactRole" VALUES(288,'Donor','True','39','110'); +INSERT INTO "OpportunityContactRole" VALUES(289,'Donor','True','39','111'); +INSERT INTO "OpportunityContactRole" VALUES(290,'Donor','True','39','112'); +INSERT INTO "OpportunityContactRole" VALUES(291,'Donor','True','39','113'); +INSERT INTO "OpportunityContactRole" VALUES(292,'Soft Credit','False','39','125'); +INSERT INTO "OpportunityContactRole" VALUES(293,'Influencer','False','40','197'); +INSERT INTO "OpportunityContactRole" VALUES(294,'Soft Credit','False','40','125'); +INSERT INTO "OpportunityContactRole" VALUES(295,'Influencer','False','41','157'); +INSERT INTO "OpportunityContactRole" VALUES(296,'Soft Credit','False','41','125'); +INSERT INTO "OpportunityContactRole" VALUES(297,'Influencer','False','42','156'); +INSERT INTO "OpportunityContactRole" VALUES(298,'Soft Credit','False','42','125'); +INSERT INTO "OpportunityContactRole" VALUES(299,'Soft Credit','False','43','125'); +INSERT INTO "OpportunityContactRole" VALUES(300,'Influencer','False','44','182'); +INSERT INTO "OpportunityContactRole" VALUES(301,'Soft Credit','False','44','125'); +INSERT INTO "OpportunityContactRole" VALUES(302,'Influencer','False','45','182'); +INSERT INTO "OpportunityContactRole" VALUES(303,'Soft Credit','False','45','125'); +INSERT INTO "OpportunityContactRole" VALUES(304,'Influencer','False','46','51'); +INSERT INTO "OpportunityContactRole" VALUES(305,'Soft Credit','False','46','125'); +INSERT INTO "OpportunityContactRole" VALUES(306,'Influencer','False','47','45'); +INSERT INTO "OpportunityContactRole" VALUES(307,'Soft Credit','False','47','125'); +INSERT INTO "OpportunityContactRole" VALUES(308,'Influencer','False','48','16'); +INSERT INTO "OpportunityContactRole" VALUES(309,'Soft Credit','False','48','125'); +INSERT INTO "OpportunityContactRole" VALUES(310,'Influencer','False','13','232'); +INSERT INTO "OpportunityContactRole" VALUES(311,'Soft Credit','False','13','125'); +INSERT INTO "OpportunityContactRole" VALUES(312,'Influencer','False','66','157'); +INSERT INTO "OpportunityContactRole" VALUES(313,'Soft Credit','False','66','125'); +INSERT INTO "OpportunityContactRole" VALUES(314,'Influencer','False','67','197'); +INSERT INTO "OpportunityContactRole" VALUES(315,'Soft Credit','False','67','125'); +INSERT INTO "OpportunityContactRole" VALUES(316,'Influencer','False','69','156'); +INSERT INTO "OpportunityContactRole" VALUES(317,'Donor','True','69','3'); +INSERT INTO "OpportunityContactRole" VALUES(318,'Donor','True','69','4'); +INSERT INTO "OpportunityContactRole" VALUES(319,'Donor','True','69','5'); +INSERT INTO "OpportunityContactRole" VALUES(320,'Donor','True','69','6'); +INSERT INTO "OpportunityContactRole" VALUES(321,'Donor','True','69','7'); +INSERT INTO "OpportunityContactRole" VALUES(322,'Donor','True','69','8'); +INSERT INTO "OpportunityContactRole" VALUES(323,'Donor','True','69','9'); +INSERT INTO "OpportunityContactRole" VALUES(324,'Donor','True','69','10'); +INSERT INTO "OpportunityContactRole" VALUES(325,'Donor','True','69','11'); +INSERT INTO "OpportunityContactRole" VALUES(326,'Donor','True','69','12'); +INSERT INTO "OpportunityContactRole" VALUES(327,'Soft Credit','False','69','125'); +INSERT INTO "OpportunityContactRole" VALUES(328,'Influencer','False','70','156'); +INSERT INTO "OpportunityContactRole" VALUES(329,'Household Member','False','70','3'); +INSERT INTO "OpportunityContactRole" VALUES(330,'Household Member','False','70','4'); +INSERT INTO "OpportunityContactRole" VALUES(331,'Household Member','False','70','5'); +INSERT INTO "OpportunityContactRole" VALUES(332,'Household Member','False','70','6'); +INSERT INTO "OpportunityContactRole" VALUES(333,'Household Member','False','70','7'); +INSERT INTO "OpportunityContactRole" VALUES(334,'Household Member','False','70','8'); +INSERT INTO "OpportunityContactRole" VALUES(335,'Household Member','False','70','9'); +INSERT INTO "OpportunityContactRole" VALUES(336,'Household Member','False','70','10'); +INSERT INTO "OpportunityContactRole" VALUES(337,'Household Member','False','70','11'); +INSERT INTO "OpportunityContactRole" VALUES(338,'Household Member','False','70','12'); +INSERT INTO "OpportunityContactRole" VALUES(339,'Soft Credit','False','70','125'); +INSERT INTO "OpportunityContactRole" VALUES(340,'Influencer','False','71','16'); +INSERT INTO "OpportunityContactRole" VALUES(341,'Household Member','False','71','3'); +INSERT INTO "OpportunityContactRole" VALUES(342,'Household Member','False','71','4'); +INSERT INTO "OpportunityContactRole" VALUES(343,'Household Member','False','71','5'); +INSERT INTO "OpportunityContactRole" VALUES(344,'Household Member','False','71','6'); +INSERT INTO "OpportunityContactRole" VALUES(345,'Household Member','False','71','7'); +INSERT INTO "OpportunityContactRole" VALUES(346,'Household Member','False','71','8'); +INSERT INTO "OpportunityContactRole" VALUES(347,'Household Member','False','71','9'); +INSERT INTO "OpportunityContactRole" VALUES(348,'Household Member','False','71','10'); +INSERT INTO "OpportunityContactRole" VALUES(349,'Household Member','False','71','11'); +INSERT INTO "OpportunityContactRole" VALUES(350,'Household Member','False','71','12'); +INSERT INTO "OpportunityContactRole" VALUES(351,'Soft Credit','False','71','125'); +INSERT INTO "OpportunityContactRole" VALUES(352,'Influencer','False','72','157'); +INSERT INTO "OpportunityContactRole" VALUES(353,'Household Member','False','72','3'); +INSERT INTO "OpportunityContactRole" VALUES(354,'Household Member','False','72','4'); +INSERT INTO "OpportunityContactRole" VALUES(355,'Household Member','False','72','5'); +INSERT INTO "OpportunityContactRole" VALUES(356,'Household Member','False','72','6'); +INSERT INTO "OpportunityContactRole" VALUES(357,'Household Member','False','72','7'); +INSERT INTO "OpportunityContactRole" VALUES(358,'Household Member','False','72','8'); +INSERT INTO "OpportunityContactRole" VALUES(359,'Household Member','False','72','9'); +INSERT INTO "OpportunityContactRole" VALUES(360,'Household Member','False','72','10'); +INSERT INTO "OpportunityContactRole" VALUES(361,'Household Member','False','72','11'); +INSERT INTO "OpportunityContactRole" VALUES(362,'Household Member','False','72','12'); +INSERT INTO "OpportunityContactRole" VALUES(363,'Soft Credit','False','72','125'); +INSERT INTO "OpportunityContactRole" VALUES(364,'Influencer','False','73','182'); +INSERT INTO "OpportunityContactRole" VALUES(365,'Household Member','False','73','3'); +INSERT INTO "OpportunityContactRole" VALUES(366,'Household Member','False','73','4'); +INSERT INTO "OpportunityContactRole" VALUES(367,'Household Member','False','73','5'); +INSERT INTO "OpportunityContactRole" VALUES(368,'Household Member','False','73','6'); +INSERT INTO "OpportunityContactRole" VALUES(369,'Household Member','False','73','7'); +INSERT INTO "OpportunityContactRole" VALUES(370,'Household Member','False','73','8'); +INSERT INTO "OpportunityContactRole" VALUES(371,'Household Member','False','73','9'); +INSERT INTO "OpportunityContactRole" VALUES(372,'Household Member','False','73','10'); +INSERT INTO "OpportunityContactRole" VALUES(373,'Household Member','False','73','11'); +INSERT INTO "OpportunityContactRole" VALUES(374,'Household Member','False','73','12'); +INSERT INTO "OpportunityContactRole" VALUES(375,'Soft Credit','False','73','125'); +INSERT INTO "OpportunityContactRole" VALUES(376,'Influencer','False','74','182'); +INSERT INTO "OpportunityContactRole" VALUES(377,'Household Member','False','74','3'); +INSERT INTO "OpportunityContactRole" VALUES(378,'Household Member','False','74','4'); +INSERT INTO "OpportunityContactRole" VALUES(379,'Household Member','False','74','5'); +INSERT INTO "OpportunityContactRole" VALUES(380,'Household Member','False','74','6'); +INSERT INTO "OpportunityContactRole" VALUES(381,'Household Member','False','74','7'); +INSERT INTO "OpportunityContactRole" VALUES(382,'Household Member','False','74','8'); +INSERT INTO "OpportunityContactRole" VALUES(383,'Household Member','False','74','9'); +INSERT INTO "OpportunityContactRole" VALUES(384,'Household Member','False','74','10'); +INSERT INTO "OpportunityContactRole" VALUES(385,'Household Member','False','74','11'); +INSERT INTO "OpportunityContactRole" VALUES(386,'Household Member','False','74','12'); +INSERT INTO "OpportunityContactRole" VALUES(387,'Soft Credit','False','74','125'); +INSERT INTO "OpportunityContactRole" VALUES(388,'Influencer','False','75','51'); +INSERT INTO "OpportunityContactRole" VALUES(389,'Household Member','False','75','3'); +INSERT INTO "OpportunityContactRole" VALUES(390,'Household Member','False','75','4'); +INSERT INTO "OpportunityContactRole" VALUES(391,'Household Member','False','75','5'); +INSERT INTO "OpportunityContactRole" VALUES(392,'Household Member','False','75','6'); +INSERT INTO "OpportunityContactRole" VALUES(393,'Household Member','False','75','7'); +INSERT INTO "OpportunityContactRole" VALUES(394,'Household Member','False','75','8'); +INSERT INTO "OpportunityContactRole" VALUES(395,'Household Member','False','75','9'); +INSERT INTO "OpportunityContactRole" VALUES(396,'Household Member','False','75','10'); +INSERT INTO "OpportunityContactRole" VALUES(397,'Household Member','False','75','11'); +INSERT INTO "OpportunityContactRole" VALUES(398,'Household Member','False','75','12'); +INSERT INTO "OpportunityContactRole" VALUES(399,'Soft Credit','False','75','125'); +INSERT INTO "OpportunityContactRole" VALUES(400,'Influencer','False','76','51'); +INSERT INTO "OpportunityContactRole" VALUES(401,'Household Member','False','76','4'); +INSERT INTO "OpportunityContactRole" VALUES(402,'Household Member','False','76','5'); +INSERT INTO "OpportunityContactRole" VALUES(403,'Household Member','False','76','6'); +INSERT INTO "OpportunityContactRole" VALUES(404,'Donor','True','9','237'); +INSERT INTO "OpportunityContactRole" VALUES(405,'Soft Credit','False','39','237'); +INSERT INTO "OpportunityContactRole" VALUES(406,'Donor','True','10','247'); +INSERT INTO "OpportunityContactRole" VALUES(407,'Household Member','False','1','247'); +INSERT INTO "OpportunityContactRole" VALUES(408,'Soft Credit','False','10','259'); +INSERT INTO "OpportunityContactRole" VALUES(409,'Donor','True','10','243'); +INSERT INTO "OpportunityContactRole" VALUES(410,'Household Member','False','1','243'); +INSERT INTO "OpportunityContactRole" VALUES(411,'Donor','True','10','244'); +INSERT INTO "OpportunityContactRole" VALUES(412,'Household Member','False','1','244'); +INSERT INTO "OpportunityContactRole" VALUES(413,'Donor','True','1','245'); +INSERT INTO "OpportunityContactRole" VALUES(414,'Soft Credit','False','10','245'); +INSERT INTO "OpportunityContactRole" VALUES(415,'Household Member','False','76','3'); +INSERT INTO "OpportunityContactRole" VALUES(416,'Donor','True','9','241'); +INSERT INTO "OpportunityContactRole" VALUES(417,'Household Member','False','39','241'); +INSERT INTO "OpportunityContactRole" VALUES(418,'Donor','True','10','242'); +INSERT INTO "OpportunityContactRole" VALUES(419,'Household Member','False','1','242'); +INSERT INTO "OpportunityContactRole" VALUES(420,'Donor','True','10','248'); +INSERT INTO "OpportunityContactRole" VALUES(421,'Household Member','False','1','248'); +INSERT INTO "OpportunityContactRole" VALUES(422,'Donor','True','10','26'); +INSERT INTO "OpportunityContactRole" VALUES(423,'Household Member','False','1','26'); +INSERT INTO "OpportunityContactRole" VALUES(424,'Donor','True','1','23'); +INSERT INTO "OpportunityContactRole" VALUES(425,'Soft Credit','False','10','23'); +INSERT INTO "OpportunityContactRole" VALUES(426,'Donor','True','68','1'); +INSERT INTO "OpportunityContactRole" VALUES(427,'Donor','True','68','2'); +INSERT INTO "OpportunityContactRole" VALUES(428,'Donor','True','69','3'); +INSERT INTO "OpportunityContactRole" VALUES(429,'Donor','True','69','4'); +INSERT INTO "OpportunityContactRole" VALUES(430,'Donor','True','69','5'); +INSERT INTO "OpportunityContactRole" VALUES(431,'Donor','True','69','6'); +INSERT INTO "OpportunityContactRole" VALUES(432,'Donor','True','69','7'); +INSERT INTO "OpportunityContactRole" VALUES(433,'Donor','True','69','8'); +INSERT INTO "OpportunityContactRole" VALUES(434,'Donor','True','69','9'); +INSERT INTO "OpportunityContactRole" VALUES(435,'Donor','True','69','10'); +INSERT INTO "OpportunityContactRole" VALUES(436,'Donor','True','69','11'); +INSERT INTO "OpportunityContactRole" VALUES(437,'Donor','True','69','12'); +INSERT INTO "OpportunityContactRole" VALUES(438,'Donor','True','81','13'); +INSERT INTO "OpportunityContactRole" VALUES(439,'Donor','True','81','14'); +INSERT INTO "OpportunityContactRole" VALUES(440,'Donor','True','81','15'); +INSERT INTO "OpportunityContactRole" VALUES(441,'Donor','True','81','16'); +INSERT INTO "OpportunityContactRole" VALUES(442,'Donor','True','81','17'); +INSERT INTO "OpportunityContactRole" VALUES(443,'Donor','True','81','18'); +INSERT INTO "OpportunityContactRole" VALUES(444,'Donor','True','81','19'); +INSERT INTO "OpportunityContactRole" VALUES(445,'Donor','True','81','20'); +INSERT INTO "OpportunityContactRole" VALUES(446,'Donor','True','81','21'); +INSERT INTO "OpportunityContactRole" VALUES(447,'Donor','True','81','22'); +INSERT INTO "OpportunityContactRole" VALUES(448,'Household Member','False','70','3'); +INSERT INTO "OpportunityContactRole" VALUES(449,'Household Member','False','71','3'); +INSERT INTO "OpportunityContactRole" VALUES(450,'Household Member','False','72','3'); +INSERT INTO "OpportunityContactRole" VALUES(451,'Household Member','False','73','3'); +INSERT INTO "OpportunityContactRole" VALUES(452,'Household Member','False','74','3'); +INSERT INTO "OpportunityContactRole" VALUES(453,'Household Member','False','75','3'); +INSERT INTO "OpportunityContactRole" VALUES(454,'Household Member','False','76','3'); +INSERT INTO "OpportunityContactRole" VALUES(455,'Household Member','False','77','3'); +INSERT INTO "OpportunityContactRole" VALUES(456,'Household Member','False','78','3'); +INSERT INTO "OpportunityContactRole" VALUES(457,'Household Member','False','70','4'); +INSERT INTO "OpportunityContactRole" VALUES(458,'Household Member','False','71','4'); +INSERT INTO "OpportunityContactRole" VALUES(459,'Household Member','False','72','4'); +INSERT INTO "OpportunityContactRole" VALUES(460,'Household Member','False','73','4'); +INSERT INTO "OpportunityContactRole" VALUES(461,'Household Member','False','74','4'); +INSERT INTO "OpportunityContactRole" VALUES(462,'Household Member','False','75','4'); +INSERT INTO "OpportunityContactRole" VALUES(463,'Household Member','False','76','4'); +INSERT INTO "OpportunityContactRole" VALUES(464,'Household Member','False','77','4'); +INSERT INTO "OpportunityContactRole" VALUES(465,'Household Member','False','78','4'); +INSERT INTO "OpportunityContactRole" VALUES(466,'Household Member','False','70','5'); +INSERT INTO "OpportunityContactRole" VALUES(467,'Household Member','False','71','5'); +INSERT INTO "OpportunityContactRole" VALUES(468,'Household Member','False','72','5'); +INSERT INTO "OpportunityContactRole" VALUES(469,'Household Member','False','73','5'); +INSERT INTO "OpportunityContactRole" VALUES(470,'Household Member','False','74','5'); +INSERT INTO "OpportunityContactRole" VALUES(471,'Household Member','False','75','5'); +INSERT INTO "OpportunityContactRole" VALUES(472,'Household Member','False','76','5'); +INSERT INTO "OpportunityContactRole" VALUES(473,'Household Member','False','77','5'); +INSERT INTO "OpportunityContactRole" VALUES(474,'Household Member','False','78','5'); +INSERT INTO "OpportunityContactRole" VALUES(475,'Household Member','False','70','6'); +INSERT INTO "OpportunityContactRole" VALUES(476,'Household Member','False','71','6'); +INSERT INTO "OpportunityContactRole" VALUES(477,'Household Member','False','77','11'); +INSERT INTO "OpportunityContactRole" VALUES(478,'Household Member','False','72','6'); +INSERT INTO "OpportunityContactRole" VALUES(479,'Household Member','False','73','6'); +INSERT INTO "OpportunityContactRole" VALUES(480,'Household Member','False','74','6'); +INSERT INTO "OpportunityContactRole" VALUES(481,'Household Member','False','75','6'); +INSERT INTO "OpportunityContactRole" VALUES(482,'Household Member','False','76','6'); +INSERT INTO "OpportunityContactRole" VALUES(483,'Household Member','False','77','6'); +INSERT INTO "OpportunityContactRole" VALUES(484,'Household Member','False','78','6'); +INSERT INTO "OpportunityContactRole" VALUES(485,'Household Member','False','70','7'); +INSERT INTO "OpportunityContactRole" VALUES(486,'Household Member','False','71','7'); +INSERT INTO "OpportunityContactRole" VALUES(487,'Household Member','False','72','7'); +INSERT INTO "OpportunityContactRole" VALUES(488,'Household Member','False','73','7'); +INSERT INTO "OpportunityContactRole" VALUES(489,'Household Member','False','74','7'); +INSERT INTO "OpportunityContactRole" VALUES(490,'Household Member','False','75','7'); +INSERT INTO "OpportunityContactRole" VALUES(491,'Household Member','False','76','7'); +INSERT INTO "OpportunityContactRole" VALUES(492,'Household Member','False','77','7'); +INSERT INTO "OpportunityContactRole" VALUES(493,'Household Member','False','78','7'); +INSERT INTO "OpportunityContactRole" VALUES(494,'Household Member','False','70','8'); +INSERT INTO "OpportunityContactRole" VALUES(495,'Household Member','False','71','8'); +INSERT INTO "OpportunityContactRole" VALUES(496,'Household Member','False','72','8'); +INSERT INTO "OpportunityContactRole" VALUES(497,'Household Member','False','73','8'); +INSERT INTO "OpportunityContactRole" VALUES(498,'Household Member','False','74','8'); +INSERT INTO "OpportunityContactRole" VALUES(499,'Household Member','False','75','8'); +INSERT INTO "OpportunityContactRole" VALUES(500,'Household Member','False','76','8'); +INSERT INTO "OpportunityContactRole" VALUES(501,'Household Member','False','77','8'); +INSERT INTO "OpportunityContactRole" VALUES(502,'Household Member','False','78','8'); +INSERT INTO "OpportunityContactRole" VALUES(503,'Household Member','False','70','9'); +INSERT INTO "OpportunityContactRole" VALUES(504,'Household Member','False','71','9'); +INSERT INTO "OpportunityContactRole" VALUES(505,'Household Member','False','72','9'); +INSERT INTO "OpportunityContactRole" VALUES(506,'Household Member','False','73','9'); +INSERT INTO "OpportunityContactRole" VALUES(507,'Household Member','False','74','9'); +INSERT INTO "OpportunityContactRole" VALUES(508,'Household Member','False','75','9'); +INSERT INTO "OpportunityContactRole" VALUES(509,'Household Member','False','76','9'); +INSERT INTO "OpportunityContactRole" VALUES(510,'Household Member','False','77','9'); +INSERT INTO "OpportunityContactRole" VALUES(511,'Household Member','False','78','9'); +INSERT INTO "OpportunityContactRole" VALUES(512,'Household Member','False','70','10'); +INSERT INTO "OpportunityContactRole" VALUES(513,'Household Member','False','71','10'); +INSERT INTO "OpportunityContactRole" VALUES(514,'Household Member','False','72','10'); +INSERT INTO "OpportunityContactRole" VALUES(515,'Household Member','False','73','10'); +INSERT INTO "OpportunityContactRole" VALUES(516,'Household Member','False','74','10'); +INSERT INTO "OpportunityContactRole" VALUES(517,'Household Member','False','75','10'); +INSERT INTO "OpportunityContactRole" VALUES(518,'Household Member','False','76','10'); +INSERT INTO "OpportunityContactRole" VALUES(519,'Household Member','False','77','10'); +INSERT INTO "OpportunityContactRole" VALUES(520,'Household Member','False','78','10'); +INSERT INTO "OpportunityContactRole" VALUES(521,'Household Member','False','70','11'); +INSERT INTO "OpportunityContactRole" VALUES(522,'Household Member','False','71','11'); +INSERT INTO "OpportunityContactRole" VALUES(523,'Household Member','False','72','11'); +INSERT INTO "OpportunityContactRole" VALUES(524,'Household Member','False','73','11'); +INSERT INTO "OpportunityContactRole" VALUES(525,'Household Member','False','74','11'); +INSERT INTO "OpportunityContactRole" VALUES(526,'Household Member','False','75','11'); +INSERT INTO "OpportunityContactRole" VALUES(527,'Household Member','False','76','11'); +INSERT INTO "OpportunityContactRole" VALUES(528,'Household Member','False','78','11'); +INSERT INTO "OpportunityContactRole" VALUES(529,'Household Member','False','70','12'); +INSERT INTO "OpportunityContactRole" VALUES(530,'Household Member','False','71','12'); +INSERT INTO "OpportunityContactRole" VALUES(531,'Household Member','False','72','12'); +INSERT INTO "OpportunityContactRole" VALUES(532,'Household Member','False','73','12'); +INSERT INTO "OpportunityContactRole" VALUES(533,'Household Member','False','74','12'); +INSERT INTO "OpportunityContactRole" VALUES(534,'Household Member','False','75','12'); +INSERT INTO "OpportunityContactRole" VALUES(535,'Household Member','False','76','12'); +INSERT INTO "OpportunityContactRole" VALUES(536,'Household Member','False','77','12'); +INSERT INTO "OpportunityContactRole" VALUES(537,'Household Member','False','78','12'); +INSERT INTO "OpportunityContactRole" VALUES(538,'Donor','True','10','24'); +INSERT INTO "OpportunityContactRole" VALUES(539,'Household Member','False','1','24'); +INSERT INTO "OpportunityContactRole" VALUES(540,'Donor','True','1','25'); +INSERT INTO "OpportunityContactRole" VALUES(541,'Soft Credit','False','10','25'); +INSERT INTO "OpportunityContactRole" VALUES(542,'Soft Credit','False','10','29'); +INSERT INTO "OpportunityContactRole" VALUES(543,'Donor','True','10','27'); +INSERT INTO "OpportunityContactRole" VALUES(544,'Donor','True','1','29'); +INSERT INTO "OpportunityContactRole" VALUES(545,'Household Member','False','1','27'); +INSERT INTO "OpportunityContactRole" VALUES(546,'Donor','True','1','28'); +INSERT INTO "OpportunityContactRole" VALUES(547,'Soft Credit','False','10','28'); +INSERT INTO "OpportunityContactRole" VALUES(548,'Donor','True','1','31'); +INSERT INTO "OpportunityContactRole" VALUES(549,'Soft Credit','False','10','31'); +INSERT INTO "OpportunityContactRole" VALUES(550,'Donor','True','10','30'); +INSERT INTO "OpportunityContactRole" VALUES(551,'Household Member','False','1','30'); +INSERT INTO "OpportunityContactRole" VALUES(552,'Donor','True','1','32'); +INSERT INTO "OpportunityContactRole" VALUES(553,'Soft Credit','False','10','32'); +INSERT INTO "OpportunityContactRole" VALUES(554,'Donor','True','1','33'); +INSERT INTO "OpportunityContactRole" VALUES(555,'Soft Credit','False','10','33'); +INSERT INTO "OpportunityContactRole" VALUES(556,'Donor','True','1','34'); +INSERT INTO "OpportunityContactRole" VALUES(557,'Soft Credit','False','10','34'); +INSERT INTO "OpportunityContactRole" VALUES(558,'Donor','True','9','335'); +INSERT INTO "OpportunityContactRole" VALUES(559,'Soft Credit','False','39','335'); +INSERT INTO "OpportunityContactRole" VALUES(560,'Donor','True','81','21'); +INSERT INTO "OpportunityContactRole" VALUES(561,'Donor','True','81','22'); +INSERT INTO "OpportunityContactRole" VALUES(562,'Soft Credit','False','81','125'); +INSERT INTO "OpportunityContactRole" VALUES(563,'Soft Credit','False','82','125'); +INSERT INTO "OpportunityContactRole" VALUES(564,'Influencer','False','83','156'); +INSERT INTO "OpportunityContactRole" VALUES(565,'Soft Credit','False','83','125'); +INSERT INTO "OpportunityContactRole" VALUES(566,'Influencer','False','84','16'); +INSERT INTO "OpportunityContactRole" VALUES(567,'Soft Credit','False','84','125'); +INSERT INTO "OpportunityContactRole" VALUES(568,'Influencer','False','85','20'); +INSERT INTO "OpportunityContactRole" VALUES(569,'Soft Credit','False','85','125'); +INSERT INTO "OpportunityContactRole" VALUES(570,'Influencer','False','86','16'); +INSERT INTO "OpportunityContactRole" VALUES(571,'Soft Credit','False','86','125'); +INSERT INTO "OpportunityContactRole" VALUES(572,'Influencer','False','87','157'); +INSERT INTO "OpportunityContactRole" VALUES(573,'Soft Credit','False','87','125'); +INSERT INTO "OpportunityContactRole" VALUES(574,'Soft Credit','False','88','125'); +INSERT INTO "OpportunityContactRole" VALUES(575,'Influencer','False','89','232'); +INSERT INTO "OpportunityContactRole" VALUES(576,'Soft Credit','False','89','125'); +INSERT INTO "OpportunityContactRole" VALUES(577,'Influencer','False','90','45'); +INSERT INTO "OpportunityContactRole" VALUES(578,'Soft Credit','False','90','125'); +INSERT INTO "OpportunityContactRole" VALUES(579,'Household Member','False','17','84'); +INSERT INTO "OpportunityContactRole" VALUES(580,'Household Member','False','17','85'); +INSERT INTO "OpportunityContactRole" VALUES(581,'Household Member','False','17','86'); +INSERT INTO "OpportunityContactRole" VALUES(582,'Household Member','False','17','87'); +INSERT INTO "OpportunityContactRole" VALUES(583,'Household Member','False','17','88'); +INSERT INTO "OpportunityContactRole" VALUES(584,'Household Member','False','17','89'); +INSERT INTO "OpportunityContactRole" VALUES(585,'Household Member','False','17','90'); +INSERT INTO "OpportunityContactRole" VALUES(586,'Household Member','False','17','91'); +INSERT INTO "OpportunityContactRole" VALUES(587,'Household Member','False','17','92'); +INSERT INTO "OpportunityContactRole" VALUES(588,'Household Member','False','17','93'); +INSERT INTO "OpportunityContactRole" VALUES(589,'Soft Credit','False','17','125'); +INSERT INTO "OpportunityContactRole" VALUES(590,'Influencer','False','91','20'); +INSERT INTO "OpportunityContactRole" VALUES(591,'Soft Credit','False','91','125'); +INSERT INTO "OpportunityContactRole" VALUES(592,'Influencer','False','92','182'); +INSERT INTO "OpportunityContactRole" VALUES(593,'Soft Credit','False','92','125'); +INSERT INTO "OpportunityContactRole" VALUES(594,'Influencer','False','93','232'); +INSERT INTO "OpportunityContactRole" VALUES(595,'Soft Credit','False','93','125'); +INSERT INTO "OpportunityContactRole" VALUES(596,'Influencer','False','94','232'); +INSERT INTO "OpportunityContactRole" VALUES(597,'Soft Credit','False','94','125'); +INSERT INTO "OpportunityContactRole" VALUES(598,'Influencer','False','95','157'); +INSERT INTO "OpportunityContactRole" VALUES(599,'Soft Credit','False','95','125'); +INSERT INTO "OpportunityContactRole" VALUES(600,'Influencer','False','96','197'); +INSERT INTO "OpportunityContactRole" VALUES(601,'Soft Credit','False','96','125'); +INSERT INTO "OpportunityContactRole" VALUES(602,'Influencer','False','97','232'); +INSERT INTO "OpportunityContactRole" VALUES(603,'Soft Credit','False','97','125'); +INSERT INTO "OpportunityContactRole" VALUES(604,'Influencer','False','98','51'); +INSERT INTO "OpportunityContactRole" VALUES(605,'Soft Credit','False','98','125'); +INSERT INTO "OpportunityContactRole" VALUES(606,'Influencer','False','99','16'); +INSERT INTO "OpportunityContactRole" VALUES(607,'Soft Credit','False','99','125'); +INSERT INTO "OpportunityContactRole" VALUES(608,'Influencer','False','100','16'); +INSERT INTO "OpportunityContactRole" VALUES(609,'Soft Credit','False','100','125'); +INSERT INTO "OpportunityContactRole" VALUES(610,'Donor','True','18','84'); +INSERT INTO "OpportunityContactRole" VALUES(611,'Donor','True','18','85'); +INSERT INTO "OpportunityContactRole" VALUES(612,'Donor','True','18','86'); +INSERT INTO "OpportunityContactRole" VALUES(613,'Donor','True','18','87'); +INSERT INTO "OpportunityContactRole" VALUES(614,'Donor','True','18','88'); +INSERT INTO "OpportunityContactRole" VALUES(615,'Donor','True','18','89'); +INSERT INTO "OpportunityContactRole" VALUES(616,'Donor','True','18','90'); +INSERT INTO "OpportunityContactRole" VALUES(617,'Donor','True','18','91'); +INSERT INTO "OpportunityContactRole" VALUES(618,'Donor','True','18','92'); +INSERT INTO "OpportunityContactRole" VALUES(619,'Donor','True','18','93'); +INSERT INTO "OpportunityContactRole" VALUES(620,'Soft Credit','False','18','125'); +INSERT INTO "OpportunityContactRole" VALUES(621,'Influencer','False','101','45'); +INSERT INTO "OpportunityContactRole" VALUES(622,'Soft Credit','False','101','125'); +INSERT INTO "OpportunityContactRole" VALUES(623,'Influencer','False','102','157'); +INSERT INTO "OpportunityContactRole" VALUES(624,'Soft Credit','False','102','125'); +INSERT INTO "OpportunityContactRole" VALUES(625,'Influencer','False','2','20'); +INSERT INTO "OpportunityContactRole" VALUES(626,'Soft Credit','False','2','125'); +INSERT INTO "OpportunityContactRole" VALUES(627,'Influencer','False','3','182'); +INSERT INTO "OpportunityContactRole" VALUES(628,'Soft Credit','False','3','125'); +INSERT INTO "OpportunityContactRole" VALUES(629,'Influencer','False','4','232'); +INSERT INTO "OpportunityContactRole" VALUES(630,'Soft Credit','False','4','125'); +INSERT INTO "OpportunityContactRole" VALUES(631,'Influencer','False','5','232'); +INSERT INTO "OpportunityContactRole" VALUES(632,'Soft Credit','False','5','125'); +INSERT INTO "OpportunityContactRole" VALUES(633,'Influencer','False','54','232'); +INSERT INTO "OpportunityContactRole" VALUES(634,'Soft Credit','False','54','125'); +INSERT INTO "OpportunityContactRole" VALUES(635,'Influencer','False','55','51'); +INSERT INTO "OpportunityContactRole" VALUES(636,'Donor','True','55','114'); +INSERT INTO "OpportunityContactRole" VALUES(637,'Donor','True','55','115'); +INSERT INTO "OpportunityContactRole" VALUES(638,'Donor','True','55','116'); +INSERT INTO "OpportunityContactRole" VALUES(639,'Donor','True','55','117'); +INSERT INTO "OpportunityContactRole" VALUES(640,'Donor','True','55','118'); +INSERT INTO "OpportunityContactRole" VALUES(641,'Donor','True','55','119'); +INSERT INTO "OpportunityContactRole" VALUES(642,'Donor','True','55','120'); +INSERT INTO "OpportunityContactRole" VALUES(643,'Donor','True','55','121'); +INSERT INTO "OpportunityContactRole" VALUES(644,'Donor','True','55','122'); +INSERT INTO "OpportunityContactRole" VALUES(645,'Donor','True','55','123'); +INSERT INTO "OpportunityContactRole" VALUES(646,'Soft Credit','False','55','125'); +INSERT INTO "OpportunityContactRole" VALUES(647,'Influencer','False','6','156'); +INSERT INTO "OpportunityContactRole" VALUES(648,'Soft Credit','False','6','125'); +INSERT INTO "OpportunityContactRole" VALUES(649,'Influencer','False','7','45'); +INSERT INTO "OpportunityContactRole" VALUES(650,'Soft Credit','False','7','125'); +INSERT INTO "OpportunityContactRole" VALUES(651,'Donor','True','10','337'); +INSERT INTO "OpportunityContactRole" VALUES(652,'Donor','True','10','338'); +INSERT INTO "OpportunityContactRole" VALUES(653,'Household Member','False','1','337'); +INSERT INTO "OpportunityContactRole" VALUES(654,'Household Member','False','1','338'); +INSERT INTO "OpportunityContactRole" VALUES(655,'Soft Credit','False','10','336'); +INSERT INTO "OpportunityContactRole" VALUES(656,'Donor','True','1','336'); +INSERT INTO "OpportunityContactRole" VALUES(657,'Donor','True','1','339'); +INSERT INTO "OpportunityContactRole" VALUES(658,'Soft Credit','False','10','339'); +INSERT INTO "OpportunityContactRole" VALUES(659,'Donor','True','1','340'); +INSERT INTO "OpportunityContactRole" VALUES(660,'Soft Credit','False','10','340'); +INSERT INTO "OpportunityContactRole" VALUES(661,'Donor','True','1','341'); +INSERT INTO "OpportunityContactRole" VALUES(662,'Soft Credit','False','10','341'); +INSERT INTO "OpportunityContactRole" VALUES(663,'Donor','True','1','342'); +INSERT INTO "OpportunityContactRole" VALUES(664,'Soft Credit','False','10','342'); +INSERT INTO "OpportunityContactRole" VALUES(665,'Donor','True','1','328'); +INSERT INTO "OpportunityContactRole" VALUES(666,'Soft Credit','False','10','328'); +INSERT INTO "OpportunityContactRole" VALUES(667,'Donor','True','10','329'); +INSERT INTO "OpportunityContactRole" VALUES(668,'Household Member','False','1','329'); +INSERT INTO "OpportunityContactRole" VALUES(669,'Donor','True','10','330'); +INSERT INTO "OpportunityContactRole" VALUES(670,'Household Member','False','1','330'); +INSERT INTO "OpportunityContactRole" VALUES(671,'Donor','True','10','331'); +INSERT INTO "OpportunityContactRole" VALUES(672,'Household Member','False','1','331'); +INSERT INTO "OpportunityContactRole" VALUES(673,'Donor','True','1','332'); +INSERT INTO "OpportunityContactRole" VALUES(674,'Soft Credit','False','10','332'); +INSERT INTO "OpportunityContactRole" VALUES(675,'Donor','True','1','333'); +INSERT INTO "OpportunityContactRole" VALUES(676,'Soft Credit','False','10','333'); +INSERT INTO "OpportunityContactRole" VALUES(677,'Donor','True','1','334'); +INSERT INTO "OpportunityContactRole" VALUES(678,'Soft Credit','False','10','334'); +INSERT INTO "OpportunityContactRole" VALUES(679,'Donor','True','1','325'); +INSERT INTO "OpportunityContactRole" VALUES(680,'Household Member','False','10','325'); +INSERT INTO "OpportunityContactRole" VALUES(681,'Household Member','False','76','7'); +INSERT INTO "OpportunityContactRole" VALUES(682,'Household Member','False','76','8'); +INSERT INTO "OpportunityContactRole" VALUES(683,'Household Member','False','76','9'); +INSERT INTO "OpportunityContactRole" VALUES(684,'Household Member','False','76','10'); +INSERT INTO "OpportunityContactRole" VALUES(685,'Household Member','False','76','11'); +INSERT INTO "OpportunityContactRole" VALUES(686,'Household Member','False','76','12'); +INSERT INTO "OpportunityContactRole" VALUES(687,'Soft Credit','False','76','125'); +INSERT INTO "OpportunityContactRole" VALUES(688,'Influencer','False','14','16'); +INSERT INTO "OpportunityContactRole" VALUES(689,'Soft Credit','False','14','125'); +INSERT INTO "OpportunityContactRole" VALUES(690,'Influencer','False','77','51'); +INSERT INTO "OpportunityContactRole" VALUES(691,'Household Member','False','77','3'); +INSERT INTO "OpportunityContactRole" VALUES(692,'Household Member','False','77','4'); +INSERT INTO "OpportunityContactRole" VALUES(693,'Household Member','False','77','5'); +INSERT INTO "OpportunityContactRole" VALUES(694,'Household Member','False','77','6'); +INSERT INTO "OpportunityContactRole" VALUES(695,'Household Member','False','77','7'); +INSERT INTO "OpportunityContactRole" VALUES(696,'Household Member','False','77','8'); +INSERT INTO "OpportunityContactRole" VALUES(697,'Household Member','False','77','9'); +INSERT INTO "OpportunityContactRole" VALUES(698,'Household Member','False','77','10'); +INSERT INTO "OpportunityContactRole" VALUES(699,'Household Member','False','77','11'); +INSERT INTO "OpportunityContactRole" VALUES(700,'Household Member','False','77','12'); +INSERT INTO "OpportunityContactRole" VALUES(701,'Soft Credit','False','77','125'); +INSERT INTO "OpportunityContactRole" VALUES(702,'Influencer','False','78','51'); +INSERT INTO "OpportunityContactRole" VALUES(703,'Household Member','False','78','3'); +INSERT INTO "OpportunityContactRole" VALUES(704,'Household Member','False','78','4'); +INSERT INTO "OpportunityContactRole" VALUES(705,'Household Member','False','78','5'); +INSERT INTO "OpportunityContactRole" VALUES(706,'Household Member','False','78','6'); +INSERT INTO "OpportunityContactRole" VALUES(707,'Household Member','False','78','7'); +INSERT INTO "OpportunityContactRole" VALUES(708,'Household Member','False','78','8'); +INSERT INTO "OpportunityContactRole" VALUES(709,'Household Member','False','78','9'); +INSERT INTO "OpportunityContactRole" VALUES(710,'Household Member','False','78','10'); +INSERT INTO "OpportunityContactRole" VALUES(711,'Household Member','False','78','11'); +INSERT INTO "OpportunityContactRole" VALUES(712,'Household Member','False','78','12'); +INSERT INTO "OpportunityContactRole" VALUES(713,'Soft Credit','False','78','125'); +INSERT INTO "OpportunityContactRole" VALUES(714,'Soft Credit','False','49','125'); +INSERT INTO "OpportunityContactRole" VALUES(715,'Influencer','False','50','156'); +INSERT INTO "OpportunityContactRole" VALUES(716,'Soft Credit','False','50','125'); +INSERT INTO "OpportunityContactRole" VALUES(717,'Influencer','False','51','20'); +INSERT INTO "OpportunityContactRole" VALUES(718,'Soft Credit','False','51','125'); +INSERT INTO "OpportunityContactRole" VALUES(719,'Influencer','False','52','20'); +INSERT INTO "OpportunityContactRole" VALUES(720,'Soft Credit','False','52','125'); +INSERT INTO "OpportunityContactRole" VALUES(721,'Influencer','False','53','232'); +INSERT INTO "OpportunityContactRole" VALUES(722,'Soft Credit','False','53','125'); +INSERT INTO "OpportunityContactRole" VALUES(723,'Influencer','False','57','20'); +INSERT INTO "OpportunityContactRole" VALUES(724,'Soft Credit','False','57','125'); +INSERT INTO "OpportunityContactRole" VALUES(725,'Donor','True','56','131'); +INSERT INTO "OpportunityContactRole" VALUES(726,'Donor','True','56','132'); +INSERT INTO "OpportunityContactRole" VALUES(727,'Donor','True','56','133'); +INSERT INTO "OpportunityContactRole" VALUES(728,'Influencer','False','56','45'); +INSERT INTO "OpportunityContactRole" VALUES(729,'Donor','True','56','124'); +INSERT INTO "OpportunityContactRole" VALUES(730,'Donor','True','56','125'); +INSERT INTO "OpportunityContactRole" VALUES(731,'Donor','True','56','126'); +INSERT INTO "OpportunityContactRole" VALUES(732,'Donor','True','56','127'); +INSERT INTO "OpportunityContactRole" VALUES(733,'Donor','True','56','128'); +INSERT INTO "OpportunityContactRole" VALUES(734,'Donor','True','56','129'); +INSERT INTO "OpportunityContactRole" VALUES(735,'Donor','True','56','130'); +INSERT INTO "OpportunityContactRole" VALUES(736,'Influencer','False','58','51'); +INSERT INTO "OpportunityContactRole" VALUES(737,'Soft Credit','False','58','125'); +INSERT INTO "OpportunityContactRole" VALUES(738,'Influencer','False','15','156'); +INSERT INTO "OpportunityContactRole" VALUES(739,'Soft Credit','False','15','125'); +INSERT INTO "OpportunityContactRole" VALUES(740,'Influencer','False','59','157'); +INSERT INTO "OpportunityContactRole" VALUES(741,'Soft Credit','False','59','125'); +INSERT INTO "OpportunityContactRole" VALUES(742,'Influencer','False','60','156'); +INSERT INTO "OpportunityContactRole" VALUES(743,'Soft Credit','False','60','125'); +INSERT INTO "OpportunityContactRole" VALUES(744,'Soft Credit','False','61','125'); +INSERT INTO "OpportunityContactRole" VALUES(745,'Soft Credit','False','62','125'); +INSERT INTO "OpportunityContactRole" VALUES(746,'Influencer','False','63','20'); +INSERT INTO "OpportunityContactRole" VALUES(747,'Soft Credit','False','63','125'); +INSERT INTO "OpportunityContactRole" VALUES(748,'Influencer','False','64','20'); +INSERT INTO "OpportunityContactRole" VALUES(749,'Soft Credit','False','64','125'); +INSERT INTO "OpportunityContactRole" VALUES(750,'Donor','True','68','136'); +INSERT INTO "OpportunityContactRole" VALUES(751,'Donor','True','68','137'); +INSERT INTO "OpportunityContactRole" VALUES(752,'Donor','True','68','138'); +INSERT INTO "OpportunityContactRole" VALUES(753,'Donor','True','68','139'); +INSERT INTO "OpportunityContactRole" VALUES(754,'Donor','True','68','140'); +INSERT INTO "OpportunityContactRole" VALUES(755,'Donor','True','68','141'); +INSERT INTO "OpportunityContactRole" VALUES(756,'Donor','True','68','142'); +INSERT INTO "OpportunityContactRole" VALUES(757,'Donor','True','68','143'); +INSERT INTO "OpportunityContactRole" VALUES(758,'Donor','True','68','144'); +INSERT INTO "OpportunityContactRole" VALUES(759,'Donor','True','68','145'); +INSERT INTO "OpportunityContactRole" VALUES(760,'Donor','True','68','146'); +INSERT INTO "OpportunityContactRole" VALUES(761,'Donor','True','68','147'); +INSERT INTO "OpportunityContactRole" VALUES(762,'Donor','True','68','148'); +INSERT INTO "OpportunityContactRole" VALUES(763,'Donor','True','68','149'); +INSERT INTO "OpportunityContactRole" VALUES(764,'Donor','True','68','150'); +INSERT INTO "OpportunityContactRole" VALUES(765,'Donor','True','68','151'); +INSERT INTO "OpportunityContactRole" VALUES(766,'Donor','True','68','152'); +INSERT INTO "OpportunityContactRole" VALUES(767,'Donor','True','68','153'); +INSERT INTO "OpportunityContactRole" VALUES(768,'Donor','True','68','154'); +INSERT INTO "OpportunityContactRole" VALUES(769,'Donor','True','68','155'); +INSERT INTO "OpportunityContactRole" VALUES(770,'Donor','True','68','156'); +INSERT INTO "OpportunityContactRole" VALUES(771,'Donor','True','68','157'); +INSERT INTO "OpportunityContactRole" VALUES(772,'Donor','True','68','158'); +INSERT INTO "OpportunityContactRole" VALUES(773,'Donor','True','68','159'); +INSERT INTO "OpportunityContactRole" VALUES(774,'Donor','True','68','160'); +INSERT INTO "OpportunityContactRole" VALUES(775,'Donor','True','68','161'); +INSERT INTO "OpportunityContactRole" VALUES(776,'Donor','True','68','162'); +INSERT INTO "OpportunityContactRole" VALUES(777,'Donor','True','68','163'); +INSERT INTO "OpportunityContactRole" VALUES(778,'Donor','True','68','164'); +INSERT INTO "OpportunityContactRole" VALUES(779,'Donor','True','68','165'); +INSERT INTO "OpportunityContactRole" VALUES(780,'Donor','True','68','166'); +INSERT INTO "OpportunityContactRole" VALUES(781,'Donor','True','68','167'); +INSERT INTO "OpportunityContactRole" VALUES(782,'Donor','True','68','168'); +INSERT INTO "OpportunityContactRole" VALUES(783,'Donor','True','68','169'); +INSERT INTO "OpportunityContactRole" VALUES(784,'Donor','True','68','170'); +INSERT INTO "OpportunityContactRole" VALUES(785,'Donor','True','68','171'); +INSERT INTO "OpportunityContactRole" VALUES(786,'Donor','True','68','172'); +INSERT INTO "OpportunityContactRole" VALUES(787,'Donor','True','68','173'); +INSERT INTO "OpportunityContactRole" VALUES(788,'Donor','True','68','174'); +INSERT INTO "OpportunityContactRole" VALUES(789,'Donor','True','68','175'); +INSERT INTO "OpportunityContactRole" VALUES(790,'Donor','True','68','176'); +INSERT INTO "OpportunityContactRole" VALUES(791,'Donor','True','68','177'); +INSERT INTO "OpportunityContactRole" VALUES(792,'Donor','True','68','178'); +INSERT INTO "OpportunityContactRole" VALUES(793,'Donor','True','68','179'); +INSERT INTO "OpportunityContactRole" VALUES(794,'Donor','True','68','180'); +INSERT INTO "OpportunityContactRole" VALUES(795,'Donor','True','68','181'); +INSERT INTO "OpportunityContactRole" VALUES(796,'Donor','True','68','182'); +INSERT INTO "OpportunityContactRole" VALUES(797,'Donor','True','68','183'); +INSERT INTO "OpportunityContactRole" VALUES(798,'Donor','True','68','184'); +INSERT INTO "OpportunityContactRole" VALUES(799,'Donor','True','68','185'); +INSERT INTO "OpportunityContactRole" VALUES(800,'Donor','True','68','186'); +INSERT INTO "OpportunityContactRole" VALUES(801,'Donor','True','68','187'); +INSERT INTO "OpportunityContactRole" VALUES(802,'Donor','True','68','188'); +INSERT INTO "OpportunityContactRole" VALUES(803,'Donor','True','68','189'); +INSERT INTO "OpportunityContactRole" VALUES(804,'Donor','True','68','190'); +INSERT INTO "OpportunityContactRole" VALUES(805,'Donor','True','68','191'); +INSERT INTO "OpportunityContactRole" VALUES(806,'Donor','True','68','192'); +INSERT INTO "OpportunityContactRole" VALUES(807,'Donor','True','68','193'); +INSERT INTO "OpportunityContactRole" VALUES(808,'Donor','True','68','194'); +INSERT INTO "OpportunityContactRole" VALUES(809,'Donor','True','68','195'); +INSERT INTO "OpportunityContactRole" VALUES(810,'Donor','True','68','196'); +INSERT INTO "OpportunityContactRole" VALUES(811,'Donor','True','68','197'); +INSERT INTO "OpportunityContactRole" VALUES(812,'Donor','True','68','198'); +INSERT INTO "OpportunityContactRole" VALUES(813,'Donor','True','68','199'); +INSERT INTO "OpportunityContactRole" VALUES(814,'Donor','True','68','200'); +INSERT INTO "OpportunityContactRole" VALUES(815,'Donor','True','68','201'); +INSERT INTO "OpportunityContactRole" VALUES(816,'Donor','True','68','202'); +INSERT INTO "OpportunityContactRole" VALUES(817,'Donor','True','68','203'); +INSERT INTO "OpportunityContactRole" VALUES(818,'Donor','True','68','204'); +INSERT INTO "OpportunityContactRole" VALUES(819,'Donor','True','68','205'); +INSERT INTO "OpportunityContactRole" VALUES(820,'Donor','True','68','206'); +INSERT INTO "OpportunityContactRole" VALUES(821,'Donor','True','68','207'); +INSERT INTO "OpportunityContactRole" VALUES(822,'Donor','True','68','208'); +INSERT INTO "OpportunityContactRole" VALUES(823,'Donor','True','68','209'); +INSERT INTO "OpportunityContactRole" VALUES(824,'Donor','True','68','210'); +INSERT INTO "OpportunityContactRole" VALUES(825,'Donor','True','68','211'); +INSERT INTO "OpportunityContactRole" VALUES(826,'Donor','True','68','212'); +INSERT INTO "OpportunityContactRole" VALUES(827,'Donor','True','68','213'); +INSERT INTO "OpportunityContactRole" VALUES(828,'Donor','True','68','214'); +INSERT INTO "OpportunityContactRole" VALUES(829,'Donor','True','68','215'); +INSERT INTO "OpportunityContactRole" VALUES(830,'Donor','True','68','216'); +INSERT INTO "OpportunityContactRole" VALUES(831,'Donor','True','68','217'); +INSERT INTO "OpportunityContactRole" VALUES(832,'Donor','True','68','218'); +INSERT INTO "OpportunityContactRole" VALUES(833,'Donor','True','68','219'); +INSERT INTO "OpportunityContactRole" VALUES(834,'Donor','True','68','220'); +INSERT INTO "OpportunityContactRole" VALUES(835,'Donor','True','68','221'); +INSERT INTO "OpportunityContactRole" VALUES(836,'Donor','True','68','222'); +INSERT INTO "OpportunityContactRole" VALUES(837,'Donor','True','68','223'); +INSERT INTO "OpportunityContactRole" VALUES(838,'Donor','True','68','224'); +INSERT INTO "OpportunityContactRole" VALUES(839,'Donor','True','68','225'); +INSERT INTO "OpportunityContactRole" VALUES(840,'Donor','True','68','226'); +INSERT INTO "OpportunityContactRole" VALUES(841,'Donor','True','68','227'); +INSERT INTO "OpportunityContactRole" VALUES(842,'Donor','True','68','228'); +INSERT INTO "OpportunityContactRole" VALUES(843,'Donor','True','68','229'); +INSERT INTO "OpportunityContactRole" VALUES(844,'Donor','True','68','230'); +INSERT INTO "OpportunityContactRole" VALUES(845,'Donor','True','68','231'); +INSERT INTO "OpportunityContactRole" VALUES(846,'Donor','True','68','232'); +INSERT INTO "OpportunityContactRole" VALUES(847,'Donor','True','68','233'); +INSERT INTO "OpportunityContactRole" VALUES(848,'Influencer','False','68','16'); +INSERT INTO "OpportunityContactRole" VALUES(849,'Soft Credit','True','68','234'); +INSERT INTO "OpportunityContactRole" VALUES(850,'Soft Credit','True','68','235'); +INSERT INTO "OpportunityContactRole" VALUES(851,'Soft Credit','True','68','236'); +INSERT INTO "OpportunityContactRole" VALUES(852,'Soft Credit','True','68','59'); +INSERT INTO "OpportunityContactRole" VALUES(853,'Soft Credit','True','68','60'); +INSERT INTO "OpportunityContactRole" VALUES(854,'Soft Credit','True','68','61'); +INSERT INTO "OpportunityContactRole" VALUES(855,'Soft Credit','True','68','62'); +INSERT INTO "OpportunityContactRole" VALUES(856,'Soft Credit','True','68','63'); +INSERT INTO "OpportunityContactRole" VALUES(857,'Soft Credit','True','68','64'); +INSERT INTO "OpportunityContactRole" VALUES(858,'Soft Credit','True','68','65'); +INSERT INTO "OpportunityContactRole" VALUES(859,'Soft Credit','True','68','1'); +INSERT INTO "OpportunityContactRole" VALUES(860,'Donor','True','68','2'); +INSERT INTO "OpportunityContactRole" VALUES(861,'Donor','True','68','134'); +INSERT INTO "OpportunityContactRole" VALUES(862,'Donor','True','68','135'); +INSERT INTO "OpportunityContactRole" VALUES(863,'Soft Credit','False','68','125'); +INSERT INTO "OpportunityContactRole" VALUES(864,'Influencer','False','65','197'); +INSERT INTO "OpportunityContactRole" VALUES(865,'Soft Credit','False','65','125'); +INSERT INTO "OpportunityContactRole" VALUES(866,'Influencer','False','79','156'); +INSERT INTO "OpportunityContactRole" VALUES(867,'Soft Credit','False','79','125'); +INSERT INTO "OpportunityContactRole" VALUES(868,'Influencer','False','80','51'); +INSERT INTO "OpportunityContactRole" VALUES(869,'Soft Credit','False','80','125'); +INSERT INTO "OpportunityContactRole" VALUES(870,'Influencer','False','16','197'); +INSERT INTO "OpportunityContactRole" VALUES(871,'Soft Credit','False','16','125'); +INSERT INTO "OpportunityContactRole" VALUES(872,'Influencer','False','81','182'); +INSERT INTO "OpportunityContactRole" VALUES(873,'Donor','True','81','13'); +INSERT INTO "OpportunityContactRole" VALUES(874,'Donor','True','81','14'); +INSERT INTO "OpportunityContactRole" VALUES(875,'Donor','True','81','15'); +INSERT INTO "OpportunityContactRole" VALUES(876,'Donor','True','81','16'); +INSERT INTO "OpportunityContactRole" VALUES(877,'Donor','True','81','17'); +INSERT INTO "OpportunityContactRole" VALUES(878,'Donor','True','81','18'); +INSERT INTO "OpportunityContactRole" VALUES(879,'Donor','True','81','19'); +INSERT INTO "OpportunityContactRole" VALUES(880,'Donor','True','81','20'); +INSERT INTO "OpportunityContactRole" VALUES(881,'Donor','True','1','322'); +INSERT INTO "OpportunityContactRole" VALUES(882,'Soft Credit','False','10','322'); +INSERT INTO "OpportunityContactRole" VALUES(883,'Donor','True','1','321'); +INSERT INTO "OpportunityContactRole" VALUES(884,'Soft Credit','False','10','321'); +INSERT INTO "OpportunityContactRole" VALUES(885,'Donor','True','1','324'); +INSERT INTO "OpportunityContactRole" VALUES(886,'Household Member','False','10','324'); +INSERT INTO "OpportunityContactRole" VALUES(887,'Donor','True','1','323'); +INSERT INTO "OpportunityContactRole" VALUES(888,'Household Member','False','10','323'); +INSERT INTO "OpportunityContactRole" VALUES(889,'Donor','True','1','327'); +INSERT INTO "OpportunityContactRole" VALUES(890,'Soft Credit','False','10','327'); +INSERT INTO "OpportunityContactRole" VALUES(891,'Donor','True','9','326'); +INSERT INTO "OpportunityContactRole" VALUES(892,'Soft Credit','False','39','326'); +INSERT INTO "OpportunityContactRole" VALUES(893,'Soft Credit','False','10','267'); +INSERT INTO "OpportunityContactRole" VALUES(894,'Donor','True','39','311'); +INSERT INTO "OpportunityContactRole" VALUES(895,'Soft Credit','False','9','311'); +INSERT INTO "OpportunityContactRole" VALUES(896,'Donor','True','1','316'); +INSERT INTO "OpportunityContactRole" VALUES(897,'Soft Credit','False','10','316'); +INSERT INTO "OpportunityContactRole" VALUES(898,'Donor','True','1','313'); +INSERT INTO "OpportunityContactRole" VALUES(899,'Soft Credit','False','10','313'); +INSERT INTO "OpportunityContactRole" VALUES(900,'Donor','True','1','314'); +INSERT INTO "OpportunityContactRole" VALUES(901,'Soft Credit','False','10','314'); +INSERT INTO "OpportunityContactRole" VALUES(902,'Donor','True','1','315'); +INSERT INTO "OpportunityContactRole" VALUES(903,'Soft Credit','False','10','315'); +INSERT INTO "OpportunityContactRole" VALUES(904,'Donor','True','10','312'); +INSERT INTO "OpportunityContactRole" VALUES(905,'Household Member','False','1','312'); +INSERT INTO "OpportunityContactRole" VALUES(906,'Donor','True','68','277'); +INSERT INTO "OpportunityContactRole" VALUES(907,'Donor','True','68','278'); +INSERT INTO "OpportunityContactRole" VALUES(908,'Donor','True','68','279'); +INSERT INTO "OpportunityContactRole" VALUES(909,'Donor','True','68','280'); +INSERT INTO "OpportunityContactRole" VALUES(910,'Donor','True','68','281'); +INSERT INTO "OpportunityContactRole" VALUES(911,'Donor','True','68','282'); +INSERT INTO "OpportunityContactRole" VALUES(912,'Donor','True','68','283'); +INSERT INTO "OpportunityContactRole" VALUES(913,'Donor','True','68','284'); +INSERT INTO "OpportunityContactRole" VALUES(914,'Donor','True','68','285'); +INSERT INTO "OpportunityContactRole" VALUES(915,'Donor','True','68','286'); +INSERT INTO "OpportunityContactRole" VALUES(916,'Donor','True','68','287'); +INSERT INTO "OpportunityContactRole" VALUES(917,'Donor','True','68','288'); +INSERT INTO "OpportunityContactRole" VALUES(918,'Donor','True','68','289'); +INSERT INTO "OpportunityContactRole" VALUES(919,'Donor','True','68','290'); +INSERT INTO "OpportunityContactRole" VALUES(920,'Donor','True','68','291'); +INSERT INTO "OpportunityContactRole" VALUES(921,'Donor','True','68','292'); +INSERT INTO "OpportunityContactRole" VALUES(922,'Donor','True','68','293'); +INSERT INTO "OpportunityContactRole" VALUES(923,'Donor','True','68','294'); +INSERT INTO "OpportunityContactRole" VALUES(924,'Donor','True','68','295'); +INSERT INTO "OpportunityContactRole" VALUES(925,'Donor','True','68','296'); +INSERT INTO "OpportunityContactRole" VALUES(926,'Donor','True','68','297'); +INSERT INTO "OpportunityContactRole" VALUES(927,'Donor','True','68','298'); +INSERT INTO "OpportunityContactRole" VALUES(928,'Donor','True','68','299'); +INSERT INTO "OpportunityContactRole" VALUES(929,'Donor','True','68','300'); +INSERT INTO "OpportunityContactRole" VALUES(930,'Donor','True','68','301'); +INSERT INTO "OpportunityContactRole" VALUES(931,'Donor','True','68','302'); +INSERT INTO "OpportunityContactRole" VALUES(932,'Donor','True','68','303'); +INSERT INTO "OpportunityContactRole" VALUES(933,'Donor','True','68','304'); +INSERT INTO "OpportunityContactRole" VALUES(934,'Donor','True','68','305'); +INSERT INTO "OpportunityContactRole" VALUES(935,'Donor','True','68','306'); +INSERT INTO "OpportunityContactRole" VALUES(936,'Donor','True','68','307'); +INSERT INTO "OpportunityContactRole" VALUES(937,'Donor','True','68','308'); +INSERT INTO "OpportunityContactRole" VALUES(938,'Donor','True','68','309'); +INSERT INTO "OpportunityContactRole" VALUES(939,'Donor','True','68','310'); +INSERT INTO "OpportunityContactRole" VALUES(940,'Donor','True','39','320'); +INSERT INTO "OpportunityContactRole" VALUES(941,'Household Member','False','9','320'); +INSERT INTO "OpportunityContactRole" VALUES(942,'Donor','True','10','317'); +INSERT INTO "OpportunityContactRole" VALUES(943,'Household Member','False','1','317'); +INSERT INTO "OpportunityContactRole" VALUES(944,'Donor','True','10','318'); +INSERT INTO "OpportunityContactRole" VALUES(945,'Household Member','False','1','318'); +INSERT INTO "OpportunityContactRole" VALUES(946,'Donor','True','10','319'); +INSERT INTO "OpportunityContactRole" VALUES(947,'Household Member','False','1','319'); +INSERT INTO "OpportunityContactRole" VALUES(948,'Donor','True','1','259'); +INSERT INTO "OpportunityContactRole" VALUES(949,'Donor','True','9','261'); +INSERT INTO "OpportunityContactRole" VALUES(950,'Soft Credit','False','39','261'); +INSERT INTO "OpportunityContactRole" VALUES(951,'Donor','True','9','262'); +INSERT INTO "OpportunityContactRole" VALUES(952,'Household Member','False','39','262'); +INSERT INTO "OpportunityContactRole" VALUES(953,'Donor','True','10','263'); +INSERT INTO "OpportunityContactRole" VALUES(954,'Household Member','False','1','263'); +INSERT INTO "OpportunityContactRole" VALUES(955,'Donor','True','1','253'); +INSERT INTO "OpportunityContactRole" VALUES(956,'Soft Credit','False','10','253'); +INSERT INTO "OpportunityContactRole" VALUES(957,'Donor','True','1','254'); +INSERT INTO "OpportunityContactRole" VALUES(958,'Soft Credit','False','10','254'); +INSERT INTO "OpportunityContactRole" VALUES(959,'Donor','True','1','255'); +INSERT INTO "OpportunityContactRole" VALUES(960,'Household Member','False','10','255'); +INSERT INTO "OpportunityContactRole" VALUES(961,'Donor','True','1','260'); +INSERT INTO "OpportunityContactRole" VALUES(962,'Soft Credit','False','10','260'); +INSERT INTO "OpportunityContactRole" VALUES(963,'Donor','True','10','256'); +INSERT INTO "OpportunityContactRole" VALUES(964,'Household Member','False','1','256'); +INSERT INTO "OpportunityContactRole" VALUES(965,'Donor','True','10','257'); +INSERT INTO "OpportunityContactRole" VALUES(966,'Household Member','False','1','257'); +INSERT INTO "OpportunityContactRole" VALUES(967,'Donor','True','10','258'); +INSERT INTO "OpportunityContactRole" VALUES(968,'Household Member','False','1','258'); +INSERT INTO "OpportunityContactRole" VALUES(969,'Donor','True','1','264'); +INSERT INTO "OpportunityContactRole" VALUES(970,'Soft Credit','False','10','264'); +INSERT INTO "OpportunityContactRole" VALUES(971,'Donor','True','1','265'); +INSERT INTO "OpportunityContactRole" VALUES(972,'Soft Credit','False','10','265'); +INSERT INTO "OpportunityContactRole" VALUES(973,'Donor','True','1','266'); +INSERT INTO "OpportunityContactRole" VALUES(974,'Soft Credit','False','10','266'); +INSERT INTO "OpportunityContactRole" VALUES(975,'Donor','True','1','267'); +INSERT INTO "OpportunityContactRole" VALUES(976,'Donor','True','1','268'); +INSERT INTO "OpportunityContactRole" VALUES(977,'Household Member','False','10','268'); +INSERT INTO "OpportunityContactRole" VALUES(978,'Donor','True','1','269'); +INSERT INTO "OpportunityContactRole" VALUES(979,'Soft Credit','False','10','269'); +INSERT INTO "OpportunityContactRole" VALUES(980,'Donor','True','1','272'); +INSERT INTO "OpportunityContactRole" VALUES(981,'Soft Credit','False','10','272'); +INSERT INTO "OpportunityContactRole" VALUES(982,'Donor','True','1','273'); +INSERT INTO "OpportunityContactRole" VALUES(983,'Soft Credit','False','10','273'); +INSERT INTO "OpportunityContactRole" VALUES(984,'Donor','True','1','274'); +INSERT INTO "OpportunityContactRole" VALUES(985,'Soft Credit','False','10','274'); +INSERT INTO "OpportunityContactRole" VALUES(986,'Donor','True','1','275'); +INSERT INTO "OpportunityContactRole" VALUES(987,'Soft Credit','False','10','275'); +INSERT INTO "OpportunityContactRole" VALUES(988,'Donor','True','1','276'); +INSERT INTO "OpportunityContactRole" VALUES(989,'Soft Credit','False','10','276'); +INSERT INTO "OpportunityContactRole" VALUES(990,'Donor','True','10','270'); +INSERT INTO "OpportunityContactRole" VALUES(991,'Household Member','False','1','270'); +INSERT INTO "OpportunityContactRole" VALUES(992,'Donor','True','10','271'); +INSERT INTO "OpportunityContactRole" VALUES(993,'Household Member','False','1','271'); +INSERT INTO "OpportunityContactRole" VALUES(994,'Donor','True','1','250'); +INSERT INTO "OpportunityContactRole" VALUES(995,'Soft Credit','False','10','250'); +INSERT INTO "OpportunityContactRole" VALUES(996,'Donor','True','1','251'); +INSERT INTO "OpportunityContactRole" VALUES(997,'Soft Credit','False','10','251'); +INSERT INTO "OpportunityContactRole" VALUES(998,'Donor','True','1','249'); +INSERT INTO "OpportunityContactRole" VALUES(999,'Soft Credit','False','10','249'); CREATE TABLE "Partial_Soft_Credit__c" ( - id INTEGER NOT NULL, - "Role_Name__c" VARCHAR(255), - "Amount__c" VARCHAR(255), - contact__c VARCHAR(255), - opportunity__c VARCHAR(255), + id INTEGER NOT NULL, + "Role_Name__c" VARCHAR(255), + "Amount__c" VARCHAR(255), + "Contact__c" VARCHAR(255), + "Opportunity__c" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "Partial_Soft_Credit__c" VALUES(1,'Soft Credit','1.0','45','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(2,'Soft Credit','1.0','46','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(3,'Soft Credit','1.0','47','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(4,'Soft Credit','1.0','57','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(5,'Soft Credit','1.0','48','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(6,'Soft Credit','1.0','22','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(7,'Soft Credit','1.0','23','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(8,'Soft Credit','1.0','24','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(9,'Soft Credit','1.0','49','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(10,'Soft Credit','1.0','50','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(11,'Soft Credit','1.0','51','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(12,'Soft Credit','1.0','52','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(13,'Soft Credit','1.0','53','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(14,'Soft Credit','1.0','54','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(15,'Soft Credit','1.0','66','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(16,'Soft Credit','1.0','55','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(17,'Soft Credit','1.0','56','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(18,'Soft Credit','1.0','96','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(19,'Soft Credit','1.0','97','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(20,'Soft Credit','1.0','58','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(21,'Soft Credit','1.0','59','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(22,'Soft Credit','1.0','60','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(23,'Soft Credit','1.0','61','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(24,'Soft Credit','1.0','62','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(25,'Soft Credit','1.0','67','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(26,'Soft Credit','1.0','77','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(27,'Soft Credit','1.0','68','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(28,'Soft Credit','1.0','69','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(29,'Soft Credit','1.0','70','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(30,'Soft Credit','1.0','71','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(31,'Soft Credit','1.0','72','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(32,'Soft Credit','1.0','73','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(33,'Soft Credit','1.0','74','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(34,'Soft Credit','1.0','75','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(35,'Soft Credit','1.0','76','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(36,'Soft Credit','1.0','78','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(37,'Soft Credit','1.0','88','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(38,'Soft Credit','1.0','79','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(39,'Soft Credit','1.0','80','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(40,'Soft Credit','1.0','81','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(41,'Soft Credit','1.0','82','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(42,'Soft Credit','1.0','83','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(43,'Soft Credit','1.0','84','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(44,'Soft Credit','1.0','85','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(45,'Soft Credit','1.0','86','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(46,'Soft Credit','1.0','87','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(47,'Soft Credit','1.0','90','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(48,'Soft Credit','1.0','89','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(49,'Soft Credit','1.0','91','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(50,'Soft Credit','1.0','63','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(51,'Soft Credit','1.0','64','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(52,'Soft Credit','1.0','65','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(53,'Soft Credit','1.0','92','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(54,'Soft Credit','1.0','93','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(55,'Soft Credit','1.0','94','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(56,'Soft Credit','1.0','95','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(57,'Soft Credit','1.0','98','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(58,'Soft Credit','1.0','99','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(59,'Soft Credit','1.0','1','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(60,'Soft Credit','1.0','100','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(61,'Soft Credit','1.0','5','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(62,'Soft Credit','1.0','2','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(63,'Soft Credit','1.0','3','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(64,'Soft Credit','1.0','6','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(65,'Soft Credit','1.0','7','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(66,'Soft Credit','1.0','8','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(67,'Soft Credit','1.0','9','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(68,'Soft Credit','1.0','10','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(69,'Soft Credit','1.0','11','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(70,'Soft Credit','1.0','14','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(71,'Soft Credit','1.0','12','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(72,'Soft Credit','1.0','13','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(73,'Soft Credit','1.0','15','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(74,'Soft Credit','1.0','16','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(75,'Soft Credit','1.0','17','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(76,'Soft Credit','1.0','18','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(77,'Soft Credit','1.0','19','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(78,'Soft Credit','1.0','20','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(79,'Soft Credit','1.0','21','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(80,'Soft Credit','1.0','26','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(81,'Soft Credit','1.0','25','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(82,'Soft Credit','1.0','4','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(83,'Soft Credit','1.0','27','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(84,'Soft Credit','1.0','28','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(85,'Soft Credit','1.0','29','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(86,'Soft Credit','1.0','30','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(87,'Soft Credit','1.0','31','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(88,'Soft Credit','1.0','32','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(89,'Soft Credit','1.0','33','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(90,'Soft Credit','1.0','34','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(91,'Soft Credit','1.0','35','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(92,'Soft Credit','1.0','36','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(93,'Soft Credit','1.0','37','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(94,'Soft Credit','1.0','38','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(95,'Soft Credit','1.0','39','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(96,'Soft Credit','1.0','40','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(97,'Soft Credit','1.0','41','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(98,'Soft Credit','1.0','42','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(99,'Soft Credit','1.0','43','94'); -INSERT INTO "Partial_Soft_Credit__c" VALUES(100,'Soft Credit','1.0','44','94'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(1,'Soft Credit','260.0','10','265'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(2,'Soft Credit','75.0','9','311'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(3,'Soft Credit','355.0','10','254'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(4,'Soft Credit','1.0','36','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(5,'Soft Credit','1.0','37','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(6,'Soft Credit','1.0','38','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(7,'Soft Credit','1.0','12','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(8,'Soft Credit','1.0','39','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(9,'Soft Credit','1.0','40','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(10,'Soft Credit','1.0','41','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(11,'Soft Credit','1.0','42','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(12,'Soft Credit','1.0','43','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(13,'Soft Credit','1.0','44','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(14,'Soft Credit','1.0','45','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(15,'Soft Credit','1.0','46','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(16,'Soft Credit','1.0','47','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(17,'Soft Credit','1.0','48','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(18,'Soft Credit','1.0','13','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(19,'Soft Credit','1.0','66','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(20,'Soft Credit','1.0','67','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(21,'Soft Credit','1.0','69','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(22,'Soft Credit','1.0','70','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(23,'Soft Credit','1.0','71','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(24,'Soft Credit','1.0','72','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(25,'Soft Credit','1.0','73','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(26,'Soft Credit','1.0','74','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(27,'Soft Credit','1.0','75','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(28,'Soft Credit','1.0','90','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(29,'Soft Credit','1.0','17','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(30,'Soft Credit','1.0','91','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(31,'Soft Credit','1.0','92','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(32,'Soft Credit','1.0','93','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(33,'Soft Credit','1.0','94','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(34,'Soft Credit','1.0','95','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(35,'Soft Credit','1.0','96','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(36,'Soft Credit','1.0','89','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(37,'Soft Credit','200.0','39','335'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(38,'Soft Credit','1.0','76','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(39,'Soft Credit','1.0','14','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(40,'Soft Credit','1.0','77','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(41,'Soft Credit','1.0','78','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(42,'Soft Credit','1.0','49','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(43,'Soft Credit','1.0','50','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(44,'Soft Credit','1.0','51','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(45,'Soft Credit','1.0','52','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(46,'Soft Credit','1.0','53','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(47,'Soft Credit','1.0','57','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(48,'Soft Credit','1.0','56','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(49,'Soft Credit','1.0','58','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(50,'Soft Credit','1.0','15','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(51,'Soft Credit','1.0','59','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(52,'Soft Credit','1.0','60','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(53,'Soft Credit','1.0','61','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(54,'Soft Credit','1.0','62','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(55,'Soft Credit','1.0','63','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(56,'Soft Credit','1.0','64','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(57,'Soft Credit','1.0','68','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(58,'Soft Credit','1.0','65','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(59,'Soft Credit','1.0','79','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(60,'Soft Credit','1.0','80','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(61,'Soft Credit','1.0','16','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(62,'Soft Credit','1.0','81','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(63,'Soft Credit','1.0','82','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(64,'Soft Credit','1.0','83','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(65,'Soft Credit','1.0','84','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(66,'Soft Credit','1.0','85','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(67,'Soft Credit','1.0','86','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(68,'Soft Credit','1.0','87','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(69,'Soft Credit','1.0','88','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(70,'Soft Credit','1.0','19','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(71,'Soft Credit','1.0','8','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(72,'Soft Credit','1.0','20','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(73,'Soft Credit','1.0','21','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(74,'Soft Credit','1.0','22','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(75,'Soft Credit','1.0','23','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(76,'Soft Credit','1.0','24','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(77,'Soft Credit','1.0','25','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(78,'Soft Credit','1.0','26','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(79,'Soft Credit','1.0','27','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(80,'Soft Credit','1.0','28','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(81,'Soft Credit','1.0','11','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(82,'Soft Credit','1.0','29','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(83,'Soft Credit','1.0','30','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(84,'Soft Credit','1.0','31','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(85,'Soft Credit','1.0','32','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(86,'Soft Credit','1.0','33','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(87,'Soft Credit','1.0','34','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(88,'Soft Credit','1.0','35','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(89,'Soft Credit','1.0','97','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(90,'Soft Credit','1.0','98','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(91,'Soft Credit','1.0','99','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(92,'Soft Credit','1.0','100','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(93,'Soft Credit','1.0','18','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(94,'Soft Credit','1.0','101','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(95,'Soft Credit','1.0','102','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(96,'Soft Credit','1.0','4','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(97,'Soft Credit','1.0','5','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(98,'Soft Credit','1.0','54','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(99,'Soft Credit','1.0','2','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(100,'Soft Credit','1.0','3','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(101,'Soft Credit','1.0','55','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(102,'Soft Credit','1.0','7','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(103,'Soft Credit','1.0','6','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(104,'Soft Credit','1.0','10','125'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(105,'Soft Credit','135.0','10','276'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(106,'Soft Credit','167.0','10','334'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(107,'Soft Credit','234.0','10','314'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(108,'Soft Credit','34.0','10','336'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(109,'Soft Credit','228.0','10','340'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(110,'Soft Credit','50.0','39','326'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(111,'Soft Credit','166.0','10','31'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(112,'Soft Credit','250.0','10','260'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(113,'Soft Credit','11.0','10','269'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(114,'Soft Credit','1345.0','10','321'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(115,'Soft Credit','200.0','10','327'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(116,'Soft Credit','30.0','10','28'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(117,'Soft Credit','75.0','10','66'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(118,'Soft Credit','27.0','10','339'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(119,'Soft Credit','12.0','10','275'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(120,'Household Member','380.0','10','255'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(121,'Soft Credit','236.0','10','34'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(122,'Soft Credit','50.0','10','267'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(123,'Soft Credit','45.0','10','32'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(124,'Soft Credit','56.0','10','322'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(125,'Soft Credit','25.0','10','264'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(126,'Soft Credit','68.0','10','315'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(127,'Household Member','98.0','10','268'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(128,'Soft Credit','112.0','10','273'); +INSERT INTO "Partial_Soft_Credit__c" VALUES(129,'Soft Credit','20.0','10','274'); CREATE TABLE "npe01__OppPayment__c" ( - id INTEGER NOT NULL, - "npe01__Payment_Amount__c" VARCHAR(255), - "npe01__Payment_Date__c" VARCHAR(255), - "npe01__Scheduled_Date__c" VARCHAR(255), - "npe01__Paid__c" VARCHAR(255), - "npe01__Written_Off__c" VARCHAR(255), - npe01__opportunity__c VARCHAR(255), + id INTEGER NOT NULL, + "npe01__Payment_Amount__c" VARCHAR(255), + "npe01__Payment_Date__c" VARCHAR(255), + "npe01__Scheduled_Date__c" VARCHAR(255), + "npe01__Paid__c" VARCHAR(255), + "npe01__Written_Off__c" VARCHAR(255), + "npe01__Opportunity__c" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "npe01__OppPayment__c" VALUES(1,'75.0','','2020-01-15','false','false','211'); -INSERT INTO "npe01__OppPayment__c" VALUES(2,'75.0','','2020-02-15','false','false','212'); -INSERT INTO "npe01__OppPayment__c" VALUES(3,'75.0','','2020-03-15','false','false','213'); -INSERT INTO "npe01__OppPayment__c" VALUES(4,'75.0','','2020-04-15','false','false','214'); -INSERT INTO "npe01__OppPayment__c" VALUES(5,'75.0','','2020-05-15','false','false','219'); -INSERT INTO "npe01__OppPayment__c" VALUES(6,'75.0','','2020-06-15','false','false','220'); -INSERT INTO "npe01__OppPayment__c" VALUES(7,'75.0','','2020-07-15','false','false','221'); -INSERT INTO "npe01__OppPayment__c" VALUES(8,'75.0','','2020-08-15','false','false','215'); -INSERT INTO "npe01__OppPayment__c" VALUES(9,'75.0','','2020-09-15','false','false','216'); -INSERT INTO "npe01__OppPayment__c" VALUES(10,'75.0','','2020-10-15','false','false','217'); -INSERT INTO "npe01__OppPayment__c" VALUES(11,'75.0','','2020-11-15','false','false','218'); -INSERT INTO "npe01__OppPayment__c" VALUES(12,'785.8','2014-11-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(13,'785.8','2014-12-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(14,'181.0','2016-12-04','','true','false','20'); -INSERT INTO "npe01__OppPayment__c" VALUES(15,'3422.0','2017-01-31','','true','false','21'); -INSERT INTO "npe01__OppPayment__c" VALUES(16,'2434.0','2016-11-02','','true','false','22'); -INSERT INTO "npe01__OppPayment__c" VALUES(17,'3738.0','2017-08-02','','true','false','33'); -INSERT INTO "npe01__OppPayment__c" VALUES(18,'6351.0','2017-12-29','','true','false','34'); -INSERT INTO "npe01__OppPayment__c" VALUES(19,'2233.0','2017-05-18','','true','false','35'); -INSERT INTO "npe01__OppPayment__c" VALUES(20,'546.0','2017-04-10','','true','false','101'); -INSERT INTO "npe01__OppPayment__c" VALUES(21,'3361.0','2016-09-08','','true','false','36'); -INSERT INTO "npe01__OppPayment__c" VALUES(22,'3124.0','2018-05-15','','true','false','37'); -INSERT INTO "npe01__OppPayment__c" VALUES(23,'2909.0','2018-05-16','','true','false','38'); -INSERT INTO "npe01__OppPayment__c" VALUES(24,'1824.0','2017-07-30','','true','false','39'); -INSERT INTO "npe01__OppPayment__c" VALUES(25,'4670.0','2016-09-17','','true','false','40'); -INSERT INTO "npe01__OppPayment__c" VALUES(26,'5400.0','2017-03-22','','true','false','41'); -INSERT INTO "npe01__OppPayment__c" VALUES(27,'9404.0','2016-02-15','','true','false','42'); -INSERT INTO "npe01__OppPayment__c" VALUES(28,'218.0','2018-11-23','','true','false','11'); -INSERT INTO "npe01__OppPayment__c" VALUES(29,'376.0','2018-10-13','','true','false','12'); -INSERT INTO "npe01__OppPayment__c" VALUES(30,'744.0','2018-04-26','','true','false','31'); -INSERT INTO "npe01__OppPayment__c" VALUES(31,'316.0','2018-10-07','','true','false','102'); -INSERT INTO "npe01__OppPayment__c" VALUES(32,'619.0','2016-09-11','','true','false','32'); -INSERT INTO "npe01__OppPayment__c" VALUES(33,'13.0','2018-02-28','','true','false','128'); -INSERT INTO "npe01__OppPayment__c" VALUES(34,'609.0','2016-12-18','','true','false','129'); -INSERT INTO "npe01__OppPayment__c" VALUES(35,'962.0','2018-01-28','','true','false','130'); -INSERT INTO "npe01__OppPayment__c" VALUES(36,'204.0','2017-12-25','','true','false','131'); -INSERT INTO "npe01__OppPayment__c" VALUES(37,'153.0','2017-06-01','','true','false','132'); -INSERT INTO "npe01__OppPayment__c" VALUES(38,'89.0','2018-01-23','','true','false','133'); -INSERT INTO "npe01__OppPayment__c" VALUES(39,'331.0','2018-01-26','','true','false','134'); -INSERT INTO "npe01__OppPayment__c" VALUES(40,'272.0','2016-12-10','','true','false','201'); -INSERT INTO "npe01__OppPayment__c" VALUES(41,'261.0','2018-06-06','','true','false','202'); -INSERT INTO "npe01__OppPayment__c" VALUES(42,'921.0','2018-05-04','','true','false','203'); -INSERT INTO "npe01__OppPayment__c" VALUES(43,'75.7','2014-08-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(44,'75.7','2014-09-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(45,'75.7','2014-10-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(46,'75.7','2014-11-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(47,'75.7','2014-12-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(48,'75.7','2015-01-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(49,'75.7','2015-02-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(50,'75.7','2015-03-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(51,'75.7','2015-04-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(52,'75.7','2015-05-28','','false','false','204'); -INSERT INTO "npe01__OppPayment__c" VALUES(53,'375.0','2016-09-04','','true','false','205'); -INSERT INTO "npe01__OppPayment__c" VALUES(54,'698.0','2016-03-23','','true','false','206'); -INSERT INTO "npe01__OppPayment__c" VALUES(55,'892.0','2017-04-20','','true','false','207'); -INSERT INTO "npe01__OppPayment__c" VALUES(56,'8.5','2015-08-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(57,'8.5','2015-09-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(58,'8.5','2015-10-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(59,'8.5','2015-11-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(60,'8.5','2015-12-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(61,'8.5','2016-01-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(62,'8.5','2016-03-01','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(63,'8.5','2016-03-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(64,'8.5','2016-04-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(65,'8.5','2016-05-30','','false','false','208'); -INSERT INTO "npe01__OppPayment__c" VALUES(66,'830.0','2017-01-05','','true','false','209'); -INSERT INTO "npe01__OppPayment__c" VALUES(67,'656.0','2018-01-15','','true','false','210'); -INSERT INTO "npe01__OppPayment__c" VALUES(68,'842.0','2017-07-29','','true','false','135'); -INSERT INTO "npe01__OppPayment__c" VALUES(69,'736.0','2016-09-14','','true','false','136'); -INSERT INTO "npe01__OppPayment__c" VALUES(70,'978.0','2016-03-20','','true','false','137'); -INSERT INTO "npe01__OppPayment__c" VALUES(71,'927.0','2017-04-24','','true','false','138'); -INSERT INTO "npe01__OppPayment__c" VALUES(72,'321.0','2016-05-17','','true','false','139'); -INSERT INTO "npe01__OppPayment__c" VALUES(73,'282.0','2016-02-04','','true','false','140'); -INSERT INTO "npe01__OppPayment__c" VALUES(74,'750.0','2016-02-21','','true','false','141'); -INSERT INTO "npe01__OppPayment__c" VALUES(75,'996.0','2016-06-16','','true','false','142'); -INSERT INTO "npe01__OppPayment__c" VALUES(76,'536.0','2019-01-14','','true','false','143'); -INSERT INTO "npe01__OppPayment__c" VALUES(77,'756.0','2018-11-10','','true','false','144'); -INSERT INTO "npe01__OppPayment__c" VALUES(78,'119.0','2018-04-03','','true','false','145'); -INSERT INTO "npe01__OppPayment__c" VALUES(79,'309.0','2016-11-01','','true','false','146'); -INSERT INTO "npe01__OppPayment__c" VALUES(80,'230.0','2017-10-06','','true','false','147'); -INSERT INTO "npe01__OppPayment__c" VALUES(81,'118.0','2017-04-18','','true','false','148'); -INSERT INTO "npe01__OppPayment__c" VALUES(82,'32.7','2014-03-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(83,'32.7','2014-04-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(84,'32.7','2014-05-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(85,'32.7','2014-06-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(86,'32.7','2014-07-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(87,'32.7','2014-08-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(88,'32.7','2014-09-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(89,'32.7','2014-10-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(90,'32.7','2014-11-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(91,'32.7','2014-12-23','','false','false','149'); -INSERT INTO "npe01__OppPayment__c" VALUES(92,'557.0','2018-01-07','','true','false','150'); -INSERT INTO "npe01__OppPayment__c" VALUES(93,'642.0','2018-02-08','','true','false','151'); -INSERT INTO "npe01__OppPayment__c" VALUES(94,'782.0','2017-04-19','','true','false','152'); -INSERT INTO "npe01__OppPayment__c" VALUES(95,'409.0','2017-05-21','','true','false','153'); -INSERT INTO "npe01__OppPayment__c" VALUES(96,'501.0','2016-11-30','','true','false','154'); -INSERT INTO "npe01__OppPayment__c" VALUES(97,'998.0','2017-08-18','','true','false','155'); -INSERT INTO "npe01__OppPayment__c" VALUES(98,'338.0','2017-01-01','','true','false','156'); -INSERT INTO "npe01__OppPayment__c" VALUES(99,'737.0','2018-12-15','','true','false','157'); -INSERT INTO "npe01__OppPayment__c" VALUES(100,'410.0','2018-04-12','','true','false','158'); -INSERT INTO "npe01__OppPayment__c" VALUES(101,'202.0','2018-10-16','','true','false','159'); -INSERT INTO "npe01__OppPayment__c" VALUES(102,'997.0','2019-01-29','','true','false','160'); -INSERT INTO "npe01__OppPayment__c" VALUES(103,'609.0','2016-06-09','','true','false','161'); -INSERT INTO "npe01__OppPayment__c" VALUES(104,'952.0','2018-06-15','','true','false','162'); -INSERT INTO "npe01__OppPayment__c" VALUES(105,'734.0','2017-02-01','','true','false','163'); -INSERT INTO "npe01__OppPayment__c" VALUES(106,'54.0','2016-08-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(107,'54.0','2016-09-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(108,'54.0','2016-10-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(109,'54.0','2016-11-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(110,'54.0','2016-12-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(111,'54.0','2017-01-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(112,'54.0','2017-02-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(113,'54.0','2017-03-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(114,'54.0','2017-04-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(115,'54.0','2017-05-28','','false','false','164'); -INSERT INTO "npe01__OppPayment__c" VALUES(116,'507.0','2017-04-23','','true','false','165'); -INSERT INTO "npe01__OppPayment__c" VALUES(117,'976.0','2017-11-07','','true','false','166'); -INSERT INTO "npe01__OppPayment__c" VALUES(118,'973.0','2017-11-20','','true','false','167'); -INSERT INTO "npe01__OppPayment__c" VALUES(119,'837.0','2018-03-22','','true','false','168'); -INSERT INTO "npe01__OppPayment__c" VALUES(120,'871.0','2018-09-25','','true','false','169'); -INSERT INTO "npe01__OppPayment__c" VALUES(121,'839.0','2018-11-11','','true','false','194'); -INSERT INTO "npe01__OppPayment__c" VALUES(122,'451.0','2018-06-22','','true','false','195'); -INSERT INTO "npe01__OppPayment__c" VALUES(123,'236.0','2019-01-18','','true','false','196'); -INSERT INTO "npe01__OppPayment__c" VALUES(124,'504.0','2017-06-15','','true','false','170'); -INSERT INTO "npe01__OppPayment__c" VALUES(125,'234.0','2017-12-20','','true','false','171'); -INSERT INTO "npe01__OppPayment__c" VALUES(126,'762.0','2016-03-31','','true','false','172'); -INSERT INTO "npe01__OppPayment__c" VALUES(127,'357.0','2017-10-24','','true','false','173'); -INSERT INTO "npe01__OppPayment__c" VALUES(128,'314.0','2018-12-27','','true','false','84'); -INSERT INTO "npe01__OppPayment__c" VALUES(129,'992.0','2018-02-28','','true','false','174'); -INSERT INTO "npe01__OppPayment__c" VALUES(130,'666.0','2016-07-19','','true','false','175'); -INSERT INTO "npe01__OppPayment__c" VALUES(131,'128.0','2017-04-22','','true','false','176'); -INSERT INTO "npe01__OppPayment__c" VALUES(132,'111.0','2016-10-31','','true','false','177'); -INSERT INTO "npe01__OppPayment__c" VALUES(133,'719.0','2016-06-10','','true','false','178'); -INSERT INTO "npe01__OppPayment__c" VALUES(134,'22.0','2018-06-07','','true','false','179'); -INSERT INTO "npe01__OppPayment__c" VALUES(135,'844.0','2017-05-30','','true','false','180'); -INSERT INTO "npe01__OppPayment__c" VALUES(136,'989.0','2017-10-02','','true','false','181'); -INSERT INTO "npe01__OppPayment__c" VALUES(137,'368.0','2017-09-04','','true','false','182'); -INSERT INTO "npe01__OppPayment__c" VALUES(138,'896.0','2016-03-07','','true','false','183'); -INSERT INTO "npe01__OppPayment__c" VALUES(139,'108.0','2018-07-01','','true','false','184'); -INSERT INTO "npe01__OppPayment__c" VALUES(140,'911.0','2017-08-17','','true','false','185'); -INSERT INTO "npe01__OppPayment__c" VALUES(141,'834.0','2017-04-08','','true','false','186'); -INSERT INTO "npe01__OppPayment__c" VALUES(142,'45.0','2016-12-31','','true','false','187'); -INSERT INTO "npe01__OppPayment__c" VALUES(143,'99.0','2017-11-25','','true','false','188'); -INSERT INTO "npe01__OppPayment__c" VALUES(144,'971.0','2016-10-07','','true','false','189'); -INSERT INTO "npe01__OppPayment__c" VALUES(145,'639.0','2018-08-19','','true','false','190'); -INSERT INTO "npe01__OppPayment__c" VALUES(146,'919.0','2018-01-13','','true','false','191'); -INSERT INTO "npe01__OppPayment__c" VALUES(147,'476.0','2016-03-22','','true','false','192'); -INSERT INTO "npe01__OppPayment__c" VALUES(148,'321.0','2017-03-19','','true','false','193'); -INSERT INTO "npe01__OppPayment__c" VALUES(149,'585.0','2018-08-25','','true','false','197'); -INSERT INTO "npe01__OppPayment__c" VALUES(150,'546.0','2017-08-29','','true','false','198'); -INSERT INTO "npe01__OppPayment__c" VALUES(151,'48.5','2014-06-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(152,'48.5','2014-07-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(153,'48.5','2014-08-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(154,'48.5','2014-09-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(155,'48.5','2014-10-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(156,'48.5','2014-11-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(157,'48.5','2014-12-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(158,'48.5','2015-01-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(159,'48.5','2015-03-01','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(160,'48.5','2015-03-29','','false','false','199'); -INSERT INTO "npe01__OppPayment__c" VALUES(161,'329.0','2018-09-18','','true','false','200'); -INSERT INTO "npe01__OppPayment__c" VALUES(162,'750.0','2017-09-03','','true','false','103'); -INSERT INTO "npe01__OppPayment__c" VALUES(163,'976.0','2016-06-01','','true','false','104'); -INSERT INTO "npe01__OppPayment__c" VALUES(164,'22.0','2016-03-06','','true','false','105'); -INSERT INTO "npe01__OppPayment__c" VALUES(165,'436.0','2019-01-04','','true','false','85'); -INSERT INTO "npe01__OppPayment__c" VALUES(166,'627.0','2018-10-08','','true','false','106'); -INSERT INTO "npe01__OppPayment__c" VALUES(167,'965.0','2018-01-01','','true','false','107'); -INSERT INTO "npe01__OppPayment__c" VALUES(168,'116.0','2018-05-15','','true','false','108'); -INSERT INTO "npe01__OppPayment__c" VALUES(169,'260.0','2018-12-06','','true','false','109'); -INSERT INTO "npe01__OppPayment__c" VALUES(170,'365.0','2018-08-07','','true','false','110'); -INSERT INTO "npe01__OppPayment__c" VALUES(171,'550.0','2018-06-25','','true','false','111'); -INSERT INTO "npe01__OppPayment__c" VALUES(172,'13.1','2016-08-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(173,'13.1','2016-09-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(174,'13.1','2016-10-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(175,'13.1','2016-11-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(176,'13.1','2016-12-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(177,'13.1','2017-01-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(178,'13.1','2017-02-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(179,'13.1','2017-03-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(180,'13.1','2017-04-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(181,'13.1','2017-05-02','','false','false','112'); -INSERT INTO "npe01__OppPayment__c" VALUES(182,'631.0','2016-07-21','','true','false','113'); -INSERT INTO "npe01__OppPayment__c" VALUES(183,'970.0','2018-09-15','','true','false','114'); -INSERT INTO "npe01__OppPayment__c" VALUES(184,'59.0','2018-06-07','','true','false','115'); -INSERT INTO "npe01__OppPayment__c" VALUES(185,'157.0','2016-12-28','','true','false','86'); -INSERT INTO "npe01__OppPayment__c" VALUES(186,'253.0','2018-11-20','','true','false','116'); -INSERT INTO "npe01__OppPayment__c" VALUES(187,'421.0','2016-04-23','','true','false','117'); -INSERT INTO "npe01__OppPayment__c" VALUES(188,'743.0','2018-09-03','','true','false','118'); -INSERT INTO "npe01__OppPayment__c" VALUES(189,'37.0','2017-11-19','','true','false','119'); -INSERT INTO "npe01__OppPayment__c" VALUES(190,'409.0','2017-03-29','','true','false','68'); -INSERT INTO "npe01__OppPayment__c" VALUES(191,'858.0','2018-03-05','','true','false','69'); -INSERT INTO "npe01__OppPayment__c" VALUES(192,'146.0','2017-11-27','','true','false','120'); -INSERT INTO "npe01__OppPayment__c" VALUES(193,'657.0','2016-09-08','','true','false','121'); -INSERT INTO "npe01__OppPayment__c" VALUES(194,'36.0','2016-09-02','','true','false','122'); -INSERT INTO "npe01__OppPayment__c" VALUES(195,'39.3','2015-05-18','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(196,'39.3','2015-06-18','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(197,'39.3','2015-07-18','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(198,'39.3','2015-08-18','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(199,'39.3','2015-09-18','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(200,'39.3','2015-10-18','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(201,'39.3','2015-11-19','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(202,'39.3','2015-12-19','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(203,'39.3','2016-01-19','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(204,'39.3','2016-02-19','','false','false','123'); -INSERT INTO "npe01__OppPayment__c" VALUES(205,'377.0','2018-06-16','','true','false','93'); -INSERT INTO "npe01__OppPayment__c" VALUES(206,'64.3','2017-01-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(207,'64.3','2017-02-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(208,'64.3','2017-03-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(209,'64.3','2017-04-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(210,'64.3','2017-05-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(211,'64.3','2017-06-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(212,'64.3','2017-07-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(213,'64.3','2017-08-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(214,'64.3','2017-09-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(215,'64.3','2017-10-24','','false','false','124'); -INSERT INTO "npe01__OppPayment__c" VALUES(216,'813.0','2017-04-10','','true','false','125'); -INSERT INTO "npe01__OppPayment__c" VALUES(217,'557.0','2017-12-02','','true','false','126'); -INSERT INTO "npe01__OppPayment__c" VALUES(218,'926.0','2018-11-10','','true','false','127'); -INSERT INTO "npe01__OppPayment__c" VALUES(219,'457.0','2017-08-27','','true','false','23'); -INSERT INTO "npe01__OppPayment__c" VALUES(220,'13.0','2017-01-19','','true','false','24'); -INSERT INTO "npe01__OppPayment__c" VALUES(221,'234.0','2017-11-04','','true','false','25'); -INSERT INTO "npe01__OppPayment__c" VALUES(222,'84.0','2016-05-05','','true','false','26'); -INSERT INTO "npe01__OppPayment__c" VALUES(223,'952.0','2018-11-16','','true','false','27'); -INSERT INTO "npe01__OppPayment__c" VALUES(224,'3.0','2017-04-20','','true','false','28'); -INSERT INTO "npe01__OppPayment__c" VALUES(225,'654.0','2016-03-02','','true','false','94'); -INSERT INTO "npe01__OppPayment__c" VALUES(226,'521.0','2016-09-22','','true','false','29'); -INSERT INTO "npe01__OppPayment__c" VALUES(227,'567.0','2016-02-26','','true','false','30'); -INSERT INTO "npe01__OppPayment__c" VALUES(228,'504.0','2017-03-01','','true','false','43'); -INSERT INTO "npe01__OppPayment__c" VALUES(229,'109.0','2019-02-07','','true','false','44'); -INSERT INTO "npe01__OppPayment__c" VALUES(230,'479.0','2017-08-07','','true','false','45'); -INSERT INTO "npe01__OppPayment__c" VALUES(231,'270.0','2016-10-28','','true','false','46'); -INSERT INTO "npe01__OppPayment__c" VALUES(232,'31.0','2019-01-20','','true','false','47'); -INSERT INTO "npe01__OppPayment__c" VALUES(233,'157.0','2018-04-24','','true','false','48'); -INSERT INTO "npe01__OppPayment__c" VALUES(234,'31.0','2017-09-28','','true','false','49'); -INSERT INTO "npe01__OppPayment__c" VALUES(235,'836.0','2019-01-12','','true','false','50'); -INSERT INTO "npe01__OppPayment__c" VALUES(236,'926.0','2017-01-18','','true','false','95'); -INSERT INTO "npe01__OppPayment__c" VALUES(237,'984.0','2016-03-20','','true','false','51'); -INSERT INTO "npe01__OppPayment__c" VALUES(238,'862.0','2017-08-04','','true','false','52'); -INSERT INTO "npe01__OppPayment__c" VALUES(239,'433.0','2018-01-22','','true','false','53'); -INSERT INTO "npe01__OppPayment__c" VALUES(240,'794.0','2018-05-02','','true','false','54'); -INSERT INTO "npe01__OppPayment__c" VALUES(241,'6.0','2018-04-27','','true','false','55'); -INSERT INTO "npe01__OppPayment__c" VALUES(242,'87.0','2017-07-30','','true','false','56'); -INSERT INTO "npe01__OppPayment__c" VALUES(243,'641.0','2018-10-05','','true','false','57'); -INSERT INTO "npe01__OppPayment__c" VALUES(244,'29.0','2017-04-01','','true','false','58'); -INSERT INTO "npe01__OppPayment__c" VALUES(245,'915.0','2016-07-21','','true','false','59'); -INSERT INTO "npe01__OppPayment__c" VALUES(246,'907.0','2018-12-19','','true','false','60'); -INSERT INTO "npe01__OppPayment__c" VALUES(247,'838.0','2017-07-29','','true','false','96'); -INSERT INTO "npe01__OppPayment__c" VALUES(248,'905.0','2017-09-13','','true','false','61'); -INSERT INTO "npe01__OppPayment__c" VALUES(249,'957.0','2016-11-01','','true','false','62'); -INSERT INTO "npe01__OppPayment__c" VALUES(250,'547.0','2017-09-16','','true','false','63'); -INSERT INTO "npe01__OppPayment__c" VALUES(251,'235.0','2017-07-08','','true','false','64'); -INSERT INTO "npe01__OppPayment__c" VALUES(252,'502.0','2017-12-11','','true','false','1'); -INSERT INTO "npe01__OppPayment__c" VALUES(253,'635.0','2018-05-04','','true','false','2'); -INSERT INTO "npe01__OppPayment__c" VALUES(254,'514.0','2017-08-28','','true','false','65'); -INSERT INTO "npe01__OppPayment__c" VALUES(255,'70.0','2018-07-06','','true','false','66'); -INSERT INTO "npe01__OppPayment__c" VALUES(256,'401.0','2016-04-13','','true','false','67'); -INSERT INTO "npe01__OppPayment__c" VALUES(257,'466.0','2016-12-10','','true','false','70'); -INSERT INTO "npe01__OppPayment__c" VALUES(258,'46.0','2018-07-03','','true','false','97'); -INSERT INTO "npe01__OppPayment__c" VALUES(259,'344.0','2018-01-07','','true','false','71'); -INSERT INTO "npe01__OppPayment__c" VALUES(260,'110.0','2017-05-08','','true','false','72'); -INSERT INTO "npe01__OppPayment__c" VALUES(261,'820.0','2018-01-26','','true','false','73'); -INSERT INTO "npe01__OppPayment__c" VALUES(262,'492.0','2018-09-09','','true','false','74'); -INSERT INTO "npe01__OppPayment__c" VALUES(263,'892.0','2018-10-25','','true','false','75'); -INSERT INTO "npe01__OppPayment__c" VALUES(264,'798.0','2017-05-13','','true','false','76'); -INSERT INTO "npe01__OppPayment__c" VALUES(265,'265.0','2016-04-17','','true','false','77'); -INSERT INTO "npe01__OppPayment__c" VALUES(266,'934.0','2018-04-23','','true','false','78'); -INSERT INTO "npe01__OppPayment__c" VALUES(267,'599.0','2016-11-23','','true','false','79'); -INSERT INTO "npe01__OppPayment__c" VALUES(268,'538.0','2018-10-03','','true','false','80'); -INSERT INTO "npe01__OppPayment__c" VALUES(269,'782.0','2017-03-21','','true','false','83'); -INSERT INTO "npe01__OppPayment__c" VALUES(270,'440.0','2016-12-12','','true','false','98'); -INSERT INTO "npe01__OppPayment__c" VALUES(271,'993.0','2016-04-21','','true','false','81'); -INSERT INTO "npe01__OppPayment__c" VALUES(272,'4.0','2017-04-22','','true','false','82'); -INSERT INTO "npe01__OppPayment__c" VALUES(273,'685.0','2017-08-07','','true','false','87'); -INSERT INTO "npe01__OppPayment__c" VALUES(274,'974.0','2017-07-18','','true','false','88'); -INSERT INTO "npe01__OppPayment__c" VALUES(275,'751.0','2019-01-30','','true','false','89'); -INSERT INTO "npe01__OppPayment__c" VALUES(276,'449.0','2016-10-17','','true','false','90'); -INSERT INTO "npe01__OppPayment__c" VALUES(277,'643.0','2017-09-26','','true','false','91'); -INSERT INTO "npe01__OppPayment__c" VALUES(278,'127.0','2018-12-13','','true','false','92'); -INSERT INTO "npe01__OppPayment__c" VALUES(279,'177.0','2018-11-23','','true','false','3'); -INSERT INTO "npe01__OppPayment__c" VALUES(280,'353.0','2018-07-09','','true','false','99'); -INSERT INTO "npe01__OppPayment__c" VALUES(281,'208.0','2018-01-20','','true','false','4'); -INSERT INTO "npe01__OppPayment__c" VALUES(282,'553.0','2017-10-15','','true','false','5'); -INSERT INTO "npe01__OppPayment__c" VALUES(283,'565.0','2018-09-06','','true','false','6'); -INSERT INTO "npe01__OppPayment__c" VALUES(284,'310.0','2018-02-02','','true','false','7'); -INSERT INTO "npe01__OppPayment__c" VALUES(285,'483.0','2016-06-11','','true','false','8'); -INSERT INTO "npe01__OppPayment__c" VALUES(286,'151.0','2018-02-20','','true','false','9'); -INSERT INTO "npe01__OppPayment__c" VALUES(287,'537.0','2016-05-31','','true','false','10'); -INSERT INTO "npe01__OppPayment__c" VALUES(288,'496.5','2015-01-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(289,'496.5','2015-02-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(290,'496.5','2015-03-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(291,'496.5','2015-04-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(292,'496.5','2015-05-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(293,'496.5','2015-06-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(294,'496.5','2015-07-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(295,'496.5','2015-08-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(296,'496.5','2015-09-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(297,'496.5','2015-10-02','','false','false','13'); -INSERT INTO "npe01__OppPayment__c" VALUES(298,'5297.0','2017-11-27','','true','false','14'); -INSERT INTO "npe01__OppPayment__c" VALUES(299,'6258.0','2016-07-24','','true','false','15'); -INSERT INTO "npe01__OppPayment__c" VALUES(300,'338.0','2016-08-31','','true','false','100'); -INSERT INTO "npe01__OppPayment__c" VALUES(301,'2804.0','2016-03-12','','true','false','16'); -INSERT INTO "npe01__OppPayment__c" VALUES(302,'7164.0','2016-03-09','','true','false','17'); -INSERT INTO "npe01__OppPayment__c" VALUES(303,'6989.0','2018-08-29','','true','false','18'); -INSERT INTO "npe01__OppPayment__c" VALUES(304,'785.8','2014-03-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(305,'785.8','2014-04-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(306,'785.8','2014-05-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(307,'785.8','2014-06-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(308,'785.8','2014-07-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(309,'785.8','2014-08-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(310,'785.8','2014-09-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(311,'785.8','2014-10-04','','false','false','19'); -INSERT INTO "npe01__OppPayment__c" VALUES(312,'75.0','2019-12-15','','true','false','222'); +INSERT INTO "npe01__OppPayment__c" VALUES(1,'60.0','','2021-11-11','False','False','317'); +INSERT INTO "npe01__OppPayment__c" VALUES(2,'680.0','2021-11-10','','True','False','320'); +INSERT INTO "npe01__OppPayment__c" VALUES(3,'509.0','2021-10-31','','True','False','318'); +INSERT INTO "npe01__OppPayment__c" VALUES(4,'11.0','2021-11-19','','True','False','319'); +INSERT INTO "npe01__OppPayment__c" VALUES(5,'13.1','2016-09-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(6,'13.1','2016-10-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(7,'13.1','2016-11-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(8,'13.1','2016-12-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(9,'13.1','2017-01-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(10,'13.1','2017-02-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(11,'13.1','2017-03-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(12,'13.1','2017-04-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(13,'13.1','2017-05-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(14,'631.0','2016-07-21','','True','False','146'); +INSERT INTO "npe01__OppPayment__c" VALUES(15,'970.0','2018-09-15','','True','False','147'); +INSERT INTO "npe01__OppPayment__c" VALUES(16,'59.0','2018-06-07','','True','False','148'); +INSERT INTO "npe01__OppPayment__c" VALUES(17,'253.0','2018-11-20','','True','False','149'); +INSERT INTO "npe01__OppPayment__c" VALUES(18,'421.0','2016-04-23','','True','False','150'); +INSERT INTO "npe01__OppPayment__c" VALUES(19,'743.0','2018-09-03','','True','False','151'); +INSERT INTO "npe01__OppPayment__c" VALUES(20,'37.0','2017-11-19','','True','False','152'); +INSERT INTO "npe01__OppPayment__c" VALUES(21,'376.0','2018-10-13','','True','False','44'); +INSERT INTO "npe01__OppPayment__c" VALUES(22,'146.0','2017-11-27','','True','False','153'); +INSERT INTO "npe01__OppPayment__c" VALUES(23,'657.0','2016-09-08','','True','False','154'); +INSERT INTO "npe01__OppPayment__c" VALUES(24,'36.0','2016-09-02','','True','False','155'); +INSERT INTO "npe01__OppPayment__c" VALUES(25,'39.3','2015-05-18','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(26,'39.3','2015-06-18','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(27,'39.3','2015-07-18','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(28,'39.3','2015-08-18','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(29,'39.3','2015-09-18','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(30,'39.3','2015-10-18','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(31,'39.3','2015-11-19','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(32,'39.3','2015-12-19','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(33,'39.3','2016-01-19','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(34,'39.3','2016-02-19','','False','False','156'); +INSERT INTO "npe01__OppPayment__c" VALUES(35,'64.3','2017-01-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(36,'64.3','2017-02-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(37,'64.3','2017-03-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(38,'64.3','2017-04-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(39,'64.3','2017-05-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(40,'64.3','2017-06-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(41,'64.3','2017-07-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(42,'64.3','2017-08-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(43,'64.3','2017-09-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(44,'64.3','2017-10-24','','False','False','157'); +INSERT INTO "npe01__OppPayment__c" VALUES(45,'813.0','2017-04-10','','True','False','158'); +INSERT INTO "npe01__OppPayment__c" VALUES(46,'557.0','2017-12-02','','True','False','159'); +INSERT INTO "npe01__OppPayment__c" VALUES(47,'926.0','2018-11-10','','True','False','160'); +INSERT INTO "npe01__OppPayment__c" VALUES(48,'13.0','2018-02-28','','True','False','161'); +INSERT INTO "npe01__OppPayment__c" VALUES(49,'609.0','2016-12-18','','True','False','162'); +INSERT INTO "npe01__OppPayment__c" VALUES(50,'496.5','2015-01-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(51,'496.5','2015-02-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(52,'496.5','2015-03-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(53,'496.5','2015-04-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(54,'496.5','2015-05-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(55,'496.5','2015-06-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(56,'496.5','2015-07-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(57,'496.5','2015-08-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(58,'496.5','2015-09-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(59,'496.5','2015-10-02','','False','False','45'); +INSERT INTO "npe01__OppPayment__c" VALUES(60,'962.0','2018-01-28','','True','False','163'); +INSERT INTO "npe01__OppPayment__c" VALUES(61,'204.0','2017-12-25','','True','False','164'); +INSERT INTO "npe01__OppPayment__c" VALUES(62,'153.0','2017-06-01','','True','False','165'); +INSERT INTO "npe01__OppPayment__c" VALUES(63,'89.0','2018-01-23','','True','False','166'); +INSERT INTO "npe01__OppPayment__c" VALUES(64,'331.0','2018-01-26','','True','False','167'); +INSERT INTO "npe01__OppPayment__c" VALUES(65,'842.0','2017-07-29','','True','False','168'); +INSERT INTO "npe01__OppPayment__c" VALUES(66,'736.0','2016-09-14','','True','False','169'); +INSERT INTO "npe01__OppPayment__c" VALUES(67,'69.0','2021-11-27','','True','False','256'); +INSERT INTO "npe01__OppPayment__c" VALUES(68,'66.0','2021-12-08','','True','False','257'); +INSERT INTO "npe01__OppPayment__c" VALUES(69,'88.0','2021-12-03','','True','False','258'); +INSERT INTO "npe01__OppPayment__c" VALUES(70,'433.0','2021-12-08','','True','False','259'); +INSERT INTO "npe01__OppPayment__c" VALUES(71,'978.0','2016-03-20','','True','False','170'); +INSERT INTO "npe01__OppPayment__c" VALUES(72,'927.0','2017-04-24','','True','False','171'); +INSERT INTO "npe01__OppPayment__c" VALUES(73,'321.0','2016-05-17','','True','False','172'); +INSERT INTO "npe01__OppPayment__c" VALUES(74,'5297.0','2017-11-27','','True','False','46'); +INSERT INTO "npe01__OppPayment__c" VALUES(75,'282.0','2016-02-04','','True','False','173'); +INSERT INTO "npe01__OppPayment__c" VALUES(76,'750.0','2016-02-21','','True','False','174'); +INSERT INTO "npe01__OppPayment__c" VALUES(77,'996.0','2016-06-16','','True','False','175'); +INSERT INTO "npe01__OppPayment__c" VALUES(78,'536.0','2019-01-14','','True','False','176'); +INSERT INTO "npe01__OppPayment__c" VALUES(79,'756.0','2018-11-10','','True','False','177'); +INSERT INTO "npe01__OppPayment__c" VALUES(80,'119.0','2018-04-03','','True','False','178'); +INSERT INTO "npe01__OppPayment__c" VALUES(81,'309.0','2016-11-01','','True','False','179'); +INSERT INTO "npe01__OppPayment__c" VALUES(82,'230.0','2017-10-06','','True','False','180'); +INSERT INTO "npe01__OppPayment__c" VALUES(83,'118.0','2017-04-18','','True','False','181'); +INSERT INTO "npe01__OppPayment__c" VALUES(84,'32.7','2014-03-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(85,'32.7','2014-04-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(86,'32.7','2014-05-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(87,'32.7','2014-06-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(88,'32.7','2014-07-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(89,'32.7','2014-08-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(90,'32.7','2014-09-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(91,'32.7','2014-10-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(92,'32.7','2014-11-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(93,'32.7','2014-12-23','','False','False','182'); +INSERT INTO "npe01__OppPayment__c" VALUES(94,'6258.0','2016-07-24','','True','False','47'); +INSERT INTO "npe01__OppPayment__c" VALUES(95,'557.0','2018-01-07','','True','False','183'); +INSERT INTO "npe01__OppPayment__c" VALUES(96,'642.0','2018-02-08','','True','False','184'); +INSERT INTO "npe01__OppPayment__c" VALUES(97,'782.0','2017-04-19','','True','False','185'); +INSERT INTO "npe01__OppPayment__c" VALUES(98,'409.0','2017-05-21','','True','False','186'); +INSERT INTO "npe01__OppPayment__c" VALUES(99,'501.0','2016-11-30','','True','False','187'); +INSERT INTO "npe01__OppPayment__c" VALUES(100,'998.0','2017-08-18','','True','False','188'); +INSERT INTO "npe01__OppPayment__c" VALUES(101,'338.0','2017-01-01','','True','False','189'); +INSERT INTO "npe01__OppPayment__c" VALUES(102,'737.0','2018-12-15','','True','False','190'); +INSERT INTO "npe01__OppPayment__c" VALUES(103,'410.0','2018-04-12','','True','False','191'); +INSERT INTO "npe01__OppPayment__c" VALUES(104,'202.0','2018-10-16','','True','False','192'); +INSERT INTO "npe01__OppPayment__c" VALUES(105,'2804.0','2016-03-12','','True','False','48'); +INSERT INTO "npe01__OppPayment__c" VALUES(106,'997.0','2019-01-29','','True','False','193'); +INSERT INTO "npe01__OppPayment__c" VALUES(107,'609.0','2016-06-09','','True','False','194'); +INSERT INTO "npe01__OppPayment__c" VALUES(108,'952.0','2018-06-15','','True','False','195'); +INSERT INTO "npe01__OppPayment__c" VALUES(109,'734.0','2017-02-01','','True','False','196'); +INSERT INTO "npe01__OppPayment__c" VALUES(110,'20.0','2021-10-02','','True','False','253'); +INSERT INTO "npe01__OppPayment__c" VALUES(111,'655.0','2021-11-18','','True','False','254'); +INSERT INTO "npe01__OppPayment__c" VALUES(112,'1080.0','2021-11-11','','True','False','255'); +INSERT INTO "npe01__OppPayment__c" VALUES(113,'54.0','2016-08-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(114,'54.0','2016-09-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(115,'54.0','2016-10-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(116,'54.0','2016-11-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(117,'54.0','2016-12-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(118,'54.0','2017-01-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(119,'54.0','2017-02-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(120,'54.0','2017-03-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(121,'54.0','2017-04-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(122,'54.0','2017-05-28','','False','False','197'); +INSERT INTO "npe01__OppPayment__c" VALUES(123,'507.0','2017-04-23','','True','False','198'); +INSERT INTO "npe01__OppPayment__c" VALUES(124,'976.0','2017-11-07','','True','False','199'); +INSERT INTO "npe01__OppPayment__c" VALUES(125,'973.0','2017-11-20','','True','False','200'); +INSERT INTO "npe01__OppPayment__c" VALUES(126,'837.0','2018-03-22','','True','False','201'); +INSERT INTO "npe01__OppPayment__c" VALUES(127,'871.0','2018-09-25','','True','False','202'); +INSERT INTO "npe01__OppPayment__c" VALUES(128,'7164.0','2016-03-09','','True','False','49'); +INSERT INTO "npe01__OppPayment__c" VALUES(129,'504.0','2017-06-15','','True','False','203'); +INSERT INTO "npe01__OppPayment__c" VALUES(130,'234.0','2017-12-20','','True','False','204'); +INSERT INTO "npe01__OppPayment__c" VALUES(131,'762.0','2016-03-31','','True','False','205'); +INSERT INTO "npe01__OppPayment__c" VALUES(132,'357.0','2017-10-24','','True','False','206'); +INSERT INTO "npe01__OppPayment__c" VALUES(133,'992.0','2018-02-28','','True','False','207'); +INSERT INTO "npe01__OppPayment__c" VALUES(134,'666.0','2016-07-19','','True','False','208'); +INSERT INTO "npe01__OppPayment__c" VALUES(135,'128.0','2017-04-22','','True','False','209'); +INSERT INTO "npe01__OppPayment__c" VALUES(136,'111.0','2016-10-31','','True','False','210'); +INSERT INTO "npe01__OppPayment__c" VALUES(137,'719.0','2016-06-10','','True','False','211'); +INSERT INTO "npe01__OppPayment__c" VALUES(138,'22.0','2018-06-07','','True','False','212'); +INSERT INTO "npe01__OppPayment__c" VALUES(139,'6989.0','2018-08-29','','True','False','50'); +INSERT INTO "npe01__OppPayment__c" VALUES(140,'844.0','2017-05-30','','True','False','213'); +INSERT INTO "npe01__OppPayment__c" VALUES(141,'989.0','2017-10-02','','True','False','214'); +INSERT INTO "npe01__OppPayment__c" VALUES(142,'368.0','2017-09-04','','True','False','215'); +INSERT INTO "npe01__OppPayment__c" VALUES(143,'896.0','2016-03-07','','True','False','216'); +INSERT INTO "npe01__OppPayment__c" VALUES(144,'108.0','2018-07-01','','True','False','217'); +INSERT INTO "npe01__OppPayment__c" VALUES(145,'911.0','2017-08-17','','True','False','218'); +INSERT INTO "npe01__OppPayment__c" VALUES(146,'834.0','2017-04-08','','True','False','219'); +INSERT INTO "npe01__OppPayment__c" VALUES(147,'45.0','2016-12-31','','True','False','220'); +INSERT INTO "npe01__OppPayment__c" VALUES(148,'99.0','2017-11-25','','True','False','221'); +INSERT INTO "npe01__OppPayment__c" VALUES(149,'971.0','2016-10-07','','True','False','222'); +INSERT INTO "npe01__OppPayment__c" VALUES(150,'785.8','2014-11-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(151,'785.8','2014-12-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(152,'785.8','2014-03-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(153,'785.8','2014-04-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(154,'785.8','2014-05-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(155,'785.8','2014-06-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(156,'785.8','2014-07-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(157,'785.8','2014-08-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(158,'785.8','2014-09-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(159,'785.8','2014-10-04','','False','False','51'); +INSERT INTO "npe01__OppPayment__c" VALUES(160,'639.0','2018-08-19','','True','False','223'); +INSERT INTO "npe01__OppPayment__c" VALUES(161,'919.0','2018-01-13','','True','False','224'); +INSERT INTO "npe01__OppPayment__c" VALUES(162,'476.0','2016-03-22','','True','False','225'); +INSERT INTO "npe01__OppPayment__c" VALUES(163,'321.0','2017-03-19','','True','False','226'); +INSERT INTO "npe01__OppPayment__c" VALUES(164,'839.0','2018-11-11','','True','False','227'); +INSERT INTO "npe01__OppPayment__c" VALUES(165,'451.0','2018-06-22','','True','False','228'); +INSERT INTO "npe01__OppPayment__c" VALUES(166,'236.0','2019-01-18','','True','False','229'); +INSERT INTO "npe01__OppPayment__c" VALUES(167,'585.0','2018-08-25','','True','False','230'); +INSERT INTO "npe01__OppPayment__c" VALUES(168,'546.0','2017-08-29','','True','False','231'); +INSERT INTO "npe01__OppPayment__c" VALUES(169,'48.5','2014-06-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(170,'48.5','2014-07-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(171,'48.5','2014-08-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(172,'48.5','2014-09-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(173,'48.5','2014-10-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(174,'48.5','2014-11-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(175,'48.5','2014-12-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(176,'48.5','2015-01-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(177,'48.5','2015-03-01','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(178,'48.5','2015-03-29','','False','False','232'); +INSERT INTO "npe01__OppPayment__c" VALUES(179,'635.0','2018-05-04','','True','False','105'); +INSERT INTO "npe01__OppPayment__c" VALUES(180,'181.0','2016-12-04','','True','False','52'); +INSERT INTO "npe01__OppPayment__c" VALUES(181,'329.0','2018-09-18','','True','False','233'); +INSERT INTO "npe01__OppPayment__c" VALUES(182,'272.0','2016-12-10','','True','False','13'); +INSERT INTO "npe01__OppPayment__c" VALUES(183,'261.0','2018-06-06','','True','False','14'); +INSERT INTO "npe01__OppPayment__c" VALUES(184,'921.0','2018-05-04','','True','False','15'); +INSERT INTO "npe01__OppPayment__c" VALUES(185,'75.7','2014-08-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(186,'75.7','2014-09-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(187,'75.7','2014-10-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(188,'75.7','2014-11-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(189,'75.7','2014-12-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(190,'75.7','2015-01-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(191,'75.7','2015-02-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(192,'75.7','2015-03-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(193,'75.7','2015-04-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(194,'75.7','2015-05-28','','False','False','16'); +INSERT INTO "npe01__OppPayment__c" VALUES(195,'375.0','2016-09-04','','True','False','17'); +INSERT INTO "npe01__OppPayment__c" VALUES(196,'1050.0','2021-11-11','','True','False','260'); +INSERT INTO "npe01__OppPayment__c" VALUES(197,'190.0','2021-11-12','','True','False','261'); +INSERT INTO "npe01__OppPayment__c" VALUES(198,'45.0','','','False','True','262'); +INSERT INTO "npe01__OppPayment__c" VALUES(199,'100.0','2021-11-11','','True','False','263'); +INSERT INTO "npe01__OppPayment__c" VALUES(200,'502.0','2017-12-11','','True','False','104'); +INSERT INTO "npe01__OppPayment__c" VALUES(201,'537.0','2016-05-31','','True','False','42'); +INSERT INTO "npe01__OppPayment__c" VALUES(202,'338.0','2016-08-31','','True','False','131'); +INSERT INTO "npe01__OppPayment__c" VALUES(203,'546.0','2017-04-10','','True','False','132'); +INSERT INTO "npe01__OppPayment__c" VALUES(204,'316.0','2018-10-07','','True','False','133'); +INSERT INTO "npe01__OppPayment__c" VALUES(205,'750.0','2017-09-03','','True','False','136'); +INSERT INTO "npe01__OppPayment__c" VALUES(206,'976.0','2016-06-01','','True','False','137'); +INSERT INTO "npe01__OppPayment__c" VALUES(207,'22.0','2016-03-06','','True','False','138'); +INSERT INTO "npe01__OppPayment__c" VALUES(208,'627.0','2018-10-08','','True','False','139'); +INSERT INTO "npe01__OppPayment__c" VALUES(209,'965.0','2018-01-01','','True','False','140'); +INSERT INTO "npe01__OppPayment__c" VALUES(210,'116.0','2018-05-15','','True','False','141'); +INSERT INTO "npe01__OppPayment__c" VALUES(211,'260.0','2018-12-06','','True','False','142'); +INSERT INTO "npe01__OppPayment__c" VALUES(212,'218.0','2018-11-23','','True','False','43'); +INSERT INTO "npe01__OppPayment__c" VALUES(213,'365.0','2018-08-07','','True','False','143'); +INSERT INTO "npe01__OppPayment__c" VALUES(214,'550.0','2018-06-25','','True','False','144'); +INSERT INTO "npe01__OppPayment__c" VALUES(215,'13.1','2016-08-02','','False','False','145'); +INSERT INTO "npe01__OppPayment__c" VALUES(216,'502.0','2021-08-04','','True','False','272'); +INSERT INTO "npe01__OppPayment__c" VALUES(217,'312.0','2021-07-03','','True','False','273'); +INSERT INTO "npe01__OppPayment__c" VALUES(218,'89.0','2021-11-26','','True','False','270'); +INSERT INTO "npe01__OppPayment__c" VALUES(219,'34.0','2021-11-26','','True','False','271'); +INSERT INTO "npe01__OppPayment__c" VALUES(220,'25.0','2021-11-25','','True','False','274'); +INSERT INTO "npe01__OppPayment__c" VALUES(221,'22.0','2021-12-02','','True','False','275'); +INSERT INTO "npe01__OppPayment__c" VALUES(222,'235.0','','','False','True','276'); +INSERT INTO "npe01__OppPayment__c" VALUES(223,'55.0','2021-11-27','','True','False','264'); +INSERT INTO "npe01__OppPayment__c" VALUES(224,'960.0','2021-11-27','','True','False','265'); +INSERT INTO "npe01__OppPayment__c" VALUES(225,'321.0','2021-11-19','','True','False','266'); +INSERT INTO "npe01__OppPayment__c" VALUES(226,'350.0','2021-11-12','','True','False','267'); +INSERT INTO "npe01__OppPayment__c" VALUES(227,'498.0','2021-11-17','','True','False','268'); +INSERT INTO "npe01__OppPayment__c" VALUES(228,'111.0','2021-11-17','','True','False','269'); +INSERT INTO "npe01__OppPayment__c" VALUES(229,'46.0','2021-07-06','','True','False','250'); +INSERT INTO "npe01__OppPayment__c" VALUES(230,'16.0','2021-09-01','','True','False','251'); +INSERT INTO "npe01__OppPayment__c" VALUES(231,'71.0','2021-09-03','','True','False','249'); +INSERT INTO "npe01__OppPayment__c" VALUES(232,'280.0','2021-11-17','','True','False','252'); +INSERT INTO "npe01__OppPayment__c" VALUES(233,'45.0','2021-11-17','','True','False','337'); +INSERT INTO "npe01__OppPayment__c" VALUES(234,'73.0','2021-11-12','','True','False','338'); +INSERT INTO "npe01__OppPayment__c" VALUES(235,'434.0','2021-11-22','','True','False','336'); +INSERT INTO "npe01__OppPayment__c" VALUES(236,'500.0','2021-11-24','','True','False','335'); +INSERT INTO "npe01__OppPayment__c" VALUES(237,'444.0','2021-12-25','','True','False','342'); +INSERT INTO "npe01__OppPayment__c" VALUES(238,'77.0','2021-09-30','','True','False','339'); +INSERT INTO "npe01__OppPayment__c" VALUES(239,'508.0','2021-08-30','','True','False','340'); +INSERT INTO "npe01__OppPayment__c" VALUES(240,'400.0','','','False','True','341'); +INSERT INTO "npe01__OppPayment__c" VALUES(241,'700.0','2021-11-26','','True','False','329'); +INSERT INTO "npe01__OppPayment__c" VALUES(242,'14.0','2021-10-31','','True','False','330'); +INSERT INTO "npe01__OppPayment__c" VALUES(243,'56.0','2021-12-13','','True','False','328'); +INSERT INTO "npe01__OppPayment__c" VALUES(244,'99.0','2021-11-03','','True','False','331'); +INSERT INTO "npe01__OppPayment__c" VALUES(245,'667.0','2021-10-19','','True','False','334'); +INSERT INTO "npe01__OppPayment__c" VALUES(246,'89.0','2021-11-07','','True','False','332'); +INSERT INTO "npe01__OppPayment__c" VALUES(247,'445.0','2021-09-14','','True','False','333'); +INSERT INTO "npe01__OppPayment__c" VALUES(248,'123.0','2021-11-18','','True','False','324'); +INSERT INTO "npe01__OppPayment__c" VALUES(249,'88.0','2021-12-11','','True','False','323'); +INSERT INTO "npe01__OppPayment__c" VALUES(250,'156.0','2021-09-28','','True','False','322'); +INSERT INTO "npe01__OppPayment__c" VALUES(251,'2345.0','2021-12-11','','True','False','321'); +INSERT INTO "npe01__OppPayment__c" VALUES(252,'120.0','2021-11-25','','True','False','325'); +INSERT INTO "npe01__OppPayment__c" VALUES(253,'400.0','','2021-11-26','False','False','327'); +INSERT INTO "npe01__OppPayment__c" VALUES(254,'150.0','','2021-11-11','False','False','326'); +INSERT INTO "npe01__OppPayment__c" VALUES(255,'785.0','2021-11-30','','True','False','316'); +INSERT INTO "npe01__OppPayment__c" VALUES(256,'70.0','2021-12-31','','True','False','313'); +INSERT INTO "npe01__OppPayment__c" VALUES(257,'534.0','2021-11-20','','True','False','314'); +INSERT INTO "npe01__OppPayment__c" VALUES(258,'568.0','','2021-11-02','False','False','315'); +INSERT INTO "npe01__OppPayment__c" VALUES(259,'75.0','','2022-04-15','False','False','304'); +INSERT INTO "npe01__OppPayment__c" VALUES(260,'75.0','','2022-05-15','False','False','305'); +INSERT INTO "npe01__OppPayment__c" VALUES(261,'75.0','','2022-06-15','False','False','306'); +INSERT INTO "npe01__OppPayment__c" VALUES(262,'75.0','','2022-07-15','False','False','307'); +INSERT INTO "npe01__OppPayment__c" VALUES(263,'75.0','','2022-08-15','False','False','308'); +INSERT INTO "npe01__OppPayment__c" VALUES(264,'75.0','','2022-09-15','False','False','309'); +INSERT INTO "npe01__OppPayment__c" VALUES(265,'75.0','','2022-10-15','False','False','310'); +INSERT INTO "npe01__OppPayment__c" VALUES(266,'675.0','2021-11-03','','True','False','311'); +INSERT INTO "npe01__OppPayment__c" VALUES(267,'100.0','','','False','True','312'); +INSERT INTO "npe01__OppPayment__c" VALUES(268,'75.0','','2020-01-15','False','False','277'); +INSERT INTO "npe01__OppPayment__c" VALUES(269,'75.0','','2020-02-15','False','False','278'); +INSERT INTO "npe01__OppPayment__c" VALUES(270,'75.0','','2020-03-15','False','False','279'); +INSERT INTO "npe01__OppPayment__c" VALUES(271,'75.0','','2020-04-15','False','False','280'); +INSERT INTO "npe01__OppPayment__c" VALUES(272,'75.0','','2020-05-15','False','False','281'); +INSERT INTO "npe01__OppPayment__c" VALUES(273,'75.0','','2020-06-15','False','False','282'); +INSERT INTO "npe01__OppPayment__c" VALUES(274,'75.0','','2020-07-15','False','False','283'); +INSERT INTO "npe01__OppPayment__c" VALUES(275,'75.0','','2020-08-15','False','False','284'); +INSERT INTO "npe01__OppPayment__c" VALUES(276,'75.0','','2020-09-15','False','False','285'); +INSERT INTO "npe01__OppPayment__c" VALUES(277,'75.0','','2020-10-15','False','False','286'); +INSERT INTO "npe01__OppPayment__c" VALUES(278,'75.0','','2020-11-15','False','False','287'); +INSERT INTO "npe01__OppPayment__c" VALUES(279,'75.0','','2020-12-15','False','False','288'); +INSERT INTO "npe01__OppPayment__c" VALUES(280,'75.0','','2021-01-15','False','False','289'); +INSERT INTO "npe01__OppPayment__c" VALUES(281,'75.0','','2021-02-15','False','False','290'); +INSERT INTO "npe01__OppPayment__c" VALUES(282,'75.0','','2021-03-15','False','False','291'); +INSERT INTO "npe01__OppPayment__c" VALUES(283,'75.0','','2021-04-15','False','False','292'); +INSERT INTO "npe01__OppPayment__c" VALUES(284,'75.0','','2021-05-15','False','False','293'); +INSERT INTO "npe01__OppPayment__c" VALUES(285,'75.0','','2021-06-15','False','False','294'); +INSERT INTO "npe01__OppPayment__c" VALUES(286,'75.0','','2021-07-15','False','False','295'); +INSERT INTO "npe01__OppPayment__c" VALUES(287,'75.0','','2021-08-15','False','False','296'); +INSERT INTO "npe01__OppPayment__c" VALUES(288,'75.0','','2021-09-15','False','False','297'); +INSERT INTO "npe01__OppPayment__c" VALUES(289,'75.0','','2021-10-15','False','False','298'); +INSERT INTO "npe01__OppPayment__c" VALUES(290,'75.0','','2021-11-15','False','False','299'); +INSERT INTO "npe01__OppPayment__c" VALUES(291,'75.0','','2021-12-15','False','False','300'); +INSERT INTO "npe01__OppPayment__c" VALUES(292,'75.0','','2022-01-15','False','False','301'); +INSERT INTO "npe01__OppPayment__c" VALUES(293,'75.0','','2022-02-15','False','False','302'); +INSERT INTO "npe01__OppPayment__c" VALUES(294,'75.0','','2022-03-15','False','False','303'); +INSERT INTO "npe01__OppPayment__c" VALUES(295,'165.0','2021-11-01','','True','False','24'); +INSERT INTO "npe01__OppPayment__c" VALUES(296,'790.0','2021-11-11','','True','False','26'); +INSERT INTO "npe01__OppPayment__c" VALUES(297,'75.0','2019-07-15','','True','False','1'); +INSERT INTO "npe01__OppPayment__c" VALUES(298,'492.0','2018-09-09','','True','False','4'); +INSERT INTO "npe01__OppPayment__c" VALUES(299,'892.0','2018-10-25','','True','False','5'); +INSERT INTO "npe01__OppPayment__c" VALUES(300,'798.0','2017-05-13','','True','False','6'); +INSERT INTO "npe01__OppPayment__c" VALUES(301,'75.0','2019-12-15','','True','False','2'); +INSERT INTO "npe01__OppPayment__c" VALUES(302,'820.0','2018-01-26','','True','False','3'); +INSERT INTO "npe01__OppPayment__c" VALUES(303,'265.0','2016-04-17','','True','False','7'); +INSERT INTO "npe01__OppPayment__c" VALUES(304,'934.0','2018-04-23','','True','False','8'); +INSERT INTO "npe01__OppPayment__c" VALUES(305,'599.0','2016-11-23','','True','False','9'); +INSERT INTO "npe01__OppPayment__c" VALUES(306,'538.0','2018-10-03','','True','False','10'); +INSERT INTO "npe01__OppPayment__c" VALUES(307,'993.0','2016-04-21','','True','False','11'); +INSERT INTO "npe01__OppPayment__c" VALUES(308,'4.0','2017-04-22','','True','False','12'); +INSERT INTO "npe01__OppPayment__c" VALUES(309,'272.0','2016-12-10','','True','False','13'); +INSERT INTO "npe01__OppPayment__c" VALUES(310,'261.0','2018-06-06','','True','False','14'); +INSERT INTO "npe01__OppPayment__c" VALUES(311,'921.0','2018-05-04','','True','False','15'); +INSERT INTO "npe01__OppPayment__c" VALUES(312,'190.0','2021-09-11','','True','False','25'); +INSERT INTO "npe01__OppPayment__c" VALUES(313,'44.0','2021-12-23','','True','False','23'); +INSERT INTO "npe01__OppPayment__c" VALUES(314,'375.0','2016-09-04','','True','False','17'); +INSERT INTO "npe01__OppPayment__c" VALUES(315,'698.0','2016-03-23','','True','False','18'); +INSERT INTO "npe01__OppPayment__c" VALUES(316,'892.0','2017-04-20','','True','False','19'); +INSERT INTO "npe01__OppPayment__c" VALUES(317,'830.0','2017-01-05','','True','False','21'); +INSERT INTO "npe01__OppPayment__c" VALUES(318,'656.0','2018-01-15','','True','False','22'); +INSERT INTO "npe01__OppPayment__c" VALUES(319,'250.0','2021-11-10','','True','False','29'); +INSERT INTO "npe01__OppPayment__c" VALUES(320,'330.0','2021-05-22','','True','False','28'); +INSERT INTO "npe01__OppPayment__c" VALUES(321,'543.0','2021-12-04','','True','False','30'); +INSERT INTO "npe01__OppPayment__c" VALUES(322,'130.0','2021-11-20','','True','False','32'); +INSERT INTO "npe01__OppPayment__c" VALUES(323,'40.0','','2021-11-19','False','False','33'); +INSERT INTO "npe01__OppPayment__c" VALUES(324,'436.0','','2021-11-05','False','False','34'); +INSERT INTO "npe01__OppPayment__c" VALUES(325,'33.0','2021-11-30','','True','False','27'); +INSERT INTO "npe01__OppPayment__c" VALUES(326,'666.0','2021-11-19','','True','False','31'); +INSERT INTO "npe01__OppPayment__c" VALUES(327,'550.0','2018-06-25','','True','False','144'); +INSERT INTO "npe01__OppPayment__c" VALUES(328,'631.0','2016-07-21','','True','False','146'); +INSERT INTO "npe01__OppPayment__c" VALUES(329,'970.0','2018-09-15','','True','False','147'); +INSERT INTO "npe01__OppPayment__c" VALUES(330,'59.0','2018-06-07','','True','False','148'); +INSERT INTO "npe01__OppPayment__c" VALUES(331,'253.0','2018-11-20','','True','False','149'); +INSERT INTO "npe01__OppPayment__c" VALUES(332,'421.0','2016-04-23','','True','False','150'); +INSERT INTO "npe01__OppPayment__c" VALUES(333,'743.0','2018-09-03','','True','False','151'); +INSERT INTO "npe01__OppPayment__c" VALUES(334,'37.0','2017-11-19','','True','False','152'); +INSERT INTO "npe01__OppPayment__c" VALUES(335,'813.0','2017-04-10','','True','False','158'); +INSERT INTO "npe01__OppPayment__c" VALUES(336,'557.0','2017-12-02','','True','False','159'); +INSERT INTO "npe01__OppPayment__c" VALUES(337,'926.0','2018-11-10','','True','False','160'); +INSERT INTO "npe01__OppPayment__c" VALUES(338,'13.0','2018-02-28','','True','False','161'); +INSERT INTO "npe01__OppPayment__c" VALUES(339,'609.0','2016-12-18','','True','False','162'); +INSERT INTO "npe01__OppPayment__c" VALUES(340,'962.0','2018-01-28','','True','False','163'); +INSERT INTO "npe01__OppPayment__c" VALUES(341,'204.0','2017-12-25','','True','False','164'); +INSERT INTO "npe01__OppPayment__c" VALUES(342,'153.0','2017-06-01','','True','False','165'); +INSERT INTO "npe01__OppPayment__c" VALUES(343,'89.0','2018-01-23','','True','False','166'); +INSERT INTO "npe01__OppPayment__c" VALUES(344,'331.0','2018-01-26','','True','False','167'); +INSERT INTO "npe01__OppPayment__c" VALUES(345,'842.0','2017-07-29','','True','False','168'); +INSERT INTO "npe01__OppPayment__c" VALUES(346,'736.0','2016-09-14','','True','False','169'); +INSERT INTO "npe01__OppPayment__c" VALUES(347,'978.0','2016-03-20','','True','False','170'); +INSERT INTO "npe01__OppPayment__c" VALUES(348,'927.0','2017-04-24','','True','False','171'); +INSERT INTO "npe01__OppPayment__c" VALUES(349,'321.0','2016-05-17','','True','False','172'); +INSERT INTO "npe01__OppPayment__c" VALUES(350,'282.0','2016-02-04','','True','False','173'); +INSERT INTO "npe01__OppPayment__c" VALUES(351,'750.0','2016-02-21','','True','False','174'); +INSERT INTO "npe01__OppPayment__c" VALUES(352,'996.0','2016-06-16','','True','False','175'); +INSERT INTO "npe01__OppPayment__c" VALUES(353,'146.0','2017-11-27','','True','False','153'); +INSERT INTO "npe01__OppPayment__c" VALUES(354,'657.0','2016-09-08','','True','False','154'); +INSERT INTO "npe01__OppPayment__c" VALUES(355,'36.0','2016-09-02','','True','False','155'); +INSERT INTO "npe01__OppPayment__c" VALUES(356,'536.0','2019-01-14','','True','False','176'); +INSERT INTO "npe01__OppPayment__c" VALUES(357,'756.0','2018-11-10','','True','False','177'); +INSERT INTO "npe01__OppPayment__c" VALUES(358,'119.0','2018-04-03','','True','False','178'); +INSERT INTO "npe01__OppPayment__c" VALUES(359,'309.0','2016-11-01','','True','False','179'); +INSERT INTO "npe01__OppPayment__c" VALUES(360,'230.0','2017-10-06','','True','False','180'); +INSERT INTO "npe01__OppPayment__c" VALUES(361,'118.0','2017-04-18','','True','False','181'); +INSERT INTO "npe01__OppPayment__c" VALUES(362,'557.0','2018-01-07','','True','False','183'); +INSERT INTO "npe01__OppPayment__c" VALUES(363,'642.0','2018-02-08','','True','False','184'); +INSERT INTO "npe01__OppPayment__c" VALUES(364,'289.0','','2021-11-10','False','False','243'); +INSERT INTO "npe01__OppPayment__c" VALUES(365,'55.0','','','False','True','244'); +INSERT INTO "npe01__OppPayment__c" VALUES(366,'80.0','','2021-11-09','False','False','245'); +INSERT INTO "npe01__OppPayment__c" VALUES(367,'23.0','2021-11-10','','True','False','247'); +INSERT INTO "npe01__OppPayment__c" VALUES(368,'75.0','2019-03-15','','True','False','236'); +INSERT INTO "npe01__OppPayment__c" VALUES(369,'75.0','2019-08-15','','True','False','60'); +INSERT INTO "npe01__OppPayment__c" VALUES(370,'75.0','2019-09-15','','True','False','61'); +INSERT INTO "npe01__OppPayment__c" VALUES(371,'75.0','2019-10-15','','True','False','62'); +INSERT INTO "npe01__OppPayment__c" VALUES(372,'75.0','2019-11-15','','True','False','63'); +INSERT INTO "npe01__OppPayment__c" VALUES(373,'75.0','2019-05-15','','True','False','64'); +INSERT INTO "npe01__OppPayment__c" VALUES(374,'75.0','2019-06-15','','True','False','65'); +INSERT INTO "npe01__OppPayment__c" VALUES(375,'75.0','2019-04-15','','True','False','59'); +INSERT INTO "npe01__OppPayment__c" VALUES(376,'575.0','2021-11-10','','True','False','66'); +INSERT INTO "npe01__OppPayment__c" VALUES(377,'501.0','2016-11-30','','True','False','187'); +INSERT INTO "npe01__OppPayment__c" VALUES(378,'998.0','2017-08-18','','True','False','188'); +INSERT INTO "npe01__OppPayment__c" VALUES(379,'338.0','2017-01-01','','True','False','189'); +INSERT INTO "npe01__OppPayment__c" VALUES(380,'737.0','2018-12-15','','True','False','190'); +INSERT INTO "npe01__OppPayment__c" VALUES(381,'410.0','2018-04-12','','True','False','191'); +INSERT INTO "npe01__OppPayment__c" VALUES(382,'782.0','2017-04-19','','True','False','185'); +INSERT INTO "npe01__OppPayment__c" VALUES(383,'409.0','2017-05-21','','True','False','186'); +INSERT INTO "npe01__OppPayment__c" VALUES(384,'202.0','2018-10-16','','True','False','192'); +INSERT INTO "npe01__OppPayment__c" VALUES(385,'997.0','2019-01-29','','True','False','193'); +INSERT INTO "npe01__OppPayment__c" VALUES(386,'609.0','2016-06-09','','True','False','194'); +INSERT INTO "npe01__OppPayment__c" VALUES(387,'483.0','2016-06-11','','True','False','40'); +INSERT INTO "npe01__OppPayment__c" VALUES(388,'151.0','2018-02-20','','True','False','41'); +INSERT INTO "npe01__OppPayment__c" VALUES(389,'537.0','2016-05-31','','True','False','42'); +INSERT INTO "npe01__OppPayment__c" VALUES(390,'177.0','2018-11-23','','True','False','35'); +INSERT INTO "npe01__OppPayment__c" VALUES(391,'218.0','2018-11-23','','True','False','43'); +INSERT INTO "npe01__OppPayment__c" VALUES(392,'208.0','2018-01-20','','True','False','36'); +INSERT INTO "npe01__OppPayment__c" VALUES(393,'553.0','2017-10-15','','True','False','37'); +INSERT INTO "npe01__OppPayment__c" VALUES(394,'565.0','2018-09-06','','True','False','38'); +INSERT INTO "npe01__OppPayment__c" VALUES(395,'310.0','2018-02-02','','True','False','39'); +INSERT INTO "npe01__OppPayment__c" VALUES(396,'376.0','2018-10-13','','True','False','44'); +INSERT INTO "npe01__OppPayment__c" VALUES(397,'507.0','2017-04-23','','True','False','198'); +INSERT INTO "npe01__OppPayment__c" VALUES(398,'976.0','2017-11-07','','True','False','199'); +INSERT INTO "npe01__OppPayment__c" VALUES(399,'973.0','2017-11-20','','True','False','200'); +INSERT INTO "npe01__OppPayment__c" VALUES(400,'837.0','2018-03-22','','True','False','201'); +INSERT INTO "npe01__OppPayment__c" VALUES(401,'871.0','2018-09-25','','True','False','202'); +INSERT INTO "npe01__OppPayment__c" VALUES(402,'504.0','2017-06-15','','True','False','203'); +INSERT INTO "npe01__OppPayment__c" VALUES(403,'234.0','2017-12-20','','True','False','204'); +INSERT INTO "npe01__OppPayment__c" VALUES(404,'952.0','2018-06-15','','True','False','195'); +INSERT INTO "npe01__OppPayment__c" VALUES(405,'734.0','2017-02-01','','True','False','196'); +INSERT INTO "npe01__OppPayment__c" VALUES(406,'762.0','2016-03-31','','True','False','205'); +INSERT INTO "npe01__OppPayment__c" VALUES(407,'357.0','2017-10-24','','True','False','206'); +INSERT INTO "npe01__OppPayment__c" VALUES(408,'992.0','2018-02-28','','True','False','207'); +INSERT INTO "npe01__OppPayment__c" VALUES(409,'666.0','2016-07-19','','True','False','208'); +INSERT INTO "npe01__OppPayment__c" VALUES(410,'128.0','2017-04-22','','True','False','209'); +INSERT INTO "npe01__OppPayment__c" VALUES(411,'111.0','2016-10-31','','True','False','210'); +INSERT INTO "npe01__OppPayment__c" VALUES(412,'719.0','2016-06-10','','True','False','211'); +INSERT INTO "npe01__OppPayment__c" VALUES(413,'22.0','2018-06-07','','True','False','212'); +INSERT INTO "npe01__OppPayment__c" VALUES(414,'844.0','2017-05-30','','True','False','213'); +INSERT INTO "npe01__OppPayment__c" VALUES(415,'989.0','2017-10-02','','True','False','214'); +INSERT INTO "npe01__OppPayment__c" VALUES(416,'368.0','2017-09-04','','True','False','215'); +INSERT INTO "npe01__OppPayment__c" VALUES(417,'896.0','2016-03-07','','True','False','216'); +INSERT INTO "npe01__OppPayment__c" VALUES(418,'108.0','2018-07-01','','True','False','217'); +INSERT INTO "npe01__OppPayment__c" VALUES(419,'911.0','2017-08-17','','True','False','218'); +INSERT INTO "npe01__OppPayment__c" VALUES(420,'834.0','2017-04-08','','True','False','219'); +INSERT INTO "npe01__OppPayment__c" VALUES(421,'45.0','2016-12-31','','True','False','220'); +INSERT INTO "npe01__OppPayment__c" VALUES(422,'99.0','2017-11-25','','True','False','221'); +INSERT INTO "npe01__OppPayment__c" VALUES(423,'971.0','2016-10-07','','True','False','222'); +INSERT INTO "npe01__OppPayment__c" VALUES(424,'639.0','2018-08-19','','True','False','223'); +INSERT INTO "npe01__OppPayment__c" VALUES(425,'919.0','2018-01-13','','True','False','224'); +INSERT INTO "npe01__OppPayment__c" VALUES(426,'476.0','2016-03-22','','True','False','225'); +INSERT INTO "npe01__OppPayment__c" VALUES(427,'321.0','2017-03-19','','True','False','226'); +INSERT INTO "npe01__OppPayment__c" VALUES(428,'839.0','2018-11-11','','True','False','227'); +INSERT INTO "npe01__OppPayment__c" VALUES(429,'451.0','2018-06-22','','True','False','228'); +INSERT INTO "npe01__OppPayment__c" VALUES(430,'236.0','2019-01-18','','True','False','229'); +INSERT INTO "npe01__OppPayment__c" VALUES(431,'585.0','2018-08-25','','True','False','230'); +INSERT INTO "npe01__OppPayment__c" VALUES(432,'546.0','2017-08-29','','True','False','231'); +INSERT INTO "npe01__OppPayment__c" VALUES(433,'329.0','2018-09-18','','True','False','233'); +INSERT INTO "npe01__OppPayment__c" VALUES(434,'75.0','2019-01-15','','True','False','234'); +INSERT INTO "npe01__OppPayment__c" VALUES(435,'75.0','2019-02-15','','True','False','235'); +INSERT INTO "npe01__OppPayment__c" VALUES(436,'100.0','','','False','True','67'); +INSERT INTO "npe01__OppPayment__c" VALUES(437,'233.0','2021-10-12','','True','False','238'); +INSERT INTO "npe01__OppPayment__c" VALUES(438,'290.0','2021-09-08','','True','False','239'); +INSERT INTO "npe01__OppPayment__c" VALUES(439,'7164.0','2016-03-09','','True','False','49'); +INSERT INTO "npe01__OppPayment__c" VALUES(440,'6989.0','2018-08-29','','True','False','50'); +INSERT INTO "npe01__OppPayment__c" VALUES(441,'181.0','2016-12-04','','True','False','52'); +INSERT INTO "npe01__OppPayment__c" VALUES(442,'3422.0','2017-01-31','','True','False','53'); +INSERT INTO "npe01__OppPayment__c" VALUES(443,'2434.0','2016-11-02','','True','False','54'); +INSERT INTO "npe01__OppPayment__c" VALUES(444,'457.0','2017-08-27','','True','False','55'); +INSERT INTO "npe01__OppPayment__c" VALUES(445,'13.0','2017-01-19','','True','False','56'); +INSERT INTO "npe01__OppPayment__c" VALUES(446,'234.0','2017-11-04','','True','False','57'); +INSERT INTO "npe01__OppPayment__c" VALUES(447,'84.0','2016-05-05','','True','False','58'); +INSERT INTO "npe01__OppPayment__c" VALUES(448,'952.0','2018-11-16','','True','False','68'); +INSERT INTO "npe01__OppPayment__c" VALUES(449,'3.0','2017-04-20','','True','False','69'); +INSERT INTO "npe01__OppPayment__c" VALUES(450,'521.0','2016-09-22','','True','False','70'); +INSERT INTO "npe01__OppPayment__c" VALUES(451,'5297.0','2017-11-27','','True','False','46'); +INSERT INTO "npe01__OppPayment__c" VALUES(452,'6258.0','2016-07-24','','True','False','47'); +INSERT INTO "npe01__OppPayment__c" VALUES(453,'2804.0','2016-03-12','','True','False','48'); +INSERT INTO "npe01__OppPayment__c" VALUES(454,'567.0','2016-02-26','','True','False','71'); +INSERT INTO "npe01__OppPayment__c" VALUES(455,'744.0','2018-04-26','','True','False','72'); +INSERT INTO "npe01__OppPayment__c" VALUES(456,'619.0','2016-09-11','','True','False','73'); +INSERT INTO "npe01__OppPayment__c" VALUES(457,'3738.0','2017-08-02','','True','False','74'); +INSERT INTO "npe01__OppPayment__c" VALUES(458,'6351.0','2017-12-29','','True','False','75'); +INSERT INTO "npe01__OppPayment__c" VALUES(459,'2233.0','2017-05-18','','True','False','76'); +INSERT INTO "npe01__OppPayment__c" VALUES(460,'3361.0','2016-09-08','','True','False','77'); +INSERT INTO "npe01__OppPayment__c" VALUES(461,'3124.0','2018-05-15','','True','False','78'); +INSERT INTO "npe01__OppPayment__c" VALUES(462,'1824.0','2017-07-30','','True','False','80'); +INSERT INTO "npe01__OppPayment__c" VALUES(463,'4670.0','2016-09-17','','True','False','81'); +INSERT INTO "npe01__OppPayment__c" VALUES(464,'5400.0','2017-03-22','','True','False','82'); +INSERT INTO "npe01__OppPayment__c" VALUES(465,'9404.0','2016-02-15','','True','False','83'); +INSERT INTO "npe01__OppPayment__c" VALUES(466,'504.0','2017-03-01','','True','False','84'); +INSERT INTO "npe01__OppPayment__c" VALUES(467,'109.0','2019-02-07','','True','False','85'); +INSERT INTO "npe01__OppPayment__c" VALUES(468,'479.0','2017-08-07','','True','False','86'); +INSERT INTO "npe01__OppPayment__c" VALUES(469,'270.0','2016-10-28','','True','False','87'); +INSERT INTO "npe01__OppPayment__c" VALUES(470,'31.0','2019-01-20','','True','False','88'); +INSERT INTO "npe01__OppPayment__c" VALUES(471,'157.0','2018-04-24','','True','False','89'); +INSERT INTO "npe01__OppPayment__c" VALUES(472,'31.0','2017-09-28','','True','False','90'); +INSERT INTO "npe01__OppPayment__c" VALUES(473,'836.0','2019-01-12','','True','False','91'); +INSERT INTO "npe01__OppPayment__c" VALUES(474,'984.0','2016-03-20','','True','False','92'); +INSERT INTO "npe01__OppPayment__c" VALUES(475,'862.0','2017-08-04','','True','False','93'); +INSERT INTO "npe01__OppPayment__c" VALUES(476,'433.0','2018-01-22','','True','False','94'); +INSERT INTO "npe01__OppPayment__c" VALUES(477,'794.0','2018-05-02','','True','False','95'); +INSERT INTO "npe01__OppPayment__c" VALUES(478,'6.0','2018-04-27','','True','False','96'); +INSERT INTO "npe01__OppPayment__c" VALUES(479,'87.0','2017-07-30','','True','False','97'); +INSERT INTO "npe01__OppPayment__c" VALUES(480,'641.0','2018-10-05','','True','False','98'); +INSERT INTO "npe01__OppPayment__c" VALUES(481,'29.0','2017-04-01','','True','False','99'); +INSERT INTO "npe01__OppPayment__c" VALUES(482,'915.0','2016-07-21','','True','False','100'); +INSERT INTO "npe01__OppPayment__c" VALUES(483,'907.0','2018-12-19','','True','False','101'); +INSERT INTO "npe01__OppPayment__c" VALUES(484,'905.0','2017-09-13','','True','False','102'); +INSERT INTO "npe01__OppPayment__c" VALUES(485,'957.0','2016-11-01','','True','False','103'); +INSERT INTO "npe01__OppPayment__c" VALUES(486,'2909.0','2018-05-16','','True','False','79'); +INSERT INTO "npe01__OppPayment__c" VALUES(487,'502.0','2017-12-11','','True','False','104'); +INSERT INTO "npe01__OppPayment__c" VALUES(488,'635.0','2018-05-04','','True','False','105'); +INSERT INTO "npe01__OppPayment__c" VALUES(489,'547.0','2017-09-16','','True','False','106'); +INSERT INTO "npe01__OppPayment__c" VALUES(490,'235.0','2017-07-08','','True','False','107'); +INSERT INTO "npe01__OppPayment__c" VALUES(491,'514.0','2017-08-28','','True','False','108'); +INSERT INTO "npe01__OppPayment__c" VALUES(492,'70.0','2018-07-06','','True','False','109'); +INSERT INTO "npe01__OppPayment__c" VALUES(493,'401.0','2016-04-13','','True','False','110'); +INSERT INTO "npe01__OppPayment__c" VALUES(494,'466.0','2016-12-10','','True','False','111'); +INSERT INTO "npe01__OppPayment__c" VALUES(495,'344.0','2018-01-07','','True','False','112'); +INSERT INTO "npe01__OppPayment__c" VALUES(496,'110.0','2017-05-08','','True','False','113'); +INSERT INTO "npe01__OppPayment__c" VALUES(497,'782.0','2017-03-21','','True','False','114'); +INSERT INTO "npe01__OppPayment__c" VALUES(498,'449.0','2016-10-17','','True','False','121'); +INSERT INTO "npe01__OppPayment__c" VALUES(499,'643.0','2017-09-26','','True','False','122'); +INSERT INTO "npe01__OppPayment__c" VALUES(500,'127.0','2018-12-13','','True','False','123'); +INSERT INTO "npe01__OppPayment__c" VALUES(501,'377.0','2018-06-16','','True','False','124'); +INSERT INTO "npe01__OppPayment__c" VALUES(502,'654.0','2016-03-02','','True','False','125'); +INSERT INTO "npe01__OppPayment__c" VALUES(503,'926.0','2017-01-18','','True','False','126'); +INSERT INTO "npe01__OppPayment__c" VALUES(504,'838.0','2017-07-29','','True','False','127'); +INSERT INTO "npe01__OppPayment__c" VALUES(505,'46.0','2018-07-03','','True','False','128'); +INSERT INTO "npe01__OppPayment__c" VALUES(506,'440.0','2016-12-12','','True','False','129'); +INSERT INTO "npe01__OppPayment__c" VALUES(507,'353.0','2018-07-09','','True','False','130'); +INSERT INTO "npe01__OppPayment__c" VALUES(508,'338.0','2016-08-31','','True','False','131'); +INSERT INTO "npe01__OppPayment__c" VALUES(509,'546.0','2017-04-10','','True','False','132'); +INSERT INTO "npe01__OppPayment__c" VALUES(510,'316.0','2018-10-07','','True','False','133'); +INSERT INTO "npe01__OppPayment__c" VALUES(511,'409.0','2017-03-29','','True','False','134'); +INSERT INTO "npe01__OppPayment__c" VALUES(512,'858.0','2018-03-05','','True','False','135'); +INSERT INTO "npe01__OppPayment__c" VALUES(513,'750.0','2017-09-03','','True','False','136'); +INSERT INTO "npe01__OppPayment__c" VALUES(514,'976.0','2016-06-01','','True','False','137'); +INSERT INTO "npe01__OppPayment__c" VALUES(515,'22.0','2016-03-06','','True','False','138'); +INSERT INTO "npe01__OppPayment__c" VALUES(516,'627.0','2018-10-08','','True','False','139'); +INSERT INTO "npe01__OppPayment__c" VALUES(517,'965.0','2018-01-01','','True','False','140'); +INSERT INTO "npe01__OppPayment__c" VALUES(518,'116.0','2018-05-15','','True','False','141'); +INSERT INTO "npe01__OppPayment__c" VALUES(519,'260.0','2018-12-06','','True','False','142'); +INSERT INTO "npe01__OppPayment__c" VALUES(520,'365.0','2018-08-07','','True','False','143'); +INSERT INTO "npe01__OppPayment__c" VALUES(521,'867.0','2021-09-16','','True','False','240'); +INSERT INTO "npe01__OppPayment__c" VALUES(522,'974.0','2017-07-18','','True','False','119'); +INSERT INTO "npe01__OppPayment__c" VALUES(523,'314.0','2018-12-27','','True','False','115'); +INSERT INTO "npe01__OppPayment__c" VALUES(524,'436.0','2019-01-04','','True','False','116'); +INSERT INTO "npe01__OppPayment__c" VALUES(525,'157.0','2016-12-28','','True','False','117'); +INSERT INTO "npe01__OppPayment__c" VALUES(526,'685.0','2017-08-07','','True','False','118'); +INSERT INTO "npe01__OppPayment__c" VALUES(527,'751.0','2019-01-30','','True','False','120'); +INSERT INTO "npe01__OppPayment__c" VALUES(528,'111.0','2021-12-04','','True','False','242'); +INSERT INTO "npe01__OppPayment__c" VALUES(529,'77.0','2021-12-01','','True','False','248'); +INSERT INTO "npe01__OppPayment__c" VALUES(530,'70.0','2018-07-06','','True','False','109'); +INSERT INTO "npe01__OppPayment__c" VALUES(531,'905.0','2017-09-13','','True','False','102'); +INSERT INTO "npe01__OppPayment__c" VALUES(532,'957.0','2016-11-01','','True','False','103'); +INSERT INTO "npe01__OppPayment__c" VALUES(533,'547.0','2017-09-16','','True','False','106'); +INSERT INTO "npe01__OppPayment__c" VALUES(534,'235.0','2017-07-08','','True','False','107'); +INSERT INTO "npe01__OppPayment__c" VALUES(535,'514.0','2017-08-28','','True','False','108'); +INSERT INTO "npe01__OppPayment__c" VALUES(536,'401.0','2016-04-13','','True','False','110'); +INSERT INTO "npe01__OppPayment__c" VALUES(537,'1000.0','2021-11-12','','True','False','246'); +INSERT INTO "npe01__OppPayment__c" VALUES(538,'436.0','2019-01-04','','True','False','116'); +INSERT INTO "npe01__OppPayment__c" VALUES(539,'157.0','2016-12-28','','True','False','117'); +INSERT INTO "npe01__OppPayment__c" VALUES(540,'685.0','2017-08-07','','True','False','118'); +INSERT INTO "npe01__OppPayment__c" VALUES(541,'974.0','2017-07-18','','True','False','119'); +INSERT INTO "npe01__OppPayment__c" VALUES(542,'751.0','2019-01-30','','True','False','120'); +INSERT INTO "npe01__OppPayment__c" VALUES(543,'151.0','2018-02-20','','True','False','41'); +INSERT INTO "npe01__OppPayment__c" VALUES(544,'449.0','2016-10-17','','True','False','121'); +INSERT INTO "npe01__OppPayment__c" VALUES(545,'643.0','2017-09-26','','True','False','122'); +INSERT INTO "npe01__OppPayment__c" VALUES(546,'127.0','2018-12-13','','True','False','123'); +INSERT INTO "npe01__OppPayment__c" VALUES(547,'377.0','2018-06-16','','True','False','124'); +INSERT INTO "npe01__OppPayment__c" VALUES(548,'654.0','2016-03-02','','True','False','125'); +INSERT INTO "npe01__OppPayment__c" VALUES(549,'926.0','2017-01-18','','True','False','126'); +INSERT INTO "npe01__OppPayment__c" VALUES(550,'838.0','2017-07-29','','True','False','127'); +INSERT INTO "npe01__OppPayment__c" VALUES(551,'46.0','2018-07-03','','True','False','128'); +INSERT INTO "npe01__OppPayment__c" VALUES(552,'440.0','2016-12-12','','True','False','129'); +INSERT INTO "npe01__OppPayment__c" VALUES(553,'353.0','2018-07-09','','True','False','130'); +INSERT INTO "npe01__OppPayment__c" VALUES(554,'698.0','2016-03-23','','True','False','18'); +INSERT INTO "npe01__OppPayment__c" VALUES(555,'892.0','2017-04-20','','True','False','19'); +INSERT INTO "npe01__OppPayment__c" VALUES(556,'8.5','2015-08-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(557,'8.5','2015-09-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(558,'8.5','2015-10-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(559,'8.5','2015-11-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(560,'8.5','2015-12-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(561,'8.5','2016-01-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(562,'8.5','2016-03-01','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(563,'8.5','2016-03-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(564,'8.5','2016-04-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(565,'8.5','2016-05-30','','False','False','20'); +INSERT INTO "npe01__OppPayment__c" VALUES(566,'830.0','2017-01-05','','True','False','21'); +INSERT INTO "npe01__OppPayment__c" VALUES(567,'3422.0','2017-01-31','','True','False','53'); +INSERT INTO "npe01__OppPayment__c" VALUES(568,'656.0','2018-01-15','','True','False','22'); +INSERT INTO "npe01__OppPayment__c" VALUES(569,'75.0','','2020-01-15','False','False','234'); +INSERT INTO "npe01__OppPayment__c" VALUES(570,'75.0','','2020-02-15','False','False','235'); +INSERT INTO "npe01__OppPayment__c" VALUES(571,'75.0','','2020-03-15','False','False','236'); +INSERT INTO "npe01__OppPayment__c" VALUES(572,'75.0','','2020-04-15','False','False','59'); +INSERT INTO "npe01__OppPayment__c" VALUES(573,'75.0','','2020-08-15','False','False','60'); +INSERT INTO "npe01__OppPayment__c" VALUES(574,'75.0','','2020-09-15','False','False','61'); +INSERT INTO "npe01__OppPayment__c" VALUES(575,'75.0','','2020-10-15','False','False','62'); +INSERT INTO "npe01__OppPayment__c" VALUES(576,'75.0','','2020-11-15','False','False','63'); +INSERT INTO "npe01__OppPayment__c" VALUES(577,'892.0','2018-10-25','','True','False','5'); +INSERT INTO "npe01__OppPayment__c" VALUES(578,'798.0','2017-05-13','','True','False','6'); +INSERT INTO "npe01__OppPayment__c" VALUES(579,'265.0','2016-04-17','','True','False','7'); +INSERT INTO "npe01__OppPayment__c" VALUES(580,'934.0','2018-04-23','','True','False','8'); +INSERT INTO "npe01__OppPayment__c" VALUES(581,'599.0','2016-11-23','','True','False','9'); +INSERT INTO "npe01__OppPayment__c" VALUES(582,'483.0','2016-06-11','','True','False','40'); +INSERT INTO "npe01__OppPayment__c" VALUES(583,'538.0','2018-10-03','','True','False','10'); +INSERT INTO "npe01__OppPayment__c" VALUES(584,'409.0','2017-03-29','','True','False','134'); +INSERT INTO "npe01__OppPayment__c" VALUES(585,'858.0','2018-03-05','','True','False','135'); +INSERT INTO "npe01__OppPayment__c" VALUES(586,'310.0','2018-02-02','','True','False','39'); +INSERT INTO "npe01__OppPayment__c" VALUES(587,'466.0','2016-12-10','','True','False','111'); +INSERT INTO "npe01__OppPayment__c" VALUES(588,'344.0','2018-01-07','','True','False','112'); +INSERT INTO "npe01__OppPayment__c" VALUES(589,'110.0','2017-05-08','','True','False','113'); +INSERT INTO "npe01__OppPayment__c" VALUES(590,'820.0','2018-01-26','','True','False','3'); +INSERT INTO "npe01__OppPayment__c" VALUES(591,'492.0','2018-09-09','','True','False','4'); +INSERT INTO "npe01__OppPayment__c" VALUES(592,'993.0','2016-04-21','','True','False','11'); +INSERT INTO "npe01__OppPayment__c" VALUES(593,'4.0','2017-04-22','','True','False','12'); +INSERT INTO "npe01__OppPayment__c" VALUES(594,'782.0','2017-03-21','','True','False','114'); +INSERT INTO "npe01__OppPayment__c" VALUES(595,'314.0','2018-12-27','','True','False','115'); +INSERT INTO "npe01__OppPayment__c" VALUES(596,'2434.0','2016-11-02','','True','False','54'); +INSERT INTO "npe01__OppPayment__c" VALUES(597,'75.0','','2020-06-15','False','False','65'); +INSERT INTO "npe01__OppPayment__c" VALUES(598,'75.0','','2020-07-15','False','False','1'); +INSERT INTO "npe01__OppPayment__c" VALUES(599,'75.0','2019-12-15','','True','False','2'); +INSERT INTO "npe01__OppPayment__c" VALUES(600,'457.0','2017-08-27','','True','False','55'); +INSERT INTO "npe01__OppPayment__c" VALUES(601,'13.0','2017-01-19','','True','False','56'); +INSERT INTO "npe01__OppPayment__c" VALUES(602,'234.0','2017-11-04','','True','False','57'); +INSERT INTO "npe01__OppPayment__c" VALUES(603,'84.0','2016-05-05','','True','False','58'); +INSERT INTO "npe01__OppPayment__c" VALUES(604,'952.0','2018-11-16','','True','False','68'); +INSERT INTO "npe01__OppPayment__c" VALUES(605,'3.0','2017-04-20','','True','False','69'); +INSERT INTO "npe01__OppPayment__c" VALUES(606,'521.0','2016-09-22','','True','False','70'); +INSERT INTO "npe01__OppPayment__c" VALUES(607,'177.0','2018-11-23','','True','False','35'); +INSERT INTO "npe01__OppPayment__c" VALUES(608,'567.0','2016-02-26','','True','False','71'); +INSERT INTO "npe01__OppPayment__c" VALUES(609,'75.0','','2020-05-15','False','False','64'); +INSERT INTO "npe01__OppPayment__c" VALUES(610,'744.0','2018-04-26','','True','False','72'); +INSERT INTO "npe01__OppPayment__c" VALUES(611,'619.0','2016-09-11','','True','False','73'); +INSERT INTO "npe01__OppPayment__c" VALUES(612,'3738.0','2017-08-02','','True','False','74'); +INSERT INTO "npe01__OppPayment__c" VALUES(613,'6351.0','2017-12-29','','True','False','75'); +INSERT INTO "npe01__OppPayment__c" VALUES(614,'2233.0','2017-05-18','','True','False','76'); +INSERT INTO "npe01__OppPayment__c" VALUES(615,'3361.0','2016-09-08','','True','False','77'); +INSERT INTO "npe01__OppPayment__c" VALUES(616,'3124.0','2018-05-15','','True','False','78'); +INSERT INTO "npe01__OppPayment__c" VALUES(617,'2909.0','2018-05-16','','True','False','79'); +INSERT INTO "npe01__OppPayment__c" VALUES(618,'1824.0','2017-07-30','','True','False','80'); +INSERT INTO "npe01__OppPayment__c" VALUES(619,'208.0','2018-01-20','','True','False','36'); +INSERT INTO "npe01__OppPayment__c" VALUES(620,'4670.0','2016-09-17','','True','False','81'); +INSERT INTO "npe01__OppPayment__c" VALUES(621,'5400.0','2017-03-22','','True','False','82'); +INSERT INTO "npe01__OppPayment__c" VALUES(622,'9404.0','2016-02-15','','True','False','83'); +INSERT INTO "npe01__OppPayment__c" VALUES(623,'504.0','2017-03-01','','True','False','84'); +INSERT INTO "npe01__OppPayment__c" VALUES(624,'109.0','2019-02-07','','True','False','85'); +INSERT INTO "npe01__OppPayment__c" VALUES(625,'479.0','2017-08-07','','True','False','86'); +INSERT INTO "npe01__OppPayment__c" VALUES(626,'270.0','2016-10-28','','True','False','87'); +INSERT INTO "npe01__OppPayment__c" VALUES(627,'31.0','2019-01-20','','True','False','88'); +INSERT INTO "npe01__OppPayment__c" VALUES(628,'157.0','2018-04-24','','True','False','89'); +INSERT INTO "npe01__OppPayment__c" VALUES(629,'31.0','2017-09-28','','True','False','90'); +INSERT INTO "npe01__OppPayment__c" VALUES(630,'553.0','2017-10-15','','True','False','37'); +INSERT INTO "npe01__OppPayment__c" VALUES(631,'836.0','2019-01-12','','True','False','91'); +INSERT INTO "npe01__OppPayment__c" VALUES(632,'984.0','2016-03-20','','True','False','92'); +INSERT INTO "npe01__OppPayment__c" VALUES(633,'862.0','2017-08-04','','True','False','93'); +INSERT INTO "npe01__OppPayment__c" VALUES(634,'433.0','2018-01-22','','True','False','94'); +INSERT INTO "npe01__OppPayment__c" VALUES(635,'794.0','2018-05-02','','True','False','95'); +INSERT INTO "npe01__OppPayment__c" VALUES(636,'6.0','2018-04-27','','True','False','96'); +INSERT INTO "npe01__OppPayment__c" VALUES(637,'87.0','2017-07-30','','True','False','97'); +INSERT INTO "npe01__OppPayment__c" VALUES(638,'641.0','2018-10-05','','True','False','98'); +INSERT INTO "npe01__OppPayment__c" VALUES(639,'29.0','2017-04-01','','True','False','99'); +INSERT INTO "npe01__OppPayment__c" VALUES(640,'915.0','2016-07-21','','True','False','100'); +INSERT INTO "npe01__OppPayment__c" VALUES(641,'565.0','2018-09-06','','True','False','38'); +INSERT INTO "npe01__OppPayment__c" VALUES(642,'907.0','2018-12-19','','True','False','101'); +INSERT INTO "npe01__OppPayment__c" VALUES(643,'4000.0','2021-11-12','','True','False','237'); +INSERT INTO "npe01__OppPayment__c" VALUES(644,'600.0','2021-11-12','','True','False','241'); CREATE TABLE "npe03__Recurring_Donation__c" ( - id INTEGER NOT NULL, - "Name" VARCHAR(255), - "npe03__Amount__c" VARCHAR(255), - "npe03__Date_Established__c" VARCHAR(255), - "npe03__Schedule_Type__c" VARCHAR(255), - "npe03__Open_Ended_Status__c" VARCHAR(255), - "npe03__Last_Payment_Date__c" VARCHAR(255), - "npe03__Next_Payment_Date__c" VARCHAR(255), - "npe03__Installment_Period__c" VARCHAR(255), - "Day_of_Month__c" VARCHAR(255), - "InstallmentFrequency__c" VARCHAR(255), - "StartDate__c" VARCHAR(255), - "PaymentMethod__c" VARCHAR(255), - "RecurringType__c" VARCHAR(255), - "Status__c" VARCHAR(255), - npe03__contact__c VARCHAR(255), + id INTEGER NOT NULL, + "Name" VARCHAR(255), + "npe03__Amount__c" VARCHAR(255), + "npe03__Date_Established__c" VARCHAR(255), + "npe03__Schedule_Type__c" VARCHAR(255), + "npe03__Open_Ended_Status__c" VARCHAR(255), + "npe03__Last_Payment_Date__c" VARCHAR(255), + "npe03__Next_Payment_Date__c" VARCHAR(255), + "npe03__Installment_Period__c" VARCHAR(255), + "Day_of_Month__c" VARCHAR(255), + "InstallmentFrequency__c" VARCHAR(255), + "StartDate__c" VARCHAR(255), + "PaymentMethod__c" VARCHAR(255), + "RecurringType__c" VARCHAR(255), + "Status__c" VARCHAR(255), + "npe03__Contact__c" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "npe03__Recurring_Donation__c" VALUES(1,'Carl Harvey: $75 Monthly','75.0','2018-02-04','','Open','2019-12-15','2020-01-15','Monthly','15','','2020-01-02','','Open','Active','66'); +INSERT INTO "npe03__Recurring_Donation__c" VALUES(1,'Carl Harvey $75 - Recurring','75.0','2018-02-04','','Open','2019-12-15','2020-01-15','Monthly','15','','2020-01-02','','Open','Active','68'); CREATE TABLE "npe4__Relationship__c" ( - id INTEGER NOT NULL, - "npe4__Type__c" VARCHAR(255), - npe4__contact__c VARCHAR(255), - npe4__related_contact__c VARCHAR(255), + id INTEGER NOT NULL, + "npe4__Type__c" VARCHAR(255), + "npe4__Contact__c" VARCHAR(255), + "npe4__RelatedContact__c" VARCHAR(255), PRIMARY KEY (id) ); -INSERT INTO "npe4__Relationship__c" VALUES(1,'Employee','4','83'); -INSERT INTO "npe4__Relationship__c" VALUES(2,'Employee','6','20'); -INSERT INTO "npe4__Relationship__c" VALUES(3,'Employee','7','12'); -INSERT INTO "npe4__Relationship__c" VALUES(4,'Partner','8','9'); -INSERT INTO "npe4__Relationship__c" VALUES(5,'Employee','8','18'); -INSERT INTO "npe4__Relationship__c" VALUES(6,'Partner','9','8'); -INSERT INTO "npe4__Relationship__c" VALUES(7,'Employee','9','36'); -INSERT INTO "npe4__Relationship__c" VALUES(8,'Employee','10','60'); -INSERT INTO "npe4__Relationship__c" VALUES(9,'Employee','11','97'); -INSERT INTO "npe4__Relationship__c" VALUES(10,'Employee','14','32'); -INSERT INTO "npe4__Relationship__c" VALUES(11,'Employee','12','7'); -INSERT INTO "npe4__Relationship__c" VALUES(12,'Employee','13','37'); -INSERT INTO "npe4__Relationship__c" VALUES(13,'Employee','15','29'); -INSERT INTO "npe4__Relationship__c" VALUES(14,'Employee','16','74'); -INSERT INTO "npe4__Relationship__c" VALUES(15,'Employee','17','66'); -INSERT INTO "npe4__Relationship__c" VALUES(16,'Employee','18','8'); -INSERT INTO "npe4__Relationship__c" VALUES(17,'Employee','19','35'); -INSERT INTO "npe4__Relationship__c" VALUES(18,'Employee','20','6'); -INSERT INTO "npe4__Relationship__c" VALUES(19,'Employee','21','2'); -INSERT INTO "npe4__Relationship__c" VALUES(20,'Employee','26','40'); -INSERT INTO "npe4__Relationship__c" VALUES(21,'Employee','25','79'); -INSERT INTO "npe4__Relationship__c" VALUES(22,'Employee','27','76'); -INSERT INTO "npe4__Relationship__c" VALUES(23,'Employee','28','3'); -INSERT INTO "npe4__Relationship__c" VALUES(24,'Employee','29','15'); -INSERT INTO "npe4__Relationship__c" VALUES(25,'Employee','30','34'); -INSERT INTO "npe4__Relationship__c" VALUES(26,'Employee','31','92'); -INSERT INTO "npe4__Relationship__c" VALUES(27,'Employee','32','14'); -INSERT INTO "npe4__Relationship__c" VALUES(28,'Employee','33','63'); -INSERT INTO "npe4__Relationship__c" VALUES(29,'Employee','34','30'); -INSERT INTO "npe4__Relationship__c" VALUES(30,'Employee','35','19'); -INSERT INTO "npe4__Relationship__c" VALUES(31,'Partner','35','36'); -INSERT INTO "npe4__Relationship__c" VALUES(32,'Employee','36','9'); -INSERT INTO "npe4__Relationship__c" VALUES(33,'Partner','36','35'); -INSERT INTO "npe4__Relationship__c" VALUES(34,'Employee','37','13'); -INSERT INTO "npe4__Relationship__c" VALUES(35,'Employee','38','64'); -INSERT INTO "npe4__Relationship__c" VALUES(36,'Employee','39','89'); -INSERT INTO "npe4__Relationship__c" VALUES(37,'Employee','40','26'); -INSERT INTO "npe4__Relationship__c" VALUES(38,'Employee','41','1'); -INSERT INTO "npe4__Relationship__c" VALUES(39,'Partner','42','43'); -INSERT INTO "npe4__Relationship__c" VALUES(40,'Employee','42','43'); -INSERT INTO "npe4__Relationship__c" VALUES(41,'Partner','42','44'); -INSERT INTO "npe4__Relationship__c" VALUES(42,'Partner','42','45'); -INSERT INTO "npe4__Relationship__c" VALUES(43,'Partner','42','46'); -INSERT INTO "npe4__Relationship__c" VALUES(44,'Partner','42','47'); -INSERT INTO "npe4__Relationship__c" VALUES(45,'Partner','42','48'); -INSERT INTO "npe4__Relationship__c" VALUES(46,'Partner','42','49'); -INSERT INTO "npe4__Relationship__c" VALUES(47,'Partner','42','50'); -INSERT INTO "npe4__Relationship__c" VALUES(48,'Partner','42','51'); -INSERT INTO "npe4__Relationship__c" VALUES(49,'Partner','43','42'); -INSERT INTO "npe4__Relationship__c" VALUES(50,'Employee','43','42'); -INSERT INTO "npe4__Relationship__c" VALUES(51,'Partner','44','42'); -INSERT INTO "npe4__Relationship__c" VALUES(52,'Employee','44','45'); -INSERT INTO "npe4__Relationship__c" VALUES(53,'Partner','45','42'); -INSERT INTO "npe4__Relationship__c" VALUES(54,'Employee','45','44'); -INSERT INTO "npe4__Relationship__c" VALUES(55,'Partner','46','42'); -INSERT INTO "npe4__Relationship__c" VALUES(56,'Employee','46','47'); -INSERT INTO "npe4__Relationship__c" VALUES(57,'Partner','47','42'); -INSERT INTO "npe4__Relationship__c" VALUES(58,'Employee','47','46'); -INSERT INTO "npe4__Relationship__c" VALUES(59,'Employee','57','58'); -INSERT INTO "npe4__Relationship__c" VALUES(60,'Partner','48','42'); -INSERT INTO "npe4__Relationship__c" VALUES(61,'Employee','48','49'); -INSERT INTO "npe4__Relationship__c" VALUES(62,'Employee','22','88'); -INSERT INTO "npe4__Relationship__c" VALUES(63,'Employee','23','91'); -INSERT INTO "npe4__Relationship__c" VALUES(64,'Employee','24','82'); -INSERT INTO "npe4__Relationship__c" VALUES(65,'Partner','49','42'); -INSERT INTO "npe4__Relationship__c" VALUES(66,'Employee','49','48'); -INSERT INTO "npe4__Relationship__c" VALUES(67,'Partner','50','42'); -INSERT INTO "npe4__Relationship__c" VALUES(68,'Employee','50','51'); -INSERT INTO "npe4__Relationship__c" VALUES(69,'Partner','51','42'); -INSERT INTO "npe4__Relationship__c" VALUES(70,'Employee','51','50'); -INSERT INTO "npe4__Relationship__c" VALUES(71,'Employee','52','84'); -INSERT INTO "npe4__Relationship__c" VALUES(72,'Employee','53','61'); -INSERT INTO "npe4__Relationship__c" VALUES(73,'Employee','54','98'); -INSERT INTO "npe4__Relationship__c" VALUES(74,'Employee','66','17'); -INSERT INTO "npe4__Relationship__c" VALUES(75,'Employee','55','62'); -INSERT INTO "npe4__Relationship__c" VALUES(76,'Employee','56','68'); -INSERT INTO "npe4__Relationship__c" VALUES(77,'Employee','96','95'); -INSERT INTO "npe4__Relationship__c" VALUES(78,'Employee','97','11'); -INSERT INTO "npe4__Relationship__c" VALUES(79,'Employee','58','57'); -INSERT INTO "npe4__Relationship__c" VALUES(80,'Employee','59','80'); -INSERT INTO "npe4__Relationship__c" VALUES(81,'Employee','60','10'); -INSERT INTO "npe4__Relationship__c" VALUES(82,'Employee','61','53'); -INSERT INTO "npe4__Relationship__c" VALUES(83,'Employee','62','55'); -INSERT INTO "npe4__Relationship__c" VALUES(84,'Employee','67','75'); -INSERT INTO "npe4__Relationship__c" VALUES(85,'Employee','77','73'); -INSERT INTO "npe4__Relationship__c" VALUES(86,'Employee','68','56'); -INSERT INTO "npe4__Relationship__c" VALUES(87,'Employee','69','71'); -INSERT INTO "npe4__Relationship__c" VALUES(88,'Employee','70','100'); -INSERT INTO "npe4__Relationship__c" VALUES(89,'Employee','71','69'); -INSERT INTO "npe4__Relationship__c" VALUES(90,'Employee','72','90'); -INSERT INTO "npe4__Relationship__c" VALUES(91,'Employee','73','77'); -INSERT INTO "npe4__Relationship__c" VALUES(92,'Employee','74','16'); -INSERT INTO "npe4__Relationship__c" VALUES(93,'Employee','75','67'); -INSERT INTO "npe4__Relationship__c" VALUES(94,'Employee','76','27'); -INSERT INTO "npe4__Relationship__c" VALUES(95,'Employee','78','87'); -INSERT INTO "npe4__Relationship__c" VALUES(96,'Employee','88','22'); -INSERT INTO "npe4__Relationship__c" VALUES(97,'Partner','88','89'); -INSERT INTO "npe4__Relationship__c" VALUES(98,'Employee','79','25'); -INSERT INTO "npe4__Relationship__c" VALUES(99,'Employee','80','59'); -INSERT INTO "npe4__Relationship__c" VALUES(100,'Employee','81','85'); -INSERT INTO "npe4__Relationship__c" VALUES(101,'Employee','82','24'); -INSERT INTO "npe4__Relationship__c" VALUES(102,'Employee','83','4'); -INSERT INTO "npe4__Relationship__c" VALUES(103,'Employee','84','52'); -INSERT INTO "npe4__Relationship__c" VALUES(104,'Employee','85','81'); -INSERT INTO "npe4__Relationship__c" VALUES(105,'Employee','86','65'); -INSERT INTO "npe4__Relationship__c" VALUES(106,'Employee','87','78'); -INSERT INTO "npe4__Relationship__c" VALUES(107,'Employee','90','72'); -INSERT INTO "npe4__Relationship__c" VALUES(108,'Employee','89','39'); -INSERT INTO "npe4__Relationship__c" VALUES(109,'Partner','89','88'); -INSERT INTO "npe4__Relationship__c" VALUES(110,'Employee','91','23'); -INSERT INTO "npe4__Relationship__c" VALUES(111,'Employee','63','33'); -INSERT INTO "npe4__Relationship__c" VALUES(112,'Employee','64','38'); -INSERT INTO "npe4__Relationship__c" VALUES(113,'Employee','65','86'); -INSERT INTO "npe4__Relationship__c" VALUES(114,'Employee','92','31'); -INSERT INTO "npe4__Relationship__c" VALUES(115,'Employee','93','5'); -INSERT INTO "npe4__Relationship__c" VALUES(116,'Employee','94','99'); -INSERT INTO "npe4__Relationship__c" VALUES(117,'Employee','95','96'); -INSERT INTO "npe4__Relationship__c" VALUES(118,'Employee','98','54'); -INSERT INTO "npe4__Relationship__c" VALUES(119,'Employee','99','94'); -INSERT INTO "npe4__Relationship__c" VALUES(120,'Employee','1','41'); -INSERT INTO "npe4__Relationship__c" VALUES(121,'Employee','100','70'); -INSERT INTO "npe4__Relationship__c" VALUES(122,'Employee','5','93'); -INSERT INTO "npe4__Relationship__c" VALUES(123,'Employee','2','21'); -INSERT INTO "npe4__Relationship__c" VALUES(124,'Employee','3','28'); +INSERT INTO "npe4__Relationship__c" VALUES(1,'Employee','2','40'); +INSERT INTO "npe4__Relationship__c" VALUES(2,'Employee','3','14'); +INSERT INTO "npe4__Relationship__c" VALUES(3,'Employee','4','7'); +INSERT INTO "npe4__Relationship__c" VALUES(4,'Employee','5','54'); +INSERT INTO "npe4__Relationship__c" VALUES(5,'Employee','54','5'); +INSERT INTO "npe4__Relationship__c" VALUES(6,'Employee','55','20'); +INSERT INTO "npe4__Relationship__c" VALUES(7,'Employee','6','51'); +INSERT INTO "npe4__Relationship__c" VALUES(8,'Employee','7','4'); +INSERT INTO "npe4__Relationship__c" VALUES(9,'Employee','67','10'); +INSERT INTO "npe4__Relationship__c" VALUES(10,'Employee','59','19'); +INSERT INTO "npe4__Relationship__c" VALUES(11,'Employee','81','8'); +INSERT INTO "npe4__Relationship__c" VALUES(12,'Employee','55','20'); +INSERT INTO "npe4__Relationship__c" VALUES(13,'Employee','16','21'); +INSERT INTO "npe4__Relationship__c" VALUES(14,'Employee','46','22'); +INSERT INTO "npe4__Relationship__c" VALUES(15,'Employee','41','23'); +INSERT INTO "npe4__Relationship__c" VALUES(16,'Employee','38','24'); +INSERT INTO "npe4__Relationship__c" VALUES(17,'Employee','85','25'); +INSERT INTO "npe4__Relationship__c" VALUES(18,'Employee','68','26'); +INSERT INTO "npe4__Relationship__c" VALUES(19,'Employee','17','27'); +INSERT INTO "npe4__Relationship__c" VALUES(20,'Employee','44','28'); +INSERT INTO "npe4__Relationship__c" VALUES(21,'Employee','30','11'); +INSERT INTO "npe4__Relationship__c" VALUES(22,'Employee','15','29'); +INSERT INTO "npe4__Relationship__c" VALUES(23,'Employee','11','30'); +INSERT INTO "npe4__Relationship__c" VALUES(24,'Employee','99','31'); +INSERT INTO "npe4__Relationship__c" VALUES(25,'Employee','102','32'); +INSERT INTO "npe4__Relationship__c" VALUES(26,'Employee','93','33'); +INSERT INTO "npe4__Relationship__c" VALUES(27,'Employee','90','34'); +INSERT INTO "npe4__Relationship__c" VALUES(28,'Employee','66','35'); +INSERT INTO "npe4__Relationship__c" VALUES(29,'Employee','10','67'); +INSERT INTO "npe4__Relationship__c" VALUES(30,'Employee','19','59'); +INSERT INTO "npe4__Relationship__c" VALUES(31,'Employee','8','81'); +INSERT INTO "npe4__Relationship__c" VALUES(32,'Employee','20','55'); +INSERT INTO "npe4__Relationship__c" VALUES(33,'Employee','21','16'); +INSERT INTO "npe4__Relationship__c" VALUES(34,'Employee','22','46'); +INSERT INTO "npe4__Relationship__c" VALUES(35,'Employee','23','41'); +INSERT INTO "npe4__Relationship__c" VALUES(36,'Employee','24','38'); +INSERT INTO "npe4__Relationship__c" VALUES(37,'Employee','25','85'); +INSERT INTO "npe4__Relationship__c" VALUES(38,'Employee','26','68'); +INSERT INTO "npe4__Relationship__c" VALUES(39,'Employee','27','17'); +INSERT INTO "npe4__Relationship__c" VALUES(40,'Employee','28','44'); +INSERT INTO "npe4__Relationship__c" VALUES(41,'Employee','11','30'); +INSERT INTO "npe4__Relationship__c" VALUES(42,'Employee','29','15'); +INSERT INTO "npe4__Relationship__c" VALUES(43,'Employee','30','11'); +INSERT INTO "npe4__Relationship__c" VALUES(44,'Employee','31','99'); +INSERT INTO "npe4__Relationship__c" VALUES(45,'Employee','32','102'); +INSERT INTO "npe4__Relationship__c" VALUES(46,'Employee','33','93'); +INSERT INTO "npe4__Relationship__c" VALUES(47,'Employee','34','90'); +INSERT INTO "npe4__Relationship__c" VALUES(48,'Employee','35','66'); +INSERT INTO "npe4__Relationship__c" VALUES(49,'Employee','36','87'); +INSERT INTO "npe4__Relationship__c" VALUES(50,'Employee','37','12'); +INSERT INTO "npe4__Relationship__c" VALUES(51,'Employee','38','24'); +INSERT INTO "npe4__Relationship__c" VALUES(52,'Employee','12','37'); +INSERT INTO "npe4__Relationship__c" VALUES(53,'Employee','39','43'); +INSERT INTO "npe4__Relationship__c" VALUES(54,'Employee','40','2'); +INSERT INTO "npe4__Relationship__c" VALUES(55,'Employee','41','23'); +INSERT INTO "npe4__Relationship__c" VALUES(56,'Employee','42','62'); +INSERT INTO "npe4__Relationship__c" VALUES(57,'Employee','43','39'); +INSERT INTO "npe4__Relationship__c" VALUES(58,'Employee','44','28'); +INSERT INTO "npe4__Relationship__c" VALUES(59,'Partner','44','45'); +INSERT INTO "npe4__Relationship__c" VALUES(60,'Partner','45','44'); +INSERT INTO "npe4__Relationship__c" VALUES(61,'Employee','45','18'); +INSERT INTO "npe4__Relationship__c" VALUES(62,'Employee','46','22'); +INSERT INTO "npe4__Relationship__c" VALUES(63,'Employee','47','63'); +INSERT INTO "npe4__Relationship__c" VALUES(64,'Employee','87','36'); +INSERT INTO "npe4__Relationship__c" VALUES(65,'Employee','12','37'); +INSERT INTO "npe4__Relationship__c" VALUES(66,'Employee','24','38'); +INSERT INTO "npe4__Relationship__c" VALUES(67,'Employee','37','12'); +INSERT INTO "npe4__Relationship__c" VALUES(68,'Employee','43','39'); +INSERT INTO "npe4__Relationship__c" VALUES(69,'Employee','2','40'); +INSERT INTO "npe4__Relationship__c" VALUES(70,'Employee','23','41'); +INSERT INTO "npe4__Relationship__c" VALUES(71,'Employee','62','42'); +INSERT INTO "npe4__Relationship__c" VALUES(72,'Employee','39','43'); +INSERT INTO "npe4__Relationship__c" VALUES(73,'Employee','28','44'); +INSERT INTO "npe4__Relationship__c" VALUES(74,'Partner','45','44'); +INSERT INTO "npe4__Relationship__c" VALUES(75,'Partner','44','45'); +INSERT INTO "npe4__Relationship__c" VALUES(76,'Employee','18','45'); +INSERT INTO "npe4__Relationship__c" VALUES(77,'Employee','22','46'); +INSERT INTO "npe4__Relationship__c" VALUES(78,'Employee','63','47'); +INSERT INTO "npe4__Relationship__c" VALUES(79,'Employee','100','48'); +INSERT INTO "npe4__Relationship__c" VALUES(80,'Employee','94','13'); +INSERT INTO "npe4__Relationship__c" VALUES(81,'Employee','35','66'); +INSERT INTO "npe4__Relationship__c" VALUES(82,'Employee','10','67'); +INSERT INTO "npe4__Relationship__c" VALUES(83,'Partner','70','69'); +INSERT INTO "npe4__Relationship__c" VALUES(84,'Employee','70','69'); +INSERT INTO "npe4__Relationship__c" VALUES(85,'Partner','71','69'); +INSERT INTO "npe4__Relationship__c" VALUES(86,'Partner','72','69'); +INSERT INTO "npe4__Relationship__c" VALUES(87,'Partner','73','69'); +INSERT INTO "npe4__Relationship__c" VALUES(88,'Partner','74','69'); +INSERT INTO "npe4__Relationship__c" VALUES(89,'Partner','75','69'); +INSERT INTO "npe4__Relationship__c" VALUES(90,'Partner','76','69'); +INSERT INTO "npe4__Relationship__c" VALUES(91,'Partner','77','69'); +INSERT INTO "npe4__Relationship__c" VALUES(92,'Partner','78','69'); +INSERT INTO "npe4__Relationship__c" VALUES(93,'Partner','69','70'); +INSERT INTO "npe4__Relationship__c" VALUES(94,'Employee','69','70'); +INSERT INTO "npe4__Relationship__c" VALUES(95,'Partner','69','71'); +INSERT INTO "npe4__Relationship__c" VALUES(96,'Employee','72','71'); +INSERT INTO "npe4__Relationship__c" VALUES(97,'Partner','69','72'); +INSERT INTO "npe4__Relationship__c" VALUES(98,'Employee','71','72'); +INSERT INTO "npe4__Relationship__c" VALUES(99,'Partner','69','73'); +INSERT INTO "npe4__Relationship__c" VALUES(100,'Employee','74','73'); +INSERT INTO "npe4__Relationship__c" VALUES(101,'Employee','13','94'); +INSERT INTO "npe4__Relationship__c" VALUES(102,'Employee','66','35'); +INSERT INTO "npe4__Relationship__c" VALUES(103,'Employee','67','10'); +INSERT INTO "npe4__Relationship__c" VALUES(104,'Partner','69','70'); +INSERT INTO "npe4__Relationship__c" VALUES(105,'Employee','69','70'); +INSERT INTO "npe4__Relationship__c" VALUES(106,'Partner','69','71'); +INSERT INTO "npe4__Relationship__c" VALUES(107,'Partner','69','72'); +INSERT INTO "npe4__Relationship__c" VALUES(108,'Partner','69','73'); +INSERT INTO "npe4__Relationship__c" VALUES(109,'Partner','69','74'); +INSERT INTO "npe4__Relationship__c" VALUES(110,'Partner','69','75'); +INSERT INTO "npe4__Relationship__c" VALUES(111,'Partner','69','76'); +INSERT INTO "npe4__Relationship__c" VALUES(112,'Partner','69','77'); +INSERT INTO "npe4__Relationship__c" VALUES(113,'Partner','69','78'); +INSERT INTO "npe4__Relationship__c" VALUES(114,'Partner','70','69'); +INSERT INTO "npe4__Relationship__c" VALUES(115,'Employee','70','69'); +INSERT INTO "npe4__Relationship__c" VALUES(116,'Partner','71','69'); +INSERT INTO "npe4__Relationship__c" VALUES(117,'Employee','71','72'); +INSERT INTO "npe4__Relationship__c" VALUES(118,'Partner','72','69'); +INSERT INTO "npe4__Relationship__c" VALUES(119,'Employee','72','71'); +INSERT INTO "npe4__Relationship__c" VALUES(120,'Partner','73','69'); +INSERT INTO "npe4__Relationship__c" VALUES(121,'Employee','73','74'); +INSERT INTO "npe4__Relationship__c" VALUES(122,'Partner','74','69'); +INSERT INTO "npe4__Relationship__c" VALUES(123,'Employee','74','73'); +INSERT INTO "npe4__Relationship__c" VALUES(124,'Partner','75','69'); +INSERT INTO "npe4__Relationship__c" VALUES(125,'Employee','75','76'); +INSERT INTO "npe4__Relationship__c" VALUES(126,'Partner','76','69'); +INSERT INTO "npe4__Relationship__c" VALUES(127,'Employee','76','75'); +INSERT INTO "npe4__Relationship__c" VALUES(128,'Employee','14','3'); +INSERT INTO "npe4__Relationship__c" VALUES(129,'Employee','48','100'); +INSERT INTO "npe4__Relationship__c" VALUES(130,'Partner','77','69'); +INSERT INTO "npe4__Relationship__c" VALUES(131,'Employee','77','78'); +INSERT INTO "npe4__Relationship__c" VALUES(132,'Partner','78','69'); +INSERT INTO "npe4__Relationship__c" VALUES(133,'Employee','78','77'); +INSERT INTO "npe4__Relationship__c" VALUES(134,'Employee','49','95'); +INSERT INTO "npe4__Relationship__c" VALUES(135,'Employee','50','60'); +INSERT INTO "npe4__Relationship__c" VALUES(136,'Employee','51','6'); +INSERT INTO "npe4__Relationship__c" VALUES(137,'Employee','52','61'); +INSERT INTO "npe4__Relationship__c" VALUES(138,'Employee','53','79'); +INSERT INTO "npe4__Relationship__c" VALUES(139,'Employee','57','56'); +INSERT INTO "npe4__Relationship__c" VALUES(140,'Employee','56','57'); +INSERT INTO "npe4__Relationship__c" VALUES(141,'Partner','69','74'); +INSERT INTO "npe4__Relationship__c" VALUES(142,'Employee','73','74'); +INSERT INTO "npe4__Relationship__c" VALUES(143,'Partner','69','75'); +INSERT INTO "npe4__Relationship__c" VALUES(144,'Employee','76','75'); +INSERT INTO "npe4__Relationship__c" VALUES(145,'Partner','69','76'); +INSERT INTO "npe4__Relationship__c" VALUES(146,'Employee','75','76'); +INSERT INTO "npe4__Relationship__c" VALUES(147,'Employee','3','14'); +INSERT INTO "npe4__Relationship__c" VALUES(148,'Partner','69','77'); +INSERT INTO "npe4__Relationship__c" VALUES(149,'Employee','78','77'); +INSERT INTO "npe4__Relationship__c" VALUES(150,'Partner','69','78'); +INSERT INTO "npe4__Relationship__c" VALUES(151,'Employee','77','78'); +INSERT INTO "npe4__Relationship__c" VALUES(152,'Employee','95','49'); +INSERT INTO "npe4__Relationship__c" VALUES(153,'Employee','60','50'); +INSERT INTO "npe4__Relationship__c" VALUES(154,'Employee','6','51'); +INSERT INTO "npe4__Relationship__c" VALUES(155,'Employee','61','52'); +INSERT INTO "npe4__Relationship__c" VALUES(156,'Employee','79','53'); +INSERT INTO "npe4__Relationship__c" VALUES(157,'Employee','56','57'); +INSERT INTO "npe4__Relationship__c" VALUES(158,'Employee','57','56'); +INSERT INTO "npe4__Relationship__c" VALUES(159,'Employee','91','58'); +INSERT INTO "npe4__Relationship__c" VALUES(160,'Employee','29','15'); +INSERT INTO "npe4__Relationship__c" VALUES(161,'Employee','19','59'); +INSERT INTO "npe4__Relationship__c" VALUES(162,'Employee','50','60'); +INSERT INTO "npe4__Relationship__c" VALUES(163,'Employee','52','61'); +INSERT INTO "npe4__Relationship__c" VALUES(164,'Employee','42','62'); +INSERT INTO "npe4__Relationship__c" VALUES(165,'Employee','47','63'); +INSERT INTO "npe4__Relationship__c" VALUES(166,'Employee','97','64'); +INSERT INTO "npe4__Relationship__c" VALUES(167,'Employee','26','68'); +INSERT INTO "npe4__Relationship__c" VALUES(168,'Employee','86','65'); +INSERT INTO "npe4__Relationship__c" VALUES(169,'Employee','15','29'); +INSERT INTO "npe4__Relationship__c" VALUES(170,'Employee','59','19'); +INSERT INTO "npe4__Relationship__c" VALUES(171,'Employee','60','50'); +INSERT INTO "npe4__Relationship__c" VALUES(172,'Employee','61','52'); +INSERT INTO "npe4__Relationship__c" VALUES(173,'Employee','62','42'); +INSERT INTO "npe4__Relationship__c" VALUES(174,'Employee','63','47'); +INSERT INTO "npe4__Relationship__c" VALUES(175,'Employee','64','97'); +INSERT INTO "npe4__Relationship__c" VALUES(176,'Employee','68','26'); +INSERT INTO "npe4__Relationship__c" VALUES(177,'Employee','65','86'); +INSERT INTO "npe4__Relationship__c" VALUES(178,'Employee','79','53'); +INSERT INTO "npe4__Relationship__c" VALUES(179,'Employee','80','82'); +INSERT INTO "npe4__Relationship__c" VALUES(180,'Employee','16','21'); +INSERT INTO "npe4__Relationship__c" VALUES(181,'Employee','81','8'); +INSERT INTO "npe4__Relationship__c" VALUES(182,'Employee','82','80'); +INSERT INTO "npe4__Relationship__c" VALUES(183,'Employee','83','101'); +INSERT INTO "npe4__Relationship__c" VALUES(184,'Employee','84','88'); +INSERT INTO "npe4__Relationship__c" VALUES(185,'Employee','85','25'); +INSERT INTO "npe4__Relationship__c" VALUES(186,'Employee','86','65'); +INSERT INTO "npe4__Relationship__c" VALUES(187,'Employee','87','36'); +INSERT INTO "npe4__Relationship__c" VALUES(188,'Employee','88','84'); +INSERT INTO "npe4__Relationship__c" VALUES(189,'Employee','89','98'); +INSERT INTO "npe4__Relationship__c" VALUES(190,'Employee','90','34'); +INSERT INTO "npe4__Relationship__c" VALUES(191,'Employee','17','27'); +INSERT INTO "npe4__Relationship__c" VALUES(192,'Employee','58','91'); +INSERT INTO "npe4__Relationship__c" VALUES(193,'Partner','17','18'); +INSERT INTO "npe4__Relationship__c" VALUES(194,'Employee','91','58'); +INSERT INTO "npe4__Relationship__c" VALUES(195,'Employee','92','96'); +INSERT INTO "npe4__Relationship__c" VALUES(196,'Employee','93','33'); +INSERT INTO "npe4__Relationship__c" VALUES(197,'Employee','94','13'); +INSERT INTO "npe4__Relationship__c" VALUES(198,'Employee','95','49'); +INSERT INTO "npe4__Relationship__c" VALUES(199,'Employee','96','92'); +INSERT INTO "npe4__Relationship__c" VALUES(200,'Employee','97','64'); +INSERT INTO "npe4__Relationship__c" VALUES(201,'Employee','98','89'); +INSERT INTO "npe4__Relationship__c" VALUES(202,'Employee','99','31'); +INSERT INTO "npe4__Relationship__c" VALUES(203,'Partner','99','100'); +INSERT INTO "npe4__Relationship__c" VALUES(204,'Employee','100','48'); +INSERT INTO "npe4__Relationship__c" VALUES(205,'Partner','100','99'); +INSERT INTO "npe4__Relationship__c" VALUES(206,'Employee','18','45'); +INSERT INTO "npe4__Relationship__c" VALUES(207,'Partner','18','17'); +INSERT INTO "npe4__Relationship__c" VALUES(208,'Employee','101','83'); +INSERT INTO "npe4__Relationship__c" VALUES(209,'Employee','102','32'); +INSERT INTO "npe4__Relationship__c" VALUES(210,'Employee','53','79'); +INSERT INTO "npe4__Relationship__c" VALUES(211,'Employee','82','80'); +INSERT INTO "npe4__Relationship__c" VALUES(212,'Employee','21','16'); +INSERT INTO "npe4__Relationship__c" VALUES(213,'Employee','8','81'); +INSERT INTO "npe4__Relationship__c" VALUES(214,'Employee','80','82'); +INSERT INTO "npe4__Relationship__c" VALUES(215,'Employee','101','83'); +INSERT INTO "npe4__Relationship__c" VALUES(216,'Employee','88','84'); +INSERT INTO "npe4__Relationship__c" VALUES(217,'Employee','25','85'); +INSERT INTO "npe4__Relationship__c" VALUES(218,'Employee','65','86'); +INSERT INTO "npe4__Relationship__c" VALUES(219,'Employee','36','87'); +INSERT INTO "npe4__Relationship__c" VALUES(220,'Employee','84','88'); +INSERT INTO "npe4__Relationship__c" VALUES(221,'Employee','98','89'); +INSERT INTO "npe4__Relationship__c" VALUES(222,'Employee','34','90'); +INSERT INTO "npe4__Relationship__c" VALUES(223,'Employee','27','17'); +INSERT INTO "npe4__Relationship__c" VALUES(224,'Partner','18','17'); +INSERT INTO "npe4__Relationship__c" VALUES(225,'Employee','58','91'); +INSERT INTO "npe4__Relationship__c" VALUES(226,'Employee','96','92'); +INSERT INTO "npe4__Relationship__c" VALUES(227,'Employee','33','93'); +INSERT INTO "npe4__Relationship__c" VALUES(228,'Employee','13','94'); +INSERT INTO "npe4__Relationship__c" VALUES(229,'Employee','49','95'); +INSERT INTO "npe4__Relationship__c" VALUES(230,'Employee','92','96'); +INSERT INTO "npe4__Relationship__c" VALUES(231,'Employee','64','97'); +INSERT INTO "npe4__Relationship__c" VALUES(232,'Employee','89','98'); +INSERT INTO "npe4__Relationship__c" VALUES(233,'Employee','31','99'); +INSERT INTO "npe4__Relationship__c" VALUES(234,'Partner','100','99'); +INSERT INTO "npe4__Relationship__c" VALUES(235,'Employee','48','100'); +INSERT INTO "npe4__Relationship__c" VALUES(236,'Partner','99','100'); +INSERT INTO "npe4__Relationship__c" VALUES(237,'Partner','17','18'); +INSERT INTO "npe4__Relationship__c" VALUES(238,'Employee','83','101'); +INSERT INTO "npe4__Relationship__c" VALUES(239,'Employee','32','102'); +INSERT INTO "npe4__Relationship__c" VALUES(240,'Employee','45','18'); +INSERT INTO "npe4__Relationship__c" VALUES(241,'Employee','40','2'); +INSERT INTO "npe4__Relationship__c" VALUES(242,'Employee','14','3'); +INSERT INTO "npe4__Relationship__c" VALUES(243,'Employee','7','4'); +INSERT INTO "npe4__Relationship__c" VALUES(244,'Employee','54','5'); +INSERT INTO "npe4__Relationship__c" VALUES(245,'Employee','5','54'); +INSERT INTO "npe4__Relationship__c" VALUES(246,'Employee','20','55'); +INSERT INTO "npe4__Relationship__c" VALUES(247,'Employee','51','6'); +INSERT INTO "npe4__Relationship__c" VALUES(248,'Employee','4','7'); COMMIT; diff --git a/documentation/automation.md b/documentation/automation.md index 29c794fbfb7..c1ff0629ba9 100644 --- a/documentation/automation.md +++ b/documentation/automation.md @@ -79,11 +79,25 @@ as well as many other features needed to define the workspace for the org's inte | `enable_crlp` | Task | Enable the NPSP Customizable Rollups feature (works for both Managed and Unmanaged) | | `enable_incremental_rollups` | Task | Configure NPSP Customizable Rollups to activate Incremental Rollups | +### Advanced Mapping + +| Name | Type | Description | +| ----------------------------- | ---- | -------------------------------------------------------------------------------------------- | +| `enable_advanced_mapping` | Flow | Enable the BDI Advanced Mapping feature (unmanaged code only). +| `enable_advanced_mapping_managed` | Flow | Enable the BDI Advanced Mapping feature (managed package only) + +### Gift Entry + +| Name | Type | Description | +| ----------------------------- | ---- | -------------------------------------------------------------------------------------------- | +| `enable_gift_entry` | Flow | Enable the Gift Entry feature (unmanaged code only). +| `enable_gift_entry_managed` | Flow | Enable the Gift Entry feature (managed package only) + ### Enhanced Recurring Donations | Name | Type | Description | | ----------------------------- | ---- | -------------------------------------------------------------------------------------------- | -| `enable_rd2` | Flow | Fully enables NPSP Enhanced Recurring Donations, including enabling CRLP and running the data migration (unmanaged only) +| `enable_rd2` | Flow | Fully enables NPSP Enhanced Recurring Donations, including enabling CRLP and running the data migration | `deploy_rd2_config` | Task | Deploys the Unpackaged Metadata needed for Enhanced Recurring Donations (unmanaged only) ### Static Analysis diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_Application.cls b/force-app/infrastructure/apex-common/main/classes/fflib_Application.cls new file mode 100644 index 00000000000..3a9bd27e9e6 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_Application.cls @@ -0,0 +1,128 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * Class provides inner classes implementing factories for the main components + * of the Apex Enterprise Patterns, Service, Unit Of Work, Selector and Domain. + * See the sample applications Application.cls file for an example + **/ +public virtual class fflib_Application +{ + /** + * Class implements a Unit of Work factory + **/ + public virtual class UnitOfWorkFactory + { + private List m_objectTypes; + private fflib_ISObjectUnitOfWork m_mockUow; + + /** + * Constructs a Unit Of Work factory + **/ + public UnitOfWorkFactory() { } + + /** + * Constructs a Unit Of Work factory + * + * @param objectTypes List of SObjectTypes in dependency order + **/ + public UnitOfWorkFactory(List objectTypes) + { + m_objectTypes = objectTypes.clone(); + } + + /** + * Returns a new fflib_SObjectUnitOfWork configured with the + * SObjectType list provided in the constructor, returns a Mock implementation + * if set via the setMock method + **/ + public virtual fflib_ISObjectUnitOfWork newInstance() + { + // Mock? + if(m_mockUow!=null) + return m_mockUow; + return new fflib_SObjectUnitOfWork(m_objectTypes); + } + + /** + * Returns a new fflib_SObjectUnitOfWork configured with the + * SObjectType list provided in the constructor, returns a Mock implementation + * if set via the setMock method + **/ + public virtual fflib_ISObjectUnitOfWork newInstance(fflib_SObjectUnitOfWork.IDML dml) + { + // Mock? + if(m_mockUow!=null) + return m_mockUow; + return new fflib_SObjectUnitOfWork(m_objectTypes, dml); + } + + /** + * Returns a new fflib_SObjectUnitOfWork configured with the + * SObjectType list specified, returns a Mock implementation + * if set via the setMock method + * + * @remark If mock is set, the list of SObjectType in the mock could be different + * then the list of SObjectType specified in this method call + **/ + public virtual fflib_ISObjectUnitOfWork newInstance(List objectTypes) + { + // Mock? + if(m_mockUow!=null) + return m_mockUow; + return new fflib_SObjectUnitOfWork(objectTypes); + } + + /** + * Returns a new fflib_SObjectUnitOfWork configured with the + * SObjectType list specified, returns a Mock implementation + * if set via the setMock method + * + * @remark If mock is set, the list of SObjectType in the mock could be different + * then the list of SObjectType specified in this method call + **/ + public virtual fflib_ISObjectUnitOfWork newInstance(List objectTypes, fflib_SObjectUnitOfWork.IDML dml) + { + // Mock? + if(m_mockUow!=null) + return m_mockUow; + return new fflib_SObjectUnitOfWork(objectTypes, dml); + } + + @TestVisible + protected virtual void setMock(fflib_ISObjectUnitOfWork mockUow) + { + m_mockUow = mockUow; + } + } + + public class ApplicationException extends Exception { } + + /** + * Exception representing a developer coding error, not intended for end user eyes + **/ + public class DeveloperException extends Exception { } +} \ No newline at end of file diff --git a/force-app/main/default/classes/ElevateAuthorizedGiftResponse.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_Application.cls-meta.xml similarity index 80% rename from force-app/main/default/classes/ElevateAuthorizedGiftResponse.cls-meta.xml rename to force-app/infrastructure/apex-common/main/classes/fflib_Application.cls-meta.xml index d75b0582fba..f928c8e56bc 100644 --- a/force-app/main/default/classes/ElevateAuthorizedGiftResponse.cls-meta.xml +++ b/force-app/infrastructure/apex-common/main/classes/fflib_Application.cls-meta.xml @@ -1,5 +1,5 @@ - 51.0 + 53.0 Active diff --git a/force-app/main/default/classes/fflib_Domain.cls b/force-app/infrastructure/apex-common/main/classes/fflib_IDomain.cls similarity index 95% rename from force-app/main/default/classes/fflib_Domain.cls rename to force-app/infrastructure/apex-common/main/classes/fflib_IDomain.cls index 7610f60579b..298f5b8ed42 100644 --- a/force-app/main/default/classes/fflib_Domain.cls +++ b/force-app/infrastructure/apex-common/main/classes/fflib_IDomain.cls @@ -23,8 +23,8 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ -public interface fflib_Domain +public interface fflib_IDomain { - Object getType(); - List getObjects(); + Object getType(); + List getObjects(); } \ No newline at end of file diff --git a/force-app/main/default/classes/RP_GitHubClient.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_IDomain.cls-meta.xml similarity index 80% rename from force-app/main/default/classes/RP_GitHubClient.cls-meta.xml rename to force-app/infrastructure/apex-common/main/classes/fflib_IDomain.cls-meta.xml index db9bf8c6a58..f928c8e56bc 100644 --- a/force-app/main/default/classes/RP_GitHubClient.cls-meta.xml +++ b/force-app/infrastructure/apex-common/main/classes/fflib_IDomain.cls-meta.xml @@ -1,5 +1,5 @@ - 48.0 + 53.0 Active diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_IDomainConstructor.cls b/force-app/infrastructure/apex-common/main/classes/fflib_IDomainConstructor.cls new file mode 100644 index 00000000000..c4096d1d00a --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_IDomainConstructor.cls @@ -0,0 +1,29 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ +public interface fflib_IDomainConstructor +{ + fflib_IDomain construct(List objects); +} \ No newline at end of file diff --git a/force-app/main/default/classes/ElevateTokenizedGift.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_IDomainConstructor.cls-meta.xml similarity index 80% rename from force-app/main/default/classes/ElevateTokenizedGift.cls-meta.xml rename to force-app/infrastructure/apex-common/main/classes/fflib_IDomainConstructor.cls-meta.xml index d75b0582fba..f928c8e56bc 100644 --- a/force-app/main/default/classes/ElevateTokenizedGift.cls-meta.xml +++ b/force-app/infrastructure/apex-common/main/classes/fflib_IDomainConstructor.cls-meta.xml @@ -1,5 +1,5 @@ - 51.0 + 53.0 Active diff --git a/force-app/main/default/classes/fflib_Objects.cls b/force-app/infrastructure/apex-common/main/classes/fflib_IObjects.cls similarity index 53% rename from force-app/main/default/classes/fflib_Objects.cls rename to force-app/infrastructure/apex-common/main/classes/fflib_IObjects.cls index ec08b84e7ee..d050e104a74 100644 --- a/force-app/main/default/classes/fflib_Objects.cls +++ b/force-app/infrastructure/apex-common/main/classes/fflib_IObjects.cls @@ -23,73 +23,57 @@ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/ -public virtual class fflib_Objects implements fflib_Domain +public interface fflib_IObjects extends fflib_IDomain { - protected List Objects { get; private set;} + /** + * @param value Values to check if they are part of the domain + * + * @return True if the provided value is part of the domain + */ + Boolean contains(Object value); - /** - * Class constructor - */ - public fflib_Objects(List objects) - { - this.Objects = objects.clone(); - } + /** + * @param values Values to check if they are part of the domain + * + * @return True if all the provided values are part of the domain + */ + Boolean containsAll(List values); - public virtual Object getType() - { - return Object.class; - } + /** + * @param values Values to check if they are part of the domain + * + * @return True if all the provided values are part of the domain + */ + Boolean containsAll(Set values); - public List getObjects() - { - return this.Objects; - } + /** + * @param value Value to check if it is part of the domain + * + * @return True if the provided value is not part of the domain + */ + Boolean containsNot(Object value); - public Boolean contains(Object value) - { - return getObjects().contains(value); - } + /** + * @param values Values to check if they are part of the domain + * + * @return True if all the provided values are not part of the domain + */ + Boolean containsNot(List values); - public Boolean containsAll(List values) - { - return containsAll(new Set(values)); - } + /** + * @param values Values to check if they are part of the domain + * + * @return True if all the provided values are not part of the domain + */ + Boolean containsNot(Set values); - public Boolean containsAll(Set values) - { - for (Object value : values) - { - if (!getObjects().contains(value)) return false; - } - return true; - } + /** + * @return Returns True is the domain is empty + */ + Boolean isEmpty(); - public Boolean containsNot(Object value) - { - return !contains(value); - } - - public Boolean containsNot(List values) - { - return containsNot(new Set(values)); - } - - public Boolean containsNot(Set values) - { - for (Object value : values) - { - if (getObjects().contains(value)) return false; - } - return true; - } - - public Boolean isEmpty() - { - return (getObjects() == null || getObjects().isEmpty()); - } - - public Boolean isNotEmpty() - { - return !isEmpty(); - } + /** + * @return Returns True is the domain has objects + */ + Boolean isNotEmpty(); } \ No newline at end of file diff --git a/force-app/main/default/classes/ElevateAuthorizedGift.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_IObjects.cls-meta.xml similarity index 80% rename from force-app/main/default/classes/ElevateAuthorizedGift.cls-meta.xml rename to force-app/infrastructure/apex-common/main/classes/fflib_IObjects.cls-meta.xml index d75b0582fba..f928c8e56bc 100644 --- a/force-app/main/default/classes/ElevateAuthorizedGift.cls-meta.xml +++ b/force-app/infrastructure/apex-common/main/classes/fflib_IObjects.cls-meta.xml @@ -1,5 +1,5 @@ - 51.0 + 53.0 Active diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectDomain.cls b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectDomain.cls new file mode 100644 index 00000000000..3dfadce34d4 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectDomain.cls @@ -0,0 +1,38 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +public interface fflib_ISObjectDomain extends fflib_IDomain +{ + /** + * Returns the SObjectType this Domain class represents + **/ + Schema.SObjectType sObjectType(); + + /** + * Alternative to the Records property, provided to support mocking of Domain classes + **/ + List getRecords(); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectDomain.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectDomain.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectDomain.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectSelector.cls b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectSelector.cls new file mode 100644 index 00000000000..6cc8a3baddc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectSelector.cls @@ -0,0 +1,38 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +public interface fflib_ISObjectSelector +{ + /** + * Provides the SObjectType for the object the given Selector is providing query logic for + **/ + Schema.SObjectType sObjectType(); + + /** + * Selects by Id records using the fields defined by the Selector configuration + **/ + List selectSObjectsById(Set idSet); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectSelector.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectSelector.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectSelector.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls new file mode 100644 index 00000000000..a18e1256f40 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls @@ -0,0 +1,229 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * @see fflib_SObjectUnitOfWork + **/ +public interface fflib_ISObjectUnitOfWork +{ + /** + * Register a newly created SObject instance to be inserted when commitWork is called + * + * @param record A newly created SObject instance to be inserted during commitWork + **/ + void registerNew(SObject record); + /** + * Register a list of newly created SObject instances to be inserted when commitWork is called + * + * @param records A list of newly created SObject instances to be inserted during commitWork + **/ + void registerNew(List records); + /** + * Register a newly created SObject instance to be inserted when commitWork is called, + * you may also provide a reference to the parent record instance (should also be registered as new separately) + * + * @param record A newly created SObject instance to be inserted during commitWork + * @param relatedToParentField A SObjectField reference to the child field that associates the child record with its parent + * @param relatedToParentRecord A SObject instance of the parent record (should also be registered as new separately) + **/ + void registerNew(SObject record, Schema.SObjectField relatedToParentField, SObject relatedToParentRecord); + /** + * Register a relationship between two records that have yet to be inserted to the database. This information will be + * used during the commitWork phase to make the references only when related records have been inserted to the database. + * + * @param record An existing or newly created record + * @param relatedToField A SObjectField reference to the lookup field that relates the two records together + * @param relatedTo A SObject instance (yet to be committed to the database) + */ + void registerRelationship(SObject record, Schema.SObjectField relatedToField, SObject relatedTo); + /** + * Registers a relationship between a record and a Messaging.Email where the record has yet to be inserted + * to the database. This information will be + * used during the commitWork phase to make the references only when related records have been inserted to the database. + * + * @param email a single email message instance + * @param relatedTo A SObject instance (yet to be committed to the database) + */ + void registerRelationship(Messaging.SingleEmailMessage email, SObject relatedTo); + /** + * Registers a relationship between a record and a lookup value using an external ID field and a provided value. This + * information will be used during the commitWork phase to make the lookup reference requested when inserted to the database. + * + * @param record An existing or newly created record + * @param relatedToField A SObjectField reference to the lookup field that relates the two records together + * @param externalIdField A SObjectField reference to a field on the target SObject that is marked as isExternalId + * @param externalId A Object representing the targeted value of the externalIdField in said lookup + * + * Usage Example: uow.registerRelationship(recordSObject, record_sobject__c.relationship_field__c, lookup_sobject__c.external_id__c, 'abc123'); + * + * Wraps putSObject, creating a new instance of the lookup sobject using the external id field and value. + */ + void registerRelationship(SObject record, Schema.SObjectField relatedToField, Schema.SObjectField externalIdField, Object externalId); + /** + * Register an existing record to be updated during the commitWork method + * + * @param record An existing record + **/ + void registerDirty(SObject record); + /** + * Register specific fields on records to be updated when work is committed + * + * If the records are previously registered as dirty, the dirty fields on the records in this call will overwrite + * the values of the previously registered dirty records + * + * @param records A list of existing records + * @param dirtyFields The fields to update if record is already registered + **/ + void registerDirty(List records, List dirtyFields); + /** + * Register specific fields on record to be updated when work is committed + * + * If the record has previously been registered as dirty, the dirty fields on the record in this call will overwrite + * the values of the previously registered dirty record + * + * @param record An existing record + * @param dirtyFields The fields to update if record is already registered + **/ + void registerDirty(SObject record, List dirtyFields); + /** + * Register an existing record to be updated when commitWork is called, + * you may also provide a reference to the parent record instance (should also be registered as new separately) + * + * @param record A newly created SObject instance to be inserted during commitWork + * @param relatedToParentField A SObjectField reference to the child field that associates the child record with its parent + * @param relatedToParentRecord A SObject instance of the parent record (should also be registered as new separately) + **/ + void registerDirty(SObject record, Schema.SObjectField relatedToParentField, SObject relatedToParentRecord); + /** + * Register a list of existing records to be updated during the commitWork method + * + * @param records A list of existing records + **/ + void registerDirty(List records); + /** + * Register an deleted record to be removed from the recycle bin during the commitWork method + * + * @param record An deleted record + **/ + void registerEmptyRecycleBin(SObject record); + /** + * Register deleted records to be removed from the recycle bin during the commitWork method + * + * @param records Deleted records + **/ + void registerEmptyRecycleBin(List records); + /** + * Register a new or existing record to be inserted or updated during the commitWork method + * + * @param record An new or existing record + **/ + void registerUpsert(SObject record); + /** + * Register a list of mix of new and existing records to be upserted during the commitWork method + * + * @param records A list of mix of existing and new records + **/ + void registerUpsert(List records); + /** + * Register an existing record to be deleted during the commitWork method + * + * @param record An existing record + **/ + void registerDeleted(SObject record); + /** + * Register a list of existing records to be deleted during the commitWork method + * + * @param records A list of existing records + **/ + void registerDeleted(List records); + /** + * Register a list of existing records to be deleted and removed from the recycle bin during the commitWork method + * + * @param records A list of existing records + **/ + void registerPermanentlyDeleted(List records); + /** + * Register a list of existing records to be deleted and removed from the recycle bin during the commitWork method + * + * @param record A list of existing records + **/ + void registerPermanentlyDeleted(SObject record); + /** + * Register a newly created SObject (Platform Event) instance to be published when commitWork is called + * + * @param record A newly created SObject (Platform Event) instance to be inserted during commitWork + **/ + void registerPublishBeforeTransaction(SObject record); + /** + * Register a list of newly created SObject (Platform Event) instance to be published when commitWork is called + * + * @param records A list of existing records + **/ + void registerPublishBeforeTransaction(List records); + /** + * Register a newly created SObject (Platform Event) instance to be published when commitWork has successfully + * completed + * + * @param record A newly created SObject (Platform Event) instance to be inserted during commitWork + **/ + void registerPublishAfterSuccessTransaction(SObject record); + /** + * Register a list of newly created SObject (Platform Event) instance to be published when commitWork has successfully + * completed + * + * @param records A list of existing records + **/ + void registerPublishAfterSuccessTransaction(List records); + /** + * Register a newly created SObject (Platform Event) instance to be published when commitWork has caused an error + * + * @param record A newly created SObject (Platform Event) instance to be inserted during commitWork + **/ + void registerPublishAfterFailureTransaction(SObject record); + /** + * Register a list of newly created SObject (Platform Event) instance to be published when commitWork has caused an + * error + * + * @param records A list of existing records + **/ + void registerPublishAfterFailureTransaction(List records); + /** + * Takes all the work that has been registered with the UnitOfWork and commits it to the database + **/ + void commitWork(); + /** + * Register a generic peace of work to be invoked during the commitWork phase + * + * @param work Work to be registered + **/ + void registerWork(fflib_SObjectUnitOfWork.IDoWork work); + /** + * Registers the given email to be sent during the commitWork + * + * @param email Email to be sent + **/ + void registerEmail(Messaging.Email email); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_ISObjects.cls b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjects.cls new file mode 100644 index 00000000000..bf30d9a276e --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjects.cls @@ -0,0 +1,42 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ +public interface fflib_ISObjects extends fflib_IObjects +{ + /** + * @return Return the SObject records contained in the domain + */ + List getRecords(); + + /** + * @return Return the SObject records ids contained in the domain + */ + Set getRecordIds(); + + /** + * @return Return the SObjectType of the SObjects contained in the domain + */ + SObjectType getSObjectType(); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_ISObjects.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjects.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_ISObjects.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_Objects.cls b/force-app/infrastructure/apex-common/main/classes/fflib_Objects.cls new file mode 100644 index 00000000000..f65335edb5e --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_Objects.cls @@ -0,0 +1,108 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ +public virtual class fflib_Objects implements fflib_IObjects +{ + protected List objects { get; private set;} { objects = new List(); } + + /** + * Class constructor + */ + public fflib_Objects(List objects) + { + this.objects = objects.clone(); + } + + public virtual Object getType() + { + return Object.class; + } + + public List getObjects() + { + return this.objects; + } + + public Boolean contains(Object value) + { + return getObjects()?.contains(value); + } + + public Boolean containsAll(List values) + { + if (values == null) return false; + + return containsAll(new Set(values)); + } + + public Boolean containsAll(Set values) + { + if (values == null) return false; + + for (Object value : values) + { + if (!getObjects()?.contains(value)) return false; + } + return true; + } + + public Boolean containsNot(Object value) + { + return !contains(value); + } + + public Boolean containsNot(List values) + { + if (values == null) return true; + + return containsNot(new Set(values)); + } + + public Boolean containsNot(Set values) + { + if (values == null) return true; + + for (Object value : values) + { + if (getObjects()?.contains(value)) return false; + } + return true; + } + + public Boolean isEmpty() + { + return (getObjects() == null || getObjects().isEmpty()); + } + + public Boolean isNotEmpty() + { + return !isEmpty(); + } + + protected void setObjects(List objects) + { + this.objects = objects; + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/fflib_Objects.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_Objects.cls-meta.xml similarity index 80% rename from force-app/main/default/classes/fflib_Objects.cls-meta.xml rename to force-app/infrastructure/apex-common/main/classes/fflib_Objects.cls-meta.xml index d75b0582fba..f928c8e56bc 100644 --- a/force-app/main/default/classes/fflib_Objects.cls-meta.xml +++ b/force-app/infrastructure/apex-common/main/classes/fflib_Objects.cls-meta.xml @@ -1,5 +1,5 @@ - 51.0 + 53.0 Active diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_QueryFactory.cls b/force-app/infrastructure/apex-common/main/classes/fflib_QueryFactory.cls new file mode 100644 index 00000000000..2d2cc425322 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_QueryFactory.cls @@ -0,0 +1,766 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * QueryFactory provides an object-oriented way of building SOQL queries without resorting to string manipulation. + * This class is not meant to be used as a replacement for all SOQL queries, and due to the relatively high overhead in both CPU and describe calls + * should be used in places where highly dynamic queries, such as those that include field sets or are mutated heavily + * in multiple locations are a good fit for use with fflib_QueryFactory. + * + * To use call construct a new instance for each query you intend to make. + * To add additional fields to the query make use of the selectField(s) methods. + * + * Currently the WHERE clause of the query is manipulated as a single string, and is decidedly less OO-styled than other methods. + * This is expected to be expanded upon in the future. + * + * To include one or more ORDER BY clause(s), use one of the addOrdering methods. If not specified, the "NULLS FIRST" keywords + * will be included by default. Constructing Ordering instances manually is discouraged. + * + * Subselect Queries are supported with the subselectQuery methods. + * More than one sub-query can be added to a single query, but sub-queries can only be 1 level deep. + * An exception will thrown from the subselectQuery method when there is an attempt to add a subquery to a sub-query + * or to add a subquery to a query with an invalid relationship. + * + * Current limitations: + * - Aggregate functions are not currently supported. + * - Cross-object references currently require using String argument(s) to selectField(s). + * - The behavior of serializing and deserializing an fflib_QueryFactory instance is currently untested and undefined. + * + * There is a google doc providing additional guidance on the use of this class with field sets at + * https://docs.google.com/a/financialforce.com/document/d/1I4cxN4xHT4UJj_3Oi0YBL_MJ5chm-KG8kMN1D1un8-g/edit?usp=sharing +**/ +public class fflib_QueryFactory { //No explicit sharing declaration - inherit from caller + public enum SortOrder {ASCENDING, DESCENDING} + + /** + * This property is read-only and may not be set after instantiation. + * The {@link Schema.SObjectType} token of the SObject that will be used in the FROM clause of the resultant query. + **/ + public Schema.SObjectType table {get; private set;} + @TestVisible + private Set fields; + private String conditionExpression; + private Integer limitCount; + private Integer offsetCount; + private List order; + /** + * Integrate checking for READ Field Level Security within the selectField(s) methods + * This can optionally be enforced (or not) by calling the setEnforceFLS method prior to calling + * one of the selectField or selectFieldset methods. + **/ + private Boolean enforceFLS; + + private Boolean sortSelectFields = true; + + /** + * The relationship and subselectQueryMap variables are used to support subselect queries. Subselects can be added to + * a query, as long as it isn't a subselect query itself. You may have many subselects inside + * a query, but they may only be 1 level deep (no subselect inside a subselect) + * to add a subselect, call the subselectQuery method, passing in the ChildRelationship. + **/ + private Schema.ChildRelationship relationship; + private Map subselectQueryMap; + + private String getFieldPath(String fieldName){ + if(!fieldName.contains('.')){ //single field + Schema.SObjectField token = fflib_SObjectDescribe.getDescribe(table).getField(fieldName.toLowerCase()); + if(token == null) + throw new InvalidFieldException(fieldName,this.table); + if (enforceFLS) + fflib_SecurityUtils.checkFieldIsReadable(this.table, token); + return token.getDescribe().getName(); + } + + //traversing FK relationship(s) + List fieldPath = new List(); + Schema.SObjectType lastSObjectType = table; + Iterator i = fieldName.split('\\.').iterator(); + while(i.hasNext()){ + String field = i.next(); + Schema.SObjectField token = fflib_SObjectDescribe.getDescribe(lastSObjectType).getField(field.toLowerCase()); + DescribeFieldResult tokenDescribe = token != null ? token.getDescribe() : null; + + if (token != null && enforceFLS) { + fflib_SecurityUtils.checkFieldIsReadable(lastSObjectType, token); + } + + if(token != null && i.hasNext() && tokenDescribe.getSoapType() == Schema.SoapType.ID){ + lastSObjectType = tokenDescribe.getReferenceTo()[0]; //if it's polymorphic doesn't matter which one we get + fieldPath.add(tokenDescribe.getRelationshipName()); + }else if(token != null && !i.hasNext()){ + fieldPath.add(tokenDescribe.getName()); + }else{ + if(token == null) + throw new InvalidFieldException(field,lastSObjectType); + else + throw new NonReferenceFieldException(lastSObjectType+'.'+field+' is not a lookup or master-detail field but is used in a cross-object query field.'); + } + } + + return String.join(fieldPath,'.'); + } + + @TestVisible + private static String getFieldTokenPath(Schema.SObjectField field){ + if(field == null){ + throw new InvalidFieldException('Invalid field: null'); + } + return field.getDescribe().getName(); + } + + /** + * fflib_QueryFactory instances will be considered equal if they produce the same SOQL query. + * A faster comparison will first be attempted to check if they apply to the same table, and contain the same number of fields selected. + * This method will never return true if the provided object is not an instance of fflib_QueryFactory. + * @param obj the object to check equality of. + **/ + public Boolean equals(Object obj){ + if( !(obj instanceof fflib_QueryFactory) || ((fflib_QueryFactory)obj).table != this.table || ((fflib_QueryFactory)obj).fields.size() != this.fields.size() ) + return false; + return ((fflib_QueryFactory)obj).toSOQL() == this.toSOQL(); + } + + /** + * Construct a new fflib_QueryFactory instance with no options other than the FROM clause. + * You *must* call selectField(s) before {@link #toSOQL} will return a valid, runnable query. + * @param table the SObject to be used in the FROM clause of the resultant query. This sets the value of {@link #table}. + **/ + public fflib_QueryFactory(Schema.SObjectType table){ + this.table = table; + fields = new Set(); + order = new List(); + enforceFLS = false; + } + + /** + * Construct a new fflib_QueryFactory instance with no options other than the FROM clause and the relationship. + * This should be used when constructing a subquery query for addition to a parent query. + * Objects created with this constructor cannot be added to another object using the subselectQuery method. + * You *must* call selectField(s) before {@link #toSOQL} will return a valid, runnable query. + * @param relationship the ChildRelationship to be used in the FROM Clause of the resultant Query (when set overrides value of table). This sets the value of {@link #relationship} and {@link #table}. + **/ + private fflib_QueryFactory(Schema.ChildRelationship relationship){ + this(relationship.getChildSObject()); + this.relationship = relationship; + } + + /** + * This method checks to see if the User has Read Access on {@link #table}. + * Asserts true if User has access. + **/ + public fflib_QueryFactory assertIsAccessible(){ + fflib_SecurityUtils.checkObjectIsReadable(table); + return this; + } + + /** + * This method sets a flag to indicate that this query should have FLS Read + * permission enforced. If this method is not called, the default behavior + * is that FLS read permission will not be checked. + * @param enforce whether to enforce field level security (read) + **/ + public fflib_QueryFactory setEnforceFLS(Boolean enforce){ + this.enforceFLS = enforce; + return this; + } + + /** + * Sets a flag to indicate that this query should have ordered + * query fields in the select statement (this at a small cost to performance). + * If you are processing large query sets, you should switch this off. + * @param doSort whether or not select fields should be sorted in the soql statement. + **/ + public fflib_QueryFactory setSortSelectFields(Boolean doSort){ + this.sortSelectFields = doSort; + return this; + } + + /** + * Selects a single field from the SObject specified in {@link #table}. + * Selecting fields is idempotent, if this field is already selected calling this method will have no additional impact. + * @param fieldName the API name of the field to add to the query's SELECT clause. + **/ + public fflib_QueryFactory selectField(String fieldName){ + fields.add( getFieldPath(fieldName) ); + return this; + } + /** + * Selects a field, avoiding the possible ambiguity of String API names. + * @see #selectField(String) + * @param field the {@link Schema.SObjectField} to select with this query. + * @exception InvalidFieldException If the field is null {@code field}. + **/ + public fflib_QueryFactory selectField(Schema.SObjectField field){ + if(field == null) + throw new InvalidFieldException(null,this.table); + if (enforceFLS) + fflib_SecurityUtils.checkFieldIsReadable(table, field); + fields.add( getFieldTokenPath(field) ); + return this; + } + /** + * Selects multiple fields. This acts the same as calling {@link #selectField(String)} multiple times. + * @param fieldNames the Set of field API names to select. + **/ + public fflib_QueryFactory selectFields(Set fieldNames){ + for(String fieldName:fieldNames){ + fields.add( getFieldPath(fieldName) ); + } + return this; + } + /** + * Selects multiple fields. This acts the same as calling {@link #selectField(String)} multiple times. + * @param fieldNames the List of field API names to select. + **/ + public fflib_QueryFactory selectFields(List fieldNames){ + for(String fieldName:fieldNames) + fields.add( getFieldPath(fieldName) ); + return this; + } + /** + * Selects multiple fields. This acts the same as calling {@link #selectField(Schema.SObjectField)} multiple times. + * @param fields the set of {@link Schema.SObjectField}s to select. + * @exception InvalidFieldException if the fields are null {@code fields}. + **/ + public fflib_QueryFactory selectFields(Set fields){ + for(Schema.SObjectField token:fields){ + if(token == null) + throw new InvalidFieldException(); + if (enforceFLS) + fflib_SecurityUtils.checkFieldIsReadable(table, token); + this.fields.add( getFieldTokenPath(token) ); + } + return this; + } + /** + * Selects multiple fields. This acts the same as calling {@link #selectField(Schema.SObjectField)} multiple times. + * @param fields the set of {@link Schema.SObjectField}s to select. + * @exception InvalidFieldException if the fields are null {@code fields}. + **/ + public fflib_QueryFactory selectFields(List fields){ + for(Schema.SObjectField token:fields){ + if(token == null) + throw new InvalidFieldException(); + if (enforceFLS) + fflib_SecurityUtils.checkFieldIsReadable(table, token); + this.fields.add( getFieldTokenPath(token) ); + } + return this; + } + /** + * @see #selectFieldSet(Schema.FieldSet,Boolean) + **/ + public fflib_QueryFactory selectFieldSet(Schema.FieldSet fieldSet){ + return selectFieldSet(fieldSet,true); + } + /** + * This is equivalent to iterating the fields in the field set and calling {@link #selectField(String)} on each. + * @param fieldSet Select all fields included in the field set. + * @param allowCrossObject if false this method will throw an exception if any fields in the field set reference fields on a related record. + * @exception InvalidFieldSetException if the fieldset is invalid for table {@code fields}. + **/ + public fflib_QueryFactory selectFieldSet(Schema.FieldSet fieldSet, Boolean allowCrossObject){ + if(fieldSet.getSObjectType() != table) + throw new InvalidFieldSetException('Field set "'+fieldSet.getName()+'" is not for SObject type "'+table+'"'); + for(Schema.FieldSetMember field: fieldSet.getFields()){ + if(!allowCrossObject && field.getFieldPath().contains('.')) + throw new InvalidFieldSetException('Cross-object fields not allowed and field "'+field.getFieldPath()+'"" is a cross-object field.'); + fields.add( getFieldPath(field.getFieldPath()) ); + } + return this; + } + /** + * @param conditionExpression Sets the WHERE clause to the string provided. Do not include the "WHERE". + **/ + public fflib_QueryFactory setCondition(String conditionExpression){ + this.conditionExpression = conditionExpression; + return this; + } + /** + * @returns the current value of the WHERE clause, if any, as set by {@link #setCondition} + **/ + public String getCondition(){ + return this.conditionExpression; + } + /** + * @param limitCount if not null causes a LIMIT clause to be added to the resulting query. + **/ + public fflib_QueryFactory setLimit(Integer limitCount){ + this.limitCount = limitCount; + return this; + } + /** + * @returns the current value of the LIMIT clause, if any. + **/ + public Integer getLimit(){ + return this.limitCount; + } + /** + * @param offsetCount if not null causes a OFFSET clause to be added to the resulting query. + **/ + public fflib_QueryFactory setOffset(Integer offsetCount){ + this.offsetCount = offsetCount; + return this; + } + /** + * @returns the current value of the OFFSET clause, if any. + **/ + public Integer getOffset(){ + return this.offsetCount; + } + /** + * @param o an instance of {@link fflib_QueryFactory.Ordering} to be added to the query's ORDER BY clause. + **/ + public fflib_QueryFactory addOrdering(Ordering o){ + this.order.add(o); + return this; + } + + /** + * @param o an instance of {@link fflib_QueryFactory.Ordering} to remove all existing (for instance defaults) and be added to the query's ORDER BY clause. + **/ + public fflib_QueryFactory setOrdering(Ordering o){ + this.order = new List{ o }; + return this; + } + /** + * @returns the list of orderings that will be used as the query's ORDER BY clause. You may remove elements from the returned list, or otherwise mutate it, to remove previously added orderings. + **/ + public List getOrderings(){ + return this.order; + } + + /** + * @returns the selected fields + **/ + public Set getSelectedFields() { + return this.fields; + } + + /** + * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned. + * If not, a new one will be created and returned. + * @deprecated Replaced by {@link #subselectQuery(String relationshipName)} and {@link #subselectQuery(ChildRelationship relationship)} + * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship + * @param related The related object type + **/ + public fflib_QueryFactory subselectQuery(SObjectType related){ + System.debug(LoggingLevel.WARN, 'fflib_QueryFactory.subselectQuery(Schema.SObjectType) is deprecated and will be removed in a future release. Use fflib_QueryFactory.subselectQuery(String) or fflib_QueryFactory.subselectQuery(ChildRelationship) instead.'); + return setSubselectQuery(getChildRelationship(related), false); + } + + /** + * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned. + * If not, a new one will be created and returned. + * @deprecated Replaced by {@link #subselectQuery(String relationshipName, Boolean assertIsAccessible)} and {@link #subselectQuery(ChildRelationship relationship, Boolean assertIsAccessible)} + * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship + * @param related The related object type + * @param assertIsAccessible indicates whether to check if the user has access to the subquery object + **/ + public fflib_QueryFactory subselectQuery(SObjectType related, Boolean assertIsAccessible){ + System.debug(LoggingLevel.WARN, 'fflib_QueryFactory.subselectQuery(Schema.SObjectType, Boolean) is deprecated and will be removed in a future release. Use fflib_QueryFactory.subselectQuery(String, Boolean) or fflib_QueryFactory.subselectQuery(ChildRelationship, Boolean) instead.'); + return setSubselectQuery(getChildRelationship(related), assertIsAccessible); + } + + /** + * Add a subquery query to this query. If a subquery for this relationshipName already exists, it will be returned. + * If not, a new one will be created and returned. + * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship + * @param relationshipName The relationshipName to be added as a subquery + **/ + public fflib_QueryFactory subselectQuery(String relationshipName){ + return subselectQuery(relationshipName, false); + } + + /** + * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned. + * If not, a new one will be created and returned. + * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship + * @param relationshipName The relationshipName to be added as a subquery + * @param assertIsAccessible indicates whether to check if the user has access to the subquery object + **/ + public fflib_QueryFactory subselectQuery(String relationshipName, Boolean assertIsAccessible){ + Schema.ChildRelationship relationship = getChildRelationship(relationshipName); + if (relationship != null) { + return setSubselectQuery(relationship, assertIsAccessible); + } + throw new InvalidSubqueryRelationshipException('Invalid call to subselectQuery with relationshipName = '+relationshipName +'. Relationship does not exist for ' + table.getDescribe().getName()); + } + + /** + * Add a subquery query to this query. If a subquery for this relationshipName already exists, it will be returned. + * If not, a new one will be created and returned. + * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship + * @param relationship The ChildRelationship to be added as a subquery + **/ + public fflib_QueryFactory subselectQuery(Schema.ChildRelationship relationship){ + return subselectQuery(relationship, false); + } + + /** + * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned. + * If not, a new one will be created and returned. + * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship + * @param relationship The ChildRelationship to be added as a subquery + * @param assertIsAccessible indicates whether to check if the user has access to the subquery object + **/ + public fflib_QueryFactory subselectQuery(Schema.ChildRelationship relationship, Boolean assertIsAccessible){ + return setSubselectQuery(relationship, assertIsAccessible); + } + + /** + * Add a subquery query to this query. If a subquery for this relationship already exists, it will be returned. + * If not, a new one will be created and returned. + * @exception InvalidSubqueryRelationshipException If this method is called on a subselectQuery or with an invalid relationship + * @param relationship The ChildRelationship to be added as a subquery + **/ + private fflib_QueryFactory setSubselectQuery(Schema.ChildRelationship relationship, Boolean assertIsAccessible){ + if (this.relationship != null){ + throw new InvalidSubqueryRelationshipException('Invalid call to subselectQuery. You may not add a subselect query to a subselect query.'); + } + if (this.subselectQueryMap == null){ + this.subselectQueryMap = new Map(); + } + if (this.subselectQueryMap.containsKey(relationship)){ + return subselectQueryMap.get(relationship); + } + + fflib_QueryFactory subSelectQuery = new fflib_QueryFactory(relationship); + + //The child queryFactory should be configured in the same way as the parent by default - can override after if required + subSelectQuery.setSortSelectFields(sortSelectFields); + + if(assertIsAccessible){ + subSelectQuery.assertIsAccessible(); + } + subselectQueryMap.put(relationship, subSelectQuery); + return subSelectQuery; + } + + /** + * @returns the list of subquery instances of fflib_QueryFactory which will be added to the SOQL as relationship/child/sub-queries. + **/ + public List getSubselectQueries(){ + if (subselectQueryMap != null) { + return subselectQueryMap.values(); + } + return null; + } + + /** + * Get the ChildRelationship from the Table for the object type passed in. + * @param objType The object type of the child relationship to get + **/ + private Schema.ChildRelationship getChildRelationship(SObjectType objType){ + for (Schema.ChildRelationship childRow : table.getDescribe().getChildRelationships()){ + //occasionally on some standard objects (Like Contact child of Contact) do not have a relationship name. + //if there is no relationship name, we cannot query on it, so throw an exception. + if (childRow.getChildSObject() == objType && childRow.getRelationshipName() != null){ + return childRow; + } + } + throw new InvalidSubqueryRelationshipException('Invalid call to subselectQuery. Invalid relationship for table '+table + ' and objtype='+objType); + } + + /** + * Get the ChildRelationship from the Table for the relationship name passed in. + * @param relationshipName The name of the object's ChildRelationship on get + **/ + private Schema.ChildRelationship getChildRelationship(String relationshipName){ + for (Schema.ChildRelationship childRow : table.getDescribe().getChildRelationships()){ + if (childRow.getRelationshipName() == relationshipName){ + return childRow; + } + } + return null; + } + + /** + * Add a field to be sorted on. This may be a direct field or a field + * related through an object lookup or master-detail relationship. + * Use the set to store unique field names, since we only want to sort + * by the same field one time. The sort expressions are stored in a list + * so that they are applied to the SOQL in the same order that they + * were added in. + * @param fieldName The string value of the field to be sorted on + * @param direction the direction to be sorted on (ASCENDING or DESCENDING) + * @param nullsLast whether to sort null values last (NULLS LAST keyword included). + **/ + public fflib_QueryFactory addOrdering(String fieldName, SortOrder direction, Boolean nullsLast){ + order.add( + new Ordering(getFieldPath(fieldName), direction, nullsLast) + ); + return this; + } + + /** + * Add a field to be sorted on. This may be a direct field or a field + * related through an object lookup or master-detail relationship. + * Use the set to store unique field names, since we only want to sort + * by the same field one time. The sort expressions are stored in a list + * so that they are applied to the SOQL in the same order that they + * were added in. + * @param field The SObjectField to sort. This can only be a direct reference. + * @param direction the direction to be sorted on (ASCENDING or DESCENDING) + * @param nullsLast whether to sort null values last (NULLS LAST keyword included). + **/ + public fflib_QueryFactory addOrdering(SObjectField field, SortOrder direction, Boolean nullsLast){ + order.add( + new Ordering(getFieldTokenPath(field), direction, nullsLast) + ); + return this; + } + + /** + * Add a field to be sorted on. This may be a direct field or a field + * related through an object lookup or master-detail relationship. + * Use the set to store unique field names, since we only want to sort + * by the same field one time. The sort expressions are stored in a list + * so that they are applied to the SOQL in the same order that they + * were added in. + * The "NULLS FIRST" keywords will be included by default. If "NULLS LAST" + * is required, use one of the overloaded addOrdering methods which include this parameter. + * @param fieldName The string value of the field to be sorted on + * @param direction the direction to be sorted on (ASCENDING or DESCENDING) + **/ + public fflib_QueryFactory addOrdering(String fieldName, SortOrder direction){ + order.add( + new Ordering(getFieldPath(fieldName), direction) + ); + return this; + } + + /** + * Add a field to be sorted on. This may be a direct field or a field + * related through an object lookup or master-detail relationship. + * Use the set to store unique field names, since we only want to sort + * by the same field one time. The sort expressions are stored in a list + * so that they are applied to the SOQL in the same order that they + * were added in. + * The "NULLS FIRST" keywords will be included by default. If "NULLS LAST" + * is required, use one of the overloaded addOrdering methods which include this parameter. + * @param field The SObjectField to sort. This can only be a direct reference. + * @param direction the direction to be sorted on (ASCENDING or DESCENDING) + **/ + public fflib_QueryFactory addOrdering(SObjectField field, SortOrder direction){ + order.add( + new Ordering(getFieldTokenPath(field), direction) + ); + return this; + } + + /** + * Remove existing ordering and set a field to be sorted on. This may be a direct field or a field + * related through an object lookup or master-detail relationship. + * Use the set to store unique field names, since we only want to sort + * by the same field one time. The sort expressions are stored in a list + * so that they are applied to the SOQL in the same order that they + * were added in. + * @param fieldName The string value of the field to be sorted on + * @param direction the direction to be sorted on (ASCENDING or DESCENDING) + * @param nullsLast whether to sort null values last (NULLS LAST keyword included). + **/ + public fflib_QueryFactory setOrdering(String fieldName, SortOrder direction, Boolean nullsLast){ + Ordering ordr = new Ordering(getFieldPath(fieldName), direction, nullsLast); + return setOrdering(ordr); + } + + /** + * Remove existing ordering and set a field to be sorted on. This may be a direct field or a field + * related through an object lookup or master-detail relationship. + * Use the set to store unique field names, since we only want to sort + * by the same field one time. The sort expressions are stored in a list + * so that they are applied to the SOQL in the same order that they + * were added in. + * @param field The SObjectField to sort. This can only be a direct reference. + * @param direction the direction to be sorted on (ASCENDING or DESCENDING) + * @param nullsLast whether to sort null values last (NULLS LAST keyword included). + **/ + public fflib_QueryFactory setOrdering(SObjectField field, SortOrder direction, Boolean nullsLast){ + Ordering ordr = new Ordering(getFieldTokenPath(field), direction, nullsLast); + return setOrdering(ordr); + } + + /** + * Remove existing ordering and set a field to be sorted on. This may be a direct field or a field + * related through an object lookup or master-detail relationship. + * Use the set to store unique field names, since we only want to sort + * by the same field one time. The sort expressions are stored in a list + * so that they are applied to the SOQL in the same order that they + * were added in. + * @param fieldName The string value of the field to be sorted on + * @param direction the direction to be sorted on (ASCENDING or DESCENDING) + **/ + public fflib_QueryFactory setOrdering(String fieldName, SortOrder direction){ + Ordering ordr = new Ordering(getFieldPath(fieldName), direction); + return setOrdering(ordr); + } + + /** + * Remove existing ordering and set a field to be sorted on. This may be a direct field or a field + * related through an object lookup or master-detail relationship. + * Use the set to store unique field names, since we only want to sort + * by the same field one time. The sort expressions are stored in a list + * so that they are applied to the SOQL in the same order that they + * were added in. + * @param field The SObjectField to sort. This can only be a direct reference. + * @param direction the direction to be sorted on (ASCENDING or DESCENDING) + **/ + public fflib_QueryFactory setOrdering(SObjectField field, SortOrder direction){ + Ordering ordr = new Ordering(getFieldTokenPath(field), direction); + return setOrdering(ordr); + } + + /** + * Convert the values provided to this instance into a full SOQL string for use with Database.query + * Check to see if subqueries queries need to be added after the field list. + **/ + public String toSOQL(){ + String result = 'SELECT '; + //if no fields have been added, just add the Id field so that the query or subquery will not just fail + if (fields.size() == 0){ + if (enforceFLS) fflib_SecurityUtils.checkFieldIsReadable(table, 'Id'); + result += 'Id'; + }else { + List fieldsToQuery = new List(fields); + + if(sortSelectFields){ + fieldsToQuery.sort(); + } + + result += String.join(fieldsToQuery,', '); + } + + if(subselectQueryMap != null && !subselectQueryMap.isEmpty()){ + for (fflib_QueryFactory childRow : subselectQueryMap.values()){ + result += ', (' + childRow.toSOQL() + ') '; + } + } + result += ' FROM ' + (relationship != null ? relationship.getRelationshipName() : table.getDescribe().getName()); + if(conditionExpression != null) + result += ' WHERE '+conditionExpression; + + if(order.size() > 0){ + result += ' ORDER BY '; + for(Ordering o:order) + result += o.toSOQL() +', '; + result = result.substring(0,result.length()-2); + } + + if(limitCount != null) + result += ' LIMIT '+limitCount; + + if(offsetCount != null) + result += ' OFFSET '+offsetCount; + + return result; + } + + /** + * Create a "deep" clone of this object that can be safely mutated without affecting the cloned instance + * @return a deep clone of this fflib_QueryFactory + **/ + public fflib_QueryFactory deepClone(){ + + fflib_QueryFactory clone = new fflib_QueryFactory(this.table) + .setLimit(this.limitCount) + .setOffset(this.offsetCount) + .setCondition(this.conditionExpression) + .setEnforceFLS(this.enforceFLS); + + Map subqueries = this.subselectQueryMap; + if(subqueries != null) { + Map clonedSubqueries = new Map(); + for(Schema.ChildRelationship key : subqueries.keySet()) { + clonedSubqueries.put(key, subqueries.get(key).deepClone()); + } + clone.subselectQueryMap = clonedSubqueries; + } + + clone.relationship = this.relationship; + clone.order = this.order.clone(); + clone.fields = this.fields.clone(); + + return clone; + } + + public class Ordering{ + private SortOrder direction; + private boolean nullsLast; + private String field; + + public Ordering(String sobjType, String fieldName, SortOrder direction){ + this( + fflib_SObjectDescribe.getDescribe(sobjType).getField(fieldName), + direction + ); + } + /** + * Construct a new ordering instance for use with {@link fflib_QueryFactory#addOrdering} + * Once constructed it's properties may not be modified. + **/ + public Ordering(Schema.SObjectField field, SortOrder direction){ + this(fflib_QueryFactory.getFieldTokenPath(field), direction, false); //SOQL docs state NULLS FIRST is default behavior + } + public Ordering(Schema.SObjectField field, SortOrder direction, Boolean nullsLast){ + this(fflib_QueryFactory.getFieldTokenPath(field), direction, nullsLast); + } + @TestVisible + private Ordering(String field, SortOrder direction){ + this(field, direction, false); + } + @TestVisible + private Ordering(String field, SortOrder direction, Boolean nullsLast){ + this.direction = direction; + this.field = field; + this.nullsLast = nullsLast; + } + public String getField(){ + return this.field; + } + public SortOrder getDirection(){ + return direction; + } + public String toSOQL(){ + return field + ' ' + (direction == SortOrder.ASCENDING ? 'ASC' : 'DESC') + (nullsLast ? ' NULLS LAST ' : ' NULLS FIRST '); + } + } + + + + public class InvalidFieldException extends Exception{ + private String fieldName; + private Schema.SObjectType objectType; + public InvalidFieldException(String fieldName, Schema.SObjectType objectType){ + this.objectType = objectType; + this.fieldName = fieldName; + this.setMessage( 'Invalid field \''+fieldName+'\' for object \''+objectType+'\'' ); + } + } + public class InvalidFieldSetException extends Exception{} + public class NonReferenceFieldException extends Exception{} + public class InvalidSubqueryRelationshipException extends Exception{} +} diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_QueryFactory.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_QueryFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_QueryFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDescribe.cls b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDescribe.cls new file mode 100644 index 00000000000..ac1575a5e57 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDescribe.cls @@ -0,0 +1,357 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * fflib_SObjectDescribe is a semi-intelligent wrapper for standard apex Schema methods. + * It provides an internal caching layer, to avoid hitting describe limits from repeated use, + * as well as wrapper classes and methods to make common tasks like working with relationship field name oddities + * as well namespace handling. + * + * Of particular note for use in contexts that may be released as managed packages are the #getFields and get #getGlobalDescribe methods + * These return special immutable wrapper objects that automatically imply the current namespace (detected as the one this class is contained in) + * and allow an older API style of omitting the namespace when working with fields or global describe maps. + * This allows both upgrading old code to APIv29 by making use of these as a nearly drop in replacement, as well as keeping + * namespace detection logic encapsulated. +**/ +public class fflib_SObjectDescribe { + //internal implementation details + private Schema.SObjectType token; + private Schema.SObjectField nameField; + private Schema.DescribeSObjectResult describe { //lazy load - keep this lightweight until we need more data + get{ + if(describe == null) + describe = token.getDescribe(); + return describe; + } + set; + } + private Map fields { + get{ + if(fields == null) + fields = describe.fields.getMap(); + return fields; + } + set; + } + private Map fieldSets { + get{ + if(fieldSets == null) + fieldSets = describe.fieldSets.getMap(); + return fieldSets; + } + set; + } + private FieldsMap wrappedFields { + get{ + if(wrappedFields == null){ + wrappedFields = new FieldsMap(this.fields); + } + return wrappedFields; + } + set; + } + + private fflib_SObjectDescribe(Schema.SObjectType token){ + if(token == null) + throw new InvalidDescribeException('Invalid SObject type: null'); + this.token = token; + instanceCache.put( String.valueOf(token).toLowerCase() , this); + } + + //public instace methods + /** + * Returns the Schema.SObjectType this fflib_SObjectDescribe instance is based on. + **/ + public Schema.SObjectType getSObjectType(){ + return token; + } + /** + * This method is a convenient shorthand for calling getField(name, true) + **/ + public Schema.SObjectField getField(String name){ + return this.getField(name, true); + } + /** + * This method provides a simplified shorthand for calling #getFields and getting the provided field. + * Additionally it handles finding the correct SObjectField for relationship notation, + * e.g. getting the Account field on Contact would fail without being referenced as AccountId - both work here. + **/ + public Schema.SObjectField getField(String fieldName, Boolean implyNamespace){ + Schema.SObjectField result = wrappedFields.get( + (fieldName.endsWithIgnoreCase('__r') ? //resolve custom field cross-object (__r) syntax + (fieldName.removeEndIgnoreCase('__r')+'__c') : + fieldName), + implyNamespace + ); + if(result == null){ + result = wrappedFields.get(fieldName+'Id', implyNamespace); //in case it's a standard lookup in cross-object format + } + return result; + } + + /** + * Returns the field where isNameField() is true (if any); otherwise returns null + **/ + public Schema.SObjectField getNameField() + { + if(nameField == null) { + for(Schema.SObjectField field : wrappedFields.values()) { + if(field.getDescribe().isNameField()) { + nameField = field; + break; + } + } + } + return nameField; + } + + /** + * Returns the raw Schema.DescribeSObjectResult an fflib_SObjectDescribe instance wraps. + **/ + public Schema.DescribeSObjectResult getDescribe(){ + return describe; + } + /** + * This method returns the raw data and provides no namespace handling. + * Due to this, __use of this method is discouraged__ in favor of getFields(). + **/ + public Map getFieldsMap(){ + return fields; + } + public FieldsMap getFields(){ + return wrappedFields; + } + public Map getFieldSetsMap(){ + return fieldSets; + } + + + + private static Map rawGlobalDescribe { + get{ + if(rawGlobalDescribe == null) + rawGlobalDescribe = Schema.getGlobalDescribe(); + return rawGlobalDescribe; + } + set; + } + private static GlobalDescribeMap wrappedGlobalDescribe{ + get{ + if(wrappedGlobalDescribe == null){ + wrappedGlobalDescribe = new GlobalDescribeMap(rawGlobalDescribe); + } + return wrappedGlobalDescribe; + } + set; + } + /** + * This is used to cache fflib_SObjectDescribe instances as they're constructed + * to prevent repeatedly re-constructing the same type. + * These instances are not guaranteed to be, but typically will be, unique per sObject type due to the presence of flushCache. + **/ + private static Map instanceCache {get{ + if(instanceCache == null) + instanceCache = new Map(); + return instanceCache; + } + set; + } + public static fflib_SObjectDescribe getDescribe(String sObjectName){ + if(String.isBlank(sObjectName)) + return null; + fflib_SObjectDescribe result = instanceCache.get(sObjectName.toLowerCase()); + if(result == null){ + Schema.SObjectType token = wrappedGlobalDescribe.get(sObjectName.toLowerCase()); + if(token == null) + result = null; + else + result = new fflib_SObjectDescribe(token); + } + return result; + } + public static fflib_SObjectDescribe getDescribe(Schema.SObjectType token){ + if(token == null) + return null; + fflib_SObjectDescribe result = instanceCache.get(String.valueOf(token).toLowerCase()); + if(result == null) + result = new fflib_SObjectDescribe(token); + return result; + } + public static fflib_SObjectDescribe getDescribe(Schema.DescribeSObjectResult nativeDescribe){ + if(nativeDescribe == null) + return null; + fflib_SObjectDescribe result = instanceCache.get(nativeDescribe.getName().toLowerCase()); + if(result == null) + result = new fflib_SObjectDescribe(nativeDescribe.getSObjectType()); + return result; + } + public static fflib_SObjectDescribe getDescribe(SObject instance){ + if(instance == null) + return null; + return getDescribe(instance.getSObjectType()); + } + + //returns the same results as the native method, just with caching built in to avoid limits + public static Map getRawGlobalDescribe(){ + return rawGlobalDescribe; + } + public static GlobalDescribeMap getGlobalDescribe(){ + return wrappedGlobalDescribe; + } + //Useful when working in heap space constrained environments. + //Existing references to SObjectDescribe instances will continue to work. + public static void flushCache(){ + rawGlobalDescribe = null; + instanceCache = null; + } + + + /** + * This class handles emulating a Map's non-mutating instance methods and helps navigate the complex topic of + * handling implicit namespace behavior like pre-APIv29 did, while also allowing fully qualified references. + * Note that this requires the API version of fflib_SObjectDescribe to be 29 or higher to function properly. + * + * Due to the lack of language support for covariant return types subclasses are responsible for implementing the get methods. + * A minimal implementation of these would be a cast and returning getObject's result. + **/ + private abstract class NamespacedAttributeMap{ + @TestVisible + protected String currentNamespace; + protected Map values; + + protected NamespacedAttributeMap(Map values){ + //namespace detection courtesy http://salesforce.stackexchange.com/a/28977/60 + currentNamespace = fflib_SObjectDescribe.class.getName().substringBefore('fflib_SObjectDescribe').removeEnd('.').toLowerCase(); + this.values = values; + } + //A no-args constructor to allow subclasses with different constructor signatures + protected NamespacedAttributeMap(){ + this(new Map()); + } + /** + * A convenient shortcut for invoking #getObject(name, true) + **/ + protected virtual Object getObject(String name){ + return this.getObject(name, true); + } + /** + * + **/ + protected virtual Object getObject(String name, Boolean implyNamespace){ + if(name == null) //short-circuit lookup logic since null can't possibly be a valid field name, and it saves us null checking + return null; + String preferredValue = ((implyNamespace ? currentNamespace+'__' : '') + name).toLowerCase(); + if(values.containsKey(preferredValue)){ + return values.get(preferredValue); + }else if(implyNamespace){ + return values.get(name.toLowerCase()); + }else{ + return null; + } + } + public virtual Boolean containsKey(String name){ + return this.containsKey(name, true); + } + public virtual Boolean containsKey(String name, Boolean implyNamespace){ + if(name == null) //short-circuit lookup logic since null can't possibly be a valid field name, and it saves us null checking + return null; + String preferredValue = ((implyNamespace ? currentNamespace+'__' : '') + name).toLowerCase(); + return ( + values.containsKey(preferredValue) || + implyNamespace && values.containsKey(name.toLowerCase()) + ); + } + public virtual Integer size(){ + return values.size(); + } + /** + * Returns the key set of the map. + * Note: unlike other NamespacedAttributeMap methods keySet defaults implyNamespace to false if not specified. + **/ + public virtual Set keySet(){ + return this.keySet(false); + } + public virtual Set keySet(Boolean implyNamespace){ + if(implyNamespace){ + Set result = new Set(); + for(String key:values.keySet()){ + result.add( + key.removeStartIgnoreCase(currentNamespace+'__') + ); + } + return result; + }else{ + return values.keySet(); + } + } + } + + /** + * A subclass of NamespacedAttributeMap for handling the data returned by #Schema.DescribeSObjectResult.fields.getMap + **/ + public class FieldsMap extends NamespacedAttributeMap{ + + @TestVisible + private FieldsMap(Map values){ + super(values); + } + + public Schema.SObjectField get(String name){ + return this.get(name, true); + } + public Schema.SObjectField get(String name, Boolean implyNamespace){ + return (Schema.SObjectField) this.getObject(name, implyNamespace); + } + public List values(){ + return (List) values.values(); + } + + } + /** + * A subclass of NamespacedAttributeMap for handling the data returned by #Schema.getGlobalDescribe + **/ + public class GlobalDescribeMap extends NamespacedAttributeMap{ + @TestVisible + private GlobalDescribeMap(Map values){ + super(values); + } + + public Schema.SObjectType get(String name){ + return this.get(name, true); + } + public Schema.SObjectType get(String name, Boolean implyNamespace){ + return (Schema.SObjectType) this.getObject(name, implyNamespace); + } + public List values(){ + return (List) values.values(); + } + } + + + public abstract class DescribeException extends Exception{} + public class DuplicateDescribeException extends DescribeException{} //Test coverage for this requires APIv28's @testVisible annotation to force exception cases. + public class InvalidDescribeException extends DescribeException{} +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDescribe.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDescribe.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDescribe.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDomain.cls b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDomain.cls new file mode 100644 index 00000000000..bd982249d48 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDomain.cls @@ -0,0 +1,1125 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * Base class aiding in the implementation of a Domain Model around SObject collections + * + * Domain (software engineering). “a set of common requirements, terminology, and functionality + * for any software program constructed to solve a problem in that field”, + * http://en.wikipedia.org/wiki/Domain_(software_engineering) + * + * Domain Model, “An object model of the domain that incorporates both behavior and data.”, + * “At its worst business logic can be very complex. Rules and logic describe many different " + * "cases and slants of behavior, and it's this complexity that objects were designed to work with...” + * Martin Fowler, EAA Patterns + * http://martinfowler.com/eaaCatalog/domainModel.html + * + **/ +public virtual with sharing class fflib_SObjectDomain + extends fflib_SObjects + implements fflib_ISObjectDomain +{ + /** + * Provides access to the data represented by this domain class + **/ + public List Records { + get + { + return getRecords(); + } + } + + + /** + * Provides access to Trigger.oldMap and allowing it to be mocked in unit-tests + **/ + @TestVisible + protected Map ExistingRecords + { + get + { + if (ExistingRecords == null) + { + if (System.Test.isRunningTest() & Test.Database.hasRecords()) + { + // If in test context and records are in the mock database use those instead of Trigger.oldMap + ExistingRecords = Test.Database.oldRecords; + } + else + { + ExistingRecords = Trigger.oldMap; + } + } + return ExistingRecords; + + } + private set; + } + + /** + * Exposes the configuration for this domain class instance + **/ + public Configuration Configuration {get; private set;} + + /** + * DEPRECATED, This property has been moved to fflib_SObjects + **/ + public static fflib_SObjectDomain.ErrorFactory Errors {get; private set;} + + /** + * Useful during unit testing to access mock support for database inserts and updates (testing without DML) + **/ + public static TestFactory Test {get; private set;} + + /** + * Retains instances of domain classes implementing trigger stateful + **/ + private static Map> TriggerStateByClass; + + /** + * Retains the trigger tracking configuration used for each domain + **/ + private static Map TriggerEventByClass; + + static + { + Errors = new fflib_SObjectDomain.ErrorFactory(); + + Test = new TestFactory(); + + TriggerStateByClass = new Map>(); + + TriggerEventByClass = new Map(); + } + + /** + * Constructs the domain class with the data on which to apply the behaviour implemented within + * + * @param sObjectList A concrete list (e.g. List vs List) of records + + **/ + public fflib_SObjectDomain(List sObjectList) + { + this(sObjectList, sObjectList.getSObjectType()); + } + + /** + * Constructs the domain class with the data and type on which to apply the behaviour implemented within + * + * @param sObjectList A list (e.g. List, List, etc.) of records + * @param sObjectType The Schema.SObjectType of the records contained in the list + * + * @remark Will support List but all records in the list will be assumed to be of + * the type specified in sObjectType + **/ + public fflib_SObjectDomain(List sObjectList, SObjectType sObjectType) + { + // Ensure the domain class has its own copy of the data + super(sObjectList, sObjectType); + + // Configure the Domain object instance + Configuration = new Configuration(); + } + + /** + * Override this to apply defaults to the records, this is called by the handleBeforeInsert method + **/ + public virtual void onApplyDefaults() { } + + /** + * Override this to apply general validation to be performed during insert or update, called by the handleAfterInsert and handleAfterUpdate methods + **/ + public virtual void onValidate() { } + + /** + * Override this to apply validation to be performed during insert, called by the handleAfterUpdate method + **/ + public virtual void onValidate(Map existingRecords) { } + + /** + * Override this to perform processing during the before insert phase, this is called by the handleBeforeInsert method + **/ + public virtual void onBeforeInsert() { } + + /** + * Override this to perform processing during the before update phase, this is called by the handleBeforeUpdate method + **/ + public virtual void onBeforeUpdate(Map existingRecords) { } + + /** + * Override this to perform processing during the before delete phase, this is called by the handleBeforeDelete method + **/ + public virtual void onBeforeDelete() { } + + /** + * Override this to perform processing during the after insert phase, this is called by the handleAfterInsert method + **/ + public virtual void onAfterInsert() { } + + /** + * Override this to perform processing during the after update phase, this is called by the handleAfterUpdate method + **/ + public virtual void onAfterUpdate(Map existingRecords) { } + + /** + * Override this to perform processing during the after delete phase, this is called by the handleAfterDelete method + **/ + public virtual void onAfterDelete() { } + + /** + * Override this to perform processing during the after undelete phase, this is called by the handleAfterDelete method + **/ + public virtual void onAfterUndelete() { } + + /** + * Base handler for the Apex Trigger event Before Insert, calls the onApplyDefaults method, followed by onBeforeInsert + **/ + public virtual void handleBeforeInsert() + { + onApplyDefaults(); + onBeforeInsert(); + } + + /** + * Base handler for the Apex Trigger event Before Update, calls the onBeforeUpdate method + **/ + public virtual void handleBeforeUpdate(Map existingRecords) + { + onBeforeUpdate(existingRecords); + } + + /** + * Base handler for the Apex Trigger event Before Delete, calls the onBeforeDelete method + **/ + public virtual void handleBeforeDelete() + { + onBeforeDelete(); + } + + /** + * Base handler for the Apex Trigger event After Insert, checks object security and calls the onValidate and onAfterInsert methods + * + * @throws DomainException if the current user context is not able to create records + **/ + public virtual void handleAfterInsert() + { + if(Configuration.EnforcingTriggerCRUDSecurity && !SObjectDescribe.isCreateable()) + throw new DomainException('Permission to create an ' + SObjectDescribe.getName() + ' denied.'); + + onValidate(); + onAfterInsert(); + } + + /** + * Base handler for the Apex Trigger event After Update, checks object security and calls the onValidate, onValidate(Map) and onAfterUpdate methods + * + * @throws DomainException if the current user context is not able to update records + **/ + public virtual void handleAfterUpdate(Map existingRecords) + { + if(Configuration.EnforcingTriggerCRUDSecurity && !SObjectDescribe.isUpdateable()) + throw new DomainException('Permission to update an ' + SObjectDescribe.getName() + ' denied.'); + + if(Configuration.OldOnUpdateValidateBehaviour) + onValidate(); + onValidate(existingRecords); + onAfterUpdate(existingRecords); + } + + /** + * Base handler for the Apex Trigger event After Delete, checks object security and calls the onAfterDelete method + * + * @throws DomainException if the current user context is not able to delete records + **/ + public virtual void handleAfterDelete() + { + if(Configuration.EnforcingTriggerCRUDSecurity && !SObjectDescribe.isDeletable()) + throw new DomainException('Permission to delete an ' + SObjectDescribe.getName() + ' denied.'); + + onAfterDelete(); + } + + /** + * Base handler for the Apex Trigger event After Undelete, checks object security and calls the onAfterUndelete method + * + * @throws DomainException if the current user context is not able to delete records + **/ + public virtual void handleAfterUndelete() + { + if(Configuration.EnforcingTriggerCRUDSecurity && !SObjectDescribe.isUndeletable()) + throw new DomainException('Permission to undelete an ' + SObjectDescribe.getName() + ' denied.'); + + onAfterUndelete(); + } + + /** + * Returns the SObjectType this Domain class represents + **/ + public SObjectType sObjectType() + { + return getSObjectType(); + } + + /** + * Detects whether any values in context records have changed for given fields as strings + * Returns list of SObject records that have changes in the specified fields + **/ + public List getChangedRecords(Set fieldNames) + { + List changedRecords = new List(); + for (SObject newRecord : Records) + { + Id recordId = (Id) newRecord.get('Id'); + if (this.ExistingRecords == null || !this.ExistingRecords.containsKey(recordId)) + { + continue; + } + + SObject oldRecord = this.ExistingRecords.get(recordId); + for (String fieldName : fieldNames) + { + if (oldRecord.get(fieldName) != newRecord.get(fieldName)) + { + changedRecords.add(newRecord); + break; // prevents the records from being added multiple times + } + } + } + return changedRecords; + } + + /** + * Detects whether any values in context records have changed for given fields as tokens + * Returns list of SObject records that have changes in the specified fields + **/ + public List getChangedRecords(Set fieldTokens) + { + List changedRecords = new List(); + for (SObject newRecord : Records) + { + Id recordId = (Id) newRecord.get('Id'); + if (this.ExistingRecords == null || !this.ExistingRecords.containsKey(recordId)) + { + continue; + } + SObject oldRecord = this.ExistingRecords.get(recordId); + for (Schema.SObjectField fieldToken : fieldTokens) + { + if (oldRecord.get(fieldToken) != newRecord.get(fieldToken)) + { + changedRecords.add(newRecord); + break; // prevents the records from being added multiple times + } + } + } + return changedRecords; + } + + /** + * Interface used to aid the triggerHandler in constructing instances of Domain classes + **/ + public interface IConstructable + { + fflib_SObjectDomain construct(List sObjectList); + } + + /** + * Interface used to aid the triggerHandler in constructing instances of Domain classes + **/ + public interface IConstructable2 extends IConstructable + { + fflib_SObjectDomain construct(List sObjectList, SObjectType sObjectType); + } + + /** + * For Domain classes implementing the ITriggerStateful interface returns the instance + * of the domain class being shared between trigger invocations, returns null if + * the Domain class trigger has not yet fired or the given domain class does not implement + * the ITriggerStateful interface. Note this method is sensitive to recursion, meaning + * it will return the applicable domain instance for the level of recursion + **/ + public static fflib_SObjectDomain getTriggerInstance(Type domainClass) + { + List domains = TriggerStateByClass.get(domainClass); + if(domains==null || domains.size()==0) + return null; + return domains[domains.size()-1]; + } + + /** + * Method constructs the given Domain class with the current Trigger context + * before calling the applicable override methods such as beforeInsert, beforeUpdate etc. + **/ + public static void triggerHandler(Type domainClass) + { + // Process the trigger context + if(System.Test.isRunningTest() & Test.Database.hasRecords()) + { + // If in test context and records in the mock database delegate initially to the mock database trigger handler + Test.Database.testTriggerHandler(domainClass); + } + else + { + // Process the runtime Apex Trigger context + triggerHandler(domainClass, + Trigger.isBefore, + Trigger.isAfter, + Trigger.isInsert, + Trigger.isUpdate, + Trigger.isDelete, + Trigger.isUnDelete, + Trigger.new, + Trigger.oldMap); + } + } + + /** + * Calls the applicable override methods such as beforeInsert, beforeUpdate etc. based on a Trigger context + **/ + private static void triggerHandler(Type domainClass, Boolean isBefore, Boolean isAfter, Boolean isInsert, Boolean isUpdate, Boolean isDelete, Boolean isUndelete, List newRecords, Map oldRecordsMap) + { + // After phase of trigger will reuse prior instance of domain class if ITriggerStateful implemented + fflib_SObjectDomain domainObject = isBefore ? null : popTriggerInstance(domainClass, isDelete ? oldRecordsMap.values() : newRecords); + if(domainObject==null) + { + // Construct the domain class constructor class + String domainClassName = domainClass.getName(); + Type constructableClass = domainClassName.endsWith('Constructor') ? Type.forName(domainClassName) : Type.forName(domainClassName+'.Constructor'); + IConstructable domainConstructor = (IConstructable) constructableClass.newInstance(); + + // Construct the domain class with the approprite record set + if(isInsert) domainObject = domainConstructor.construct(newRecords); + else if(isUpdate) domainObject = domainConstructor.construct(newRecords); + else if(isDelete) domainObject = domainConstructor.construct(oldRecordsMap.values()); + else if(isUndelete) domainObject = domainConstructor.construct(newRecords); + + // Should this instance be reused on the next trigger invocation? + if(domainObject.Configuration.TriggerStateEnabled) + // Push this instance onto the stack to be popped during the after phase + pushTriggerInstance(domainClass, domainObject); + } + + // has this event been disabled? + if(!getTriggerEvent(domainClass).isEnabled(isBefore, isAfter, isInsert, isUpdate, isDelete, isUndelete)) + { + return; + } + + // Invoke the applicable handler + if(isBefore) + { + if(isInsert) domainObject.handleBeforeInsert(); + else if(isUpdate) domainObject.handleBeforeUpdate(oldRecordsMap); + else if(isDelete) domainObject.handleBeforeDelete(); + } + else + { + if(isInsert) domainObject.handleAfterInsert(); + else if(isUpdate) domainObject.handleAfterUpdate(oldRecordsMap); + else if(isDelete) domainObject.handleAfterDelete(); + else if(isUndelete) domainObject.handleAfterUndelete(); + } + } + + /** + * Pushes to the stack of domain classes per type a domain object instance + **/ + private static void pushTriggerInstance(Type domainClass, fflib_SObjectDomain domain) + { + List domains = TriggerStateByClass.get(domainClass); + if(domains==null) + TriggerStateByClass.put(domainClass, domains = new List()); + domains.add(domain); + } + + /** + * Pops from the stack of domain classes per type a domain object instance and updates the record set + **/ + private static fflib_SObjectDomain popTriggerInstance(Type domainClass, List records) + { + List domains = TriggerStateByClass.get(domainClass); + if(domains==null || domains.size()==0) + return null; + fflib_SObjectDomain domain = domains.remove(domains.size()-1); + + domain.setObjects(records); + return domain; + } + + public static TriggerEvent getTriggerEvent(Type domainClass) + { + if(!TriggerEventByClass.containsKey(domainClass)) + { + TriggerEventByClass.put(domainClass, new TriggerEvent()); + } + + return TriggerEventByClass.get(domainClass); + } + + public class TriggerEvent + { + public boolean BeforeInsertEnabled {get; private set;} + public boolean BeforeUpdateEnabled {get; private set;} + public boolean BeforeDeleteEnabled {get; private set;} + + public boolean AfterInsertEnabled {get; private set;} + public boolean AfterUpdateEnabled {get; private set;} + public boolean AfterDeleteEnabled {get; private set;} + public boolean AfterUndeleteEnabled {get; private set;} + + public TriggerEvent() + { + this.enableAll(); + } + + // befores + public TriggerEvent enableBeforeInsert() {BeforeInsertEnabled = true; return this;} + public TriggerEvent enableBeforeUpdate() {BeforeUpdateEnabled = true; return this;} + public TriggerEvent enableBeforeDelete() {BeforeDeleteEnabled = true; return this;} + + public TriggerEvent disableBeforeInsert() {BeforeInsertEnabled = false; return this;} + public TriggerEvent disableBeforeUpdate() {BeforeUpdateEnabled = false; return this;} + public TriggerEvent disableBeforeDelete() {BeforeDeleteEnabled = false; return this;} + + // afters + public TriggerEvent enableAfterInsert() {AfterInsertEnabled = true; return this;} + public TriggerEvent enableAfterUpdate() {AfterUpdateEnabled = true; return this;} + public TriggerEvent enableAfterDelete() {AfterDeleteEnabled = true; return this;} + public TriggerEvent enableAfterUndelete() {AfterUndeleteEnabled = true; return this;} + + + public TriggerEvent disableAfterInsert() {AfterInsertEnabled = false; return this;} + public TriggerEvent disableAfterUpdate() {AfterUpdateEnabled = false; return this;} + public TriggerEvent disableAfterDelete() {AfterDeleteEnabled = false; return this;} + public TriggerEvent disableAfterUndelete(){AfterUndeleteEnabled = false; return this;} + + public TriggerEvent enableAll() + { + return this.enableAllBefore().enableAllAfter(); + } + + public TriggerEvent disableAll() + { + return this.disableAllBefore().disableAllAfter(); + } + + public TriggerEvent enableAllBefore() + { + return this.enableBeforeInsert().enableBeforeUpdate().enableBeforeDelete(); + } + + public TriggerEvent disableAllBefore() + { + return this.disableBeforeInsert().disableBeforeUpdate().disableBeforeDelete(); + } + + public TriggerEvent enableAllAfter() + { + return this.enableAfterInsert().enableAfterUpdate().enableAfterDelete().enableAfterUndelete(); + } + + public TriggerEvent disableAllAfter() + { + return this.disableAfterInsert().disableAfterUpdate().disableAfterDelete().disableAfterUndelete(); + } + + public boolean isEnabled(Boolean isBefore, Boolean isAfter, Boolean isInsert, Boolean isUpdate, Boolean isDelete, Boolean isUndelete) + { + if(isBefore) + { + if(isInsert) return BeforeInsertEnabled; + else if(isUpdate) return BeforeUpdateEnabled; + else if(isDelete) return BeforeDeleteEnabled; + } + else if(isAfter) + { + if(isInsert) return AfterInsertEnabled; + else if(isUpdate) return AfterUpdateEnabled; + else if(isDelete) return AfterDeleteEnabled; + else if(isUndelete) return AfterUndeleteEnabled; + } + return true; // shouldnt ever get here! + } + } + + /** + * Fluent style Configuration system for Domain class creation + **/ + public class Configuration + { + /** + * Backwards compatibility mode for handleAfterUpdate routing to onValidate() + **/ + public Boolean OldOnUpdateValidateBehaviour {get; private set;} + /** + * True if the base class is checking the users CRUD requirements before invoking trigger methods + **/ + public Boolean EnforcingTriggerCRUDSecurity {get; private set;} + + /** + * Enables reuse of the same Domain instance between before and after trigger phases (subject to recursive scenarios) + **/ + public Boolean TriggerStateEnabled {get; private set;} + + /** + * Default configuration + **/ + public Configuration() + { + EnforcingTriggerCRUDSecurity = true; // Default is true for backwards compatability + TriggerStateEnabled = false; + OldOnUpdateValidateBehaviour = false; // Breaking change, but felt to better practice + } + + /** + * See associated property + **/ + public Configuration enableTriggerState() + { + TriggerStateEnabled = true; + return this; + } + + /** + * See associated property + **/ + public Configuration disableTriggerState() + { + TriggerStateEnabled = false; + return this; + } + + /** + * See associated property + **/ + public Configuration enforceTriggerCRUDSecurity() + { + EnforcingTriggerCRUDSecurity = true; + return this; + } + + /** + * See associated property + **/ + public Configuration disableTriggerCRUDSecurity() + { + EnforcingTriggerCRUDSecurity = false; + return this; + } + + /** + * See associated property + **/ + public Configuration enableOldOnUpdateValidateBehaviour() + { + OldOnUpdateValidateBehaviour = true; + return this; + } + + /** + * See associated property + **/ + public Configuration disableOldOnUpdateValidateBehaviour() + { + OldOnUpdateValidateBehaviour = false; + return this; + } + } + + /** + * General exception class for the domain layer + **/ + public class DomainException extends Exception + { + } + + /** + * Ensures logging of errors in the Domain context for later assertions in tests + **/ + public override String error(String message, SObject record) + { + return fflib_SObjectDomain.Errors.error(this, message, record); + } + + /** + * Ensures logging of errors in the Domain context for later assertions in tests + **/ + public override String error(String message, SObject record, SObjectField field) + { + return fflib_SObjectDomain.Errors.error(this, message, record, field); + } + + /** + * DEPRECATED, This class has been moved to fflib_SObjects + **/ + public class ErrorFactory + { + private List errorList = new List(); + + private ErrorFactory() + { + + } + + public String error(String message, SObject record) + { + return error(null, message, record); + } + + private String error(fflib_SObjectDomain domain, String message, SObject record) + { + ObjectError objectError = new ObjectError(); + objectError.domain = domain; + objectError.message = message; + objectError.record = record; + errorList.add(objectError); + return message; + } + + public String error(String message, SObject record, SObjectField field) + { + return error(null, message, record, field); + } + + private String error(fflib_SObjectDomain domain, String message, SObject record, SObjectField field) + { + FieldError fieldError = new FieldError(); + fieldError.domain = domain; + fieldError.message = message; + fieldError.record = record; + fieldError.field = field; + errorList.add(fieldError); + return message; + } + + public List getAll() + { + return errorList.clone(); + } + + public void clearAll() + { + errorList.clear(); + } + } + + /** + * DEPRECATED, This class has been moved to fflib_SObjects + **/ + public virtual class FieldError extends ObjectError + { + public SObjectField field; + + public FieldError() + { + + } + } + + /** + * DEPRECATED, This class has been moved to fflib_SObjects + **/ + public virtual class ObjectError extends Error + { + public SObject record; + + public ObjectError() + { + + } + } + + /** + * DEPRECATED, This class has been moved to fflib_SObjects + **/ + public abstract class Error + { + public String message; + public fflib_SObjectDomain domain; + } + + /** + * Provides test context mocking facilities to unit tests testing domain classes + **/ + public class TestFactory + { + public MockDatabase Database = new MockDatabase(); + + private TestFactory() + { + + } + } + + /** + * Class used during Unit testing of Domain classes, can be used (not exclusively) to speed up test execution and focus testing + **/ + public class MockDatabase + { + private Boolean isInsert = false; + private Boolean isUpdate = false; + private Boolean isDelete = false; + private Boolean isUndelete = false; + private List records = new List(); + private Map oldRecords = new Map(); + + private MockDatabase() + { + + } + + private void testTriggerHandler(Type domainClass) + { + // Mock Before + triggerHandler(domainClass, true, false, isInsert, isUpdate, isDelete, isUndelete, records, oldRecords); + + // Mock After + triggerHandler(domainClass, false, true, isInsert, isUpdate, isDelete, isUndelete, records, oldRecords); + } + + public void onInsert(List records) + { + this.isInsert = true; + this.isUpdate = false; + this.isDelete = false; + this.isUndelete = false; + this.records = records; + } + + public void onUpdate(List records, Map oldRecords) + { + this.isInsert = false; + this.isUpdate = true; + this.isDelete = false; + this.records = records; + this.isUndelete = false; + this.oldRecords = oldRecords; + } + + public void onDelete(Map records) + { + this.isInsert = false; + this.isUpdate = false; + this.isDelete = true; + this.isUndelete = false; + this.oldRecords = records; + } + + public void onUndelete(List records) + { + this.isInsert = false; + this.isUpdate = false; + this.isDelete = false; + this.isUndelete = true; + this.records = records; + } + + public Boolean hasRecords() + { + return records!=null && records.size()>0 || oldRecords!=null && oldRecords.size()>0; + } + } + + /** + * Test domain class (ideally this would be in the test class, however Type.newInstance does not see such classes) + **/ + public with sharing class TestSObjectDomain extends fflib_SObjectDomain + { + private String someState; + + public TestSObjectDomain(List sObjectList) + { + // Domain classes are initialised with lists to enforce bulkification throughout + super(sObjectList); + } + + public TestSObjectDomain(List sObjectList, SObjectType sObjectType) + { + // Domain classes are initialised with lists to enforce bulkification throughout + super(sObjectList, sObjectType); + } + + public override void onApplyDefaults() + { + // Not required in production code + super.onApplyDefaults(); + + // Apply defaults to Testfflib_SObjectDomain + for(Opportunity opportunity : (List) Records) + { + opportunity.CloseDate = System.today().addDays(30); + } + } + + public override void onValidate() + { + // Not required in production code + super.onValidate(); + + // Validate Testfflib_SObjectDomain + for(Opportunity opp : (List) Records) + { + if(opp.Type!=null && opp.Type.startsWith('Existing') && opp.AccountId == null) + { + opp.AccountId.addError( error('You must provide an Account for Opportunities for existing Customers.', opp, Opportunity.AccountId) ); + } + } + } + + public override void onValidate(Map existingRecords) + { + // Not required in production code + super.onValidate(existingRecords); + + // Validate changes to Testfflib_SObjectDomain + for(Opportunity opp : (List) Records) + { + Opportunity existingOpp = (Opportunity) existingRecords.get(opp.Id); + if(opp.Type != existingOpp.Type) + { + opp.Type.addError( error('You cannot change the Opportunity type once it has been created.', opp, Opportunity.Type) ); + } + } + } + + public override void onBeforeDelete() + { + // Not required in production code + super.onBeforeDelete(); + + // Validate changes to Testfflib_SObjectDomain + for(Opportunity opp : (List) Records) + { + opp.addError( error('You cannot delete this Opportunity.', opp) ); + } + } + + public override void onAfterUndelete() + { + // Not required in production code + super.onAfterUndelete(); + } + + public override void onBeforeInsert() + { + // Assert this variable is null in the after insert (since this domain class is stateless) + someState = 'This should not survice the trigger after phase'; + } + + public override void onAfterInsert() + { + // This is a stateless domain class, so should not retain anything betweet before and after + System.assertEquals(null, someState); + } + } + + /** + * Typically an inner class to the domain class, supported here for test purposes + **/ + public class TestSObjectDomainConstructor implements fflib_SObjectDomain.IConstructable + { + public fflib_SObjectDomain construct(List sObjectList) + { + return new TestSObjectDomain(sObjectList); + } + } + + /** + * Test domain class (ideally this would be in the test class, however Type.newInstance does not see such classes) + **/ + public with sharing class TestSObjectStatefulDomain + extends fflib_SObjectDomain + { + public String someState; + + public TestSObjectStatefulDomain(List sObjectList) + { + super(sObjectList); + + // Ensure this instance is re-used in the after trigger phase (subject to recursive scenarios) + Configuration.enableTriggerState(); + } + + public override void onBeforeInsert() + { + // This must always be null, as we do not reuse domain instances within recursive scenarios (different record sets) + System.assertEquals(null, someState); + + // Process records + List newOpps = new List(); + for(Opportunity opp : (List) Records) + { + // Set some state sensitive to the incoming records + someState = 'Error on Record ' + opp.Name; + + // Create a new Opportunity record to trigger recursive code path? + if(opp.Name.equals('Test Recursive 1')) + newOpps.add(new Opportunity ( Name = 'Test Recursive 2', Type = 'Existing Account' )); + } + + // If testing recursiving emulate an insert + if(newOpps.size()>0) + { + // This will force recursion and thus validate via the above assert results in a new domain instance + fflib_SObjectDomain.Test.Database.onInsert(newOpps); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectStatefulDomainConstructor.class); + } + } + + public override void onAfterInsert() + { + // Use the state set in the before insert (since this is a stateful domain class) + if(someState!=null) + for(Opportunity opp : (List) Records) + opp.addError(error(someState, opp)); + } + } + + /** + * Typically an inner class to the domain class, supported here for test purposes + **/ + public class TestSObjectStatefulDomainConstructor implements fflib_SObjectDomain.IConstructable + { + public fflib_SObjectDomain construct(List sObjectList) + { + return new TestSObjectStatefulDomain(sObjectList); + } + } + + /** + * Test domain class (ideally this would be in the test class, however Type.newInstance does not see such classes) + **/ + public with sharing class TestSObjectOnValidateBehaviour + extends fflib_SObjectDomain + { + public TestSObjectOnValidateBehaviour(List sObjectList) + { + super(sObjectList); + + // Enable old behaviour based on the test Opportunity name passed in + if(sObjectList[0].Name == 'Test Enable Old Behaviour') + Configuration.enableOldOnUpdateValidateBehaviour(); + } + + public override void onValidate() + { + // Throw exception to give the test somethign to assert on + throw new DomainException('onValidate called'); + } + } + + /** + * Typically an inner class to the domain class, supported here for test purposes + **/ + public class TestSObjectOnValidateBehaviourConstructor implements fflib_SObjectDomain.IConstructable + { + public fflib_SObjectDomain construct(List sObjectList) + { + return new TestSObjectOnValidateBehaviour(sObjectList); + } + } + + /** + * Test domain class (ideally this would be in the test class, however Type.newInstance does not see such classes) + **/ + public with sharing class TestSObjectChangedRecords + extends fflib_SObjectDomain + { + public TestSObjectChangedRecords(List sObjectList) + { + super(sObjectList); + } + } + + /** + * Typically an inner class to the domain class, supported here for test purposes + **/ + public class TestSObjectChangedRecordsConstructor implements fflib_SObjectDomain.IConstructable + { + public fflib_SObjectDomain construct(List sObjectList) + { + return new TestSObjectChangedRecords(sObjectList); + } + } + + /** + * Test domain class (ideally this would be in the test class, however Type.newInstance does not see such classes) + **/ + public with sharing class TestSObjectDisableBehaviour + extends fflib_SObjectDomain + { + public TestSObjectDisableBehaviour(List sObjectList) + { + super(sObjectList); + } + + public override void onAfterInsert() { + // Throw exception to give the test somethign to assert on + throw new DomainException('onAfterInsert called'); + } + + public override void onBeforeInsert() { + // Throw exception to give the test somethign to assert on + throw new DomainException('onBeforeInsert called'); + } + + public override void onAfterUpdate(map existing) { + // Throw exception to give the test somethign to assert on + throw new DomainException('onAfterUpdate called'); + } + + public override void onBeforeUpdate(map existing) { + // Throw exception to give the test somethign to assert on + throw new DomainException('onBeforeUpdate called'); + } + + public override void onAfterDelete() { + // Throw exception to give the test somethign to assert on + throw new DomainException('onAfterDelete called'); + } + + public override void onBeforeDelete() { + // Throw exception to give the test somethign to assert on + throw new DomainException('onBeforeDelete called'); + } + + public override void onAfterUndelete() { + // Throw exception to give the test somethign to assert on + throw new DomainException('onAfterUndelete called'); + } + } + + /** + * Typically an inner class to the domain class, supported here for test purposes + **/ + public class TestSObjectDisableBehaviourConstructor implements fflib_SObjectDomain.IConstructable + { + public fflib_SObjectDomain construct(List sObjectList) + { + return new TestSObjectDisableBehaviour(sObjectList); + } + } +} diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDomain.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDomain.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectDomain.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjectSelector.cls b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectSelector.cls new file mode 100644 index 00000000000..4f981bf2a3a --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectSelector.cls @@ -0,0 +1,492 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * Class providing common database query support for abstracting and encapsulating query logic + **/ +public abstract with sharing class fflib_SObjectSelector + implements fflib_ISObjectSelector +{ + /** + * Indicates whether the sObject has the currency ISO code field for organisations which have multi-currency + * enabled. + **/ + private Boolean CURRENCY_ISO_CODE_ENABLED { + get { + if(CURRENCY_ISO_CODE_ENABLED == null){ + CURRENCY_ISO_CODE_ENABLED = describeWrapper.getFieldsMap().keySet().contains('currencyisocode'); + } + return CURRENCY_ISO_CODE_ENABLED; + } + set; + } + + /** + * Should this selector automatically include the FieldSet fields when building queries? + **/ + private Boolean m_includeFieldSetFields = false; + + /** + * Enforce FLS Security + **/ + private Boolean m_enforceFLS = false; + + /** + * Enforce CRUD Security + **/ + private Boolean m_enforceCRUD = true; + + /** + * Order by field + **/ + private String m_orderBy; + + /** + * Sort the query fields in the select statement (defaults to true, at the expense of performance). + * Switch this off if you need more performant queries. + **/ + private Boolean m_sortSelectFields = true; + + /** + * Describe helper + **/ + private fflib_SObjectDescribe describeWrapper { + get { + if(describeWrapper == null) + describeWrapper = fflib_SObjectDescribe.getDescribe(getSObjectType()); + return describeWrapper; + } + set; + } + /** + * static variables + **/ + private static String DEFAULT_SORT_FIELD = 'CreatedDate'; + private static String SF_ID_FIELD = 'Id'; + + /** + * Implement this method to inform the base class of the SObject (custom or standard) to be queried + **/ + abstract Schema.SObjectType getSObjectType(); + + /** + * Implement this method to inform the base class of the common fields to be queried or listed by the base class methods + **/ + abstract List getSObjectFieldList(); + + /** + * Constructs the Selector with the default settings + **/ + public fflib_SObjectSelector() { } + + /** + * Constructs the Selector + * + * @param includeFieldSetFields Set to true if the Selector queries are to include Fieldset fields as well + **/ + public fflib_SObjectSelector(Boolean includeFieldSetFields) + { + this(includeFieldSetFields, true, false); + } + + /** + * Constructs the Selector + * + * @param includeFieldSetFields Set to true if the Selector queries are to include Fieldset fields as well + **/ + public fflib_SObjectSelector(Boolean includeFieldSetFields, Boolean enforceCRUD, Boolean enforceFLS) + { + this(includeFieldSetFields, enforceCRUD, enforceFLS, true); + } + + /** + * Constructs the Selector + * + * @param includeFieldSetFields Set to true if the Selector queries are to include Fieldset fields as well + * @param enforceCRUD Enforce CRUD security + * @param enforceFLS Enforce Field Level Security + * @param sortSelectFields Set to false if selecting many columns to skip sorting select fields and improve performance + **/ + public fflib_SObjectSelector(Boolean includeFieldSetFields, Boolean enforceCRUD, Boolean enforceFLS, Boolean sortSelectFields) + { + m_includeFieldSetFields = includeFieldSetFields; + m_enforceCRUD = enforceCRUD; + m_enforceFLS = enforceFLS; + m_sortSelectFields = sortSelectFields; + } + + /** + * Override this method to provide a list of Fieldsets that can optionally drive inclusion of additional fields in the base queries + **/ + public virtual List getSObjectFieldSetList() + { + return null; + } + + /** + * Override this method to control the default ordering of records returned by the base queries, + * defaults to the name field of the object if it is not encrypted or CreatedDate if there the object has createdDated or Id + **/ + public virtual String getOrderBy() + { + if (m_orderBy == null) + { + Schema.SObjectField nameField = describeWrapper.getNameField(); + if (nameField != null && !nameField.getDescribe().isEncrypted()) + { + m_orderBy = nameField.getDescribe().getName(); + } + else + { + m_orderBy = DEFAULT_SORT_FIELD; + try { + if (describeWrapper.getField(m_orderBy) == null) + { + m_orderBy = SF_ID_FIELD; + } + } + catch(fflib_QueryFactory.InvalidFieldException ex) { + m_orderBy = SF_ID_FIELD; + } + } + } + return m_orderBy; + } + + + /** + * @description Set the selector to enforce FLS Security + **/ + public fflib_SObjectSelector enforceFLS() + { + m_enforceFLS = true; + return this; + } + + /** + * @description Set the selector to automatically include the FieldSet fields when building queries + **/ + public fflib_SObjectSelector includeFieldSetFields() + { + this.m_includeFieldSetFields = true; + return this; + } + + /** + * @description Set the selector to ignore CRUD security + * @return + */ + public fflib_SObjectSelector ignoreCRUD() + { + this.m_enforceCRUD = false; + return this; + } + + public fflib_SObjectSelector unsortedSelectFields() + { + this.m_sortSelectFields = false; + return this; + } + + /** + * Returns True if this Selector instance has been instructed by the caller to include Field Set fields + **/ + public Boolean isIncludeFieldSetFields() + { + return m_includeFieldSetFields; + } + + /** + * Returns True if this Selector is enforcing FLS + **/ + public Boolean isEnforcingFLS() + { + return m_enforceFLS; + } + + /** + * Returns True if this Selector is enforcing CRUD Security + **/ + public Boolean isEnforcingCRUD() + { + return m_enforceCRUD; + } + + /** + * Provides access to the builder containing the list of fields base queries are using, this is demand + * created if one has not already been defined via setFieldListBuilder + * + * @depricated See newQueryFactory + **/ + public fflib_StringBuilder.CommaDelimitedListBuilder getFieldListBuilder() + { + return + new fflib_StringBuilder.CommaDelimitedListBuilder( + new List(newQueryFactory().getSelectedFields())); + } + + /** + * Use this method to override the default FieldListBuilder (created on demand via getFieldListBuilder) with a custom one, + * warning, this will bypass anything getSObjectFieldList or getSObjectFieldSetList returns + * + * @depricated See newQueryFactory + **/ + public void setFieldListBuilder(fflib_StringBuilder.FieldListBuilder fieldListBuilder) + { + // TODO: Consider if given the known use cases for this (dynamic selector optimisation) if it's OK to leave this as a null operation + } + + /** + * Returns in string form a comma delimited list of fields as defined via getSObjectFieldList and optionally getSObjectFieldSetList + * + * @deprecated See newQueryFactory + **/ + public String getFieldListString() + { + return getFieldListBuilder().getStringValue(); + } + + /** + * Returns in string form a comma delimited list of fields as defined via getSObjectFieldList and optionally getSObjectFieldSetList + * @param relation Will prefix fields with the given relation, e.g. MyLookupField__r + * + * @depricated See newQueryFactory + **/ + public String getRelatedFieldListString(String relation) + { + return getFieldListBuilder().getStringValue(relation + '.'); + } + + /** + * Returns the string representation of the SObject this selector represents + **/ + public String getSObjectName() + { + return describeWrapper.getDescribe().getName(); + } + + /** + * Performs a SOQL query, + * - Selecting the fields described via getSObjectFieldsList and getSObjectFieldSetList (if included) + * - From the SObject described by getSObjectType + * - Where the Id's match those provided in the set + * - Ordered by the fields returned via getOrderBy + * @returns A list of SObject's + **/ + public virtual List selectSObjectsById(Set idSet) + { + return Database.query(buildQuerySObjectById()); + } + + /** + * Performs a SOQL query, + * - Selecting the fields described via getSObjectFieldsList and getSObjectFieldSetList (if included) + * - From the SObject described by getSObjectType + * - Where the Id's match those provided in the set + * - Ordered by the fields returned via getOrderBy + * @returns A QueryLocator (typically for use in a Batch Apex job) + **/ + public virtual Database.QueryLocator queryLocatorById(Set idSet) + { + return Database.getQueryLocator(buildQuerySObjectById()); + } + + /** + * Throws an exception if the SObject indicated by getSObjectType is not accessible to the current user (read access) + * + * @deprecated If you utilise the newQueryFactory method this is automatically done for you (unless disabled by the selector) + **/ + public void assertIsAccessible() + { + if(!getSObjectType().getDescribe().isAccessible()) + throw new fflib_SObjectDomain.DomainException( + 'Permission to access an ' + getSObjectType().getDescribe().getName() + ' denied.'); + } + + /** + * Public access for the getSObjectType during Mock registration + * (adding public to the existing method broken base class API backwards compatibility) + **/ + public SObjectType getSObjectType2() + { + return getSObjectType(); + } + + /** + * Public access for the getSObjectType during Mock registration + * (adding public to the existing method broken base class API backwards compatibility1) + **/ + public SObjectType sObjectType() + { + return getSObjectType(); + } + + /** + * Returns a QueryFactory configured with the Selectors object, fields, fieldsets and default order by + **/ + public fflib_QueryFactory newQueryFactory() + { + return newQueryFactory(m_enforceCRUD, m_enforceFLS, true); + } + + /** + * Returns a QueryFactory configured with the Selectors object, fields, fieldsets and default order by + **/ + public fflib_QueryFactory newQueryFactory(Boolean includeSelectorFields) + { + return newQueryFactory(m_enforceCRUD, m_enforceFLS, includeSelectorFields); + } + + /** + * Returns a QueryFactory configured with the Selectors object, fields, fieldsets and default order by + * CRUD and FLS read security will be checked if the corresponding inputs are true (overrides that defined in the selector). + **/ + public fflib_QueryFactory newQueryFactory(Boolean assertCRUD, Boolean enforceFLS, Boolean includeSelectorFields) + { + // Construct QueryFactory around the given SObject + return configureQueryFactory( + new fflib_QueryFactory(getSObjectType2()), + assertCRUD, enforceFLS, includeSelectorFields); + } + + /** + * Adds the selectors fields to the given QueryFactory using the given relationship path as a prefix + * + * // TODO: This should be consistent (ideally) with configureQueryFactory below + **/ + public void configureQueryFactoryFields(fflib_QueryFactory queryFactory, String relationshipFieldPath) + { + // Add fields from selector prefixing the relationship path + for(SObjectField field : getSObjectFieldList()) + queryFactory.selectField(relationshipFieldPath + '.' + field.getDescribe().getName()); + // Automatically select the CurrencyIsoCode for MC orgs (unless the object is a known exception to the rule) + if(UserInfo.isMultiCurrencyOrganization() && CURRENCY_ISO_CODE_ENABLED) + queryFactory.selectField(relationshipFieldPath+'.CurrencyIsoCode'); + } + + /** + * Adds a subselect QueryFactory based on this selector to the given QueryFactor, returns the parentQueryFactory + **/ + public fflib_QueryFactory addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory) + { + return addQueryFactorySubselect(parentQueryFactory, true); + } + + /** + * Adds a subselect QueryFactory based on this selector to the given QueryFactor + **/ + public fflib_QueryFactory addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory, Boolean includeSelectorFields) + { + fflib_QueryFactory subSelectQueryFactory = + parentQueryFactory.subselectQuery(getSObjectType2()); + return configureQueryFactory( + subSelectQueryFactory, + m_enforceCRUD, + m_enforceFLS, + includeSelectorFields); + } + + /** + * Adds a subselect QueryFactory based on this selector to the given QueryFactor, returns the parentQueryFactory + **/ + public fflib_QueryFactory addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory, String relationshipName) + { + return addQueryFactorySubselect(parentQueryFactory, relationshipName, TRUE); + } + + /** + * Adds a subselect QueryFactory based on this selector to the given QueryFactor + **/ + public fflib_QueryFactory addQueryFactorySubselect(fflib_QueryFactory parentQueryFactory, String relationshipName, Boolean includeSelectorFields) + { + fflib_QueryFactory subSelectQueryFactory = parentQueryFactory.subselectQuery(relationshipName); + return configureQueryFactory(subSelectQueryFactory, m_enforceCRUD, m_enforceFLS, includeSelectorFields); + } + + /** + * Constructs the default SOQL query for this selector, see selectSObjectsById and queryLocatorById + **/ + protected String buildQuerySObjectById() + { + return newQueryFactory().setCondition('id in :idSet').toSOQL(); + } + + /** + * Configures a QueryFactory instance according to the configuration of this selector + **/ + private fflib_QueryFactory configureQueryFactory(fflib_QueryFactory queryFactory, Boolean assertCRUD, Boolean enforceFLS, Boolean includeSelectorFields) + { + // CRUD and FLS security required? + if (assertCRUD) + { + try { + // Leverage QueryFactory for CRUD checking + queryFactory.assertIsAccessible(); + } catch (fflib_SecurityUtils.CrudException e) { + // Marshal exception into DomainException for backwards compatibility + throw new fflib_SObjectDomain.DomainException( + 'Permission to access an ' + getSObjectType().getDescribe().getName() + ' denied.'); + } + } + queryFactory.setEnforceFLS(enforceFLS); + + // Configure the QueryFactory with the Selector fields? + if(includeSelectorFields) + { + // select the Selector fields and Fieldsets and set order + queryFactory.selectFields(getSObjectFieldList()); + + List fieldSetList = getSObjectFieldSetList(); + if(m_includeFieldSetFields && fieldSetList != null) + for(Schema.FieldSet fieldSet : fieldSetList) + queryFactory.selectFieldSet(fieldSet); + + // Automatically select the CurrencyIsoCode for MC orgs (unless the object is a known exception to the rule) + if(UserInfo.isMultiCurrencyOrganization() && CURRENCY_ISO_CODE_ENABLED) + queryFactory.selectField('CurrencyIsoCode'); + } + + // Parse the getOrderBy() + for(String orderBy : getOrderBy().split(',')) + { + List orderByParts = orderBy.trim().split(' '); + String fieldNamePart = orderByParts[0]; + String fieldSortOrderPart = orderByParts.size() > 1 ? orderByParts[1] : null; + fflib_QueryFactory.SortOrder fieldSortOrder = fflib_QueryFactory.SortOrder.ASCENDING; + if(fieldSortOrderPart==null) + fieldSortOrder = fflib_QueryFactory.SortOrder.ASCENDING; + else if(fieldSortOrderPart.equalsIgnoreCase('DESC')) + fieldSortOrder = fflib_QueryFactory.SortOrder.DESCENDING; + else if(fieldSortOrderPart.equalsIgnoreCase('ASC')) + fieldSortOrder = fflib_QueryFactory.SortOrder.ASCENDING; + queryFactory.addOrdering(fieldNamePart, fieldSortOrder, orderBy.containsIgnoreCase('NULLS LAST')); + } + + queryFactory.setSortSelectFields(m_sortSelectFields); + + return queryFactory; + } +} diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjectSelector.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectSelector.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectSelector.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectUnitOfWork.cls new file mode 100644 index 00000000000..324265def06 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectUnitOfWork.cls @@ -0,0 +1,921 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * Provides an implementation of the Enterprise Application Architecture Unit Of Work, as defined by Martin Fowler + * http://martinfowler.com/eaaCatalog/unitOfWork.html + * + * "When you're pulling data in and out of a database, it's important to keep track of what you've changed; otherwise, + * that data won't be written back into the database. Similarly you have to insert new objects you create and + * remove any objects you delete." + * + * "You can change the database with each change to your object model, but this can lead to lots of very small database calls, + * which ends up being very slow. Furthermore it requires you to have a transaction open for the whole interaction, which is + * impractical if you have a business transaction that spans multiple requests. The situation is even worse if you need to + * keep track of the objects you've read so you can avoid inconsistent reads." + * + * "A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you're done, + * it figures out everything that needs to be done to alter the database as a result of your work." + * + * In an Apex context this pattern provides the following specific benefits + * - Applies bulkfication to DML operations, insert, update and delete + * - Manages a business transaction around the work and ensures a rollback occurs (even when exceptions are later handled by the caller) + * - Honours dependency rules between records and updates dependent relationships automatically during the commit + * + * Please refer to the testMethod's in this class for example usage + * + * TODO: Need to complete the 100% coverage by covering parameter exceptions in tests + * TODO: Need to add some more test methods for more complex use cases and some unexpected (e.g. registerDirty and then registerDeleted) + * + **/ +public virtual class fflib_SObjectUnitOfWork + implements fflib_ISObjectUnitOfWork +{ + protected List m_sObjectTypes = new List(); + + protected Map> m_newListByType = new Map>(); + + protected Map> m_dirtyMapByType = new Map>(); + + protected Map> m_deletedMapByType = new Map>(); + protected Map> m_emptyRecycleBinMapByType = new Map>(); + + protected Map m_relationships = new Map(); + + protected Map> m_publishBeforeListByType = new Map>(); + protected Map> m_publishAfterSuccessListByType = new Map>(); + protected Map> m_publishAfterFailureListByType = new Map>(); + + protected List m_workList = new List(); + + @TestVisible + protected IEmailWork m_emailWork = new SendEmailWork(); + + protected IDML m_dml; + + /** + * Interface describes work to be performed during the commitWork method + **/ + public interface IDoWork + { + void doWork(); + } + + public interface IDML + { + void dmlInsert(List objList); + void dmlUpdate(List objList); + void dmlDelete(List objList); + void eventPublish(List objList); + void emptyRecycleBin(List objList); + } + + public virtual class SimpleDML implements IDML + { + public virtual void dmlInsert(List objList) + { + insert objList; + } + public virtual void dmlUpdate(List objList) + { + update objList; + } + public virtual void dmlDelete(List objList) + { + delete objList; + } + public virtual void eventPublish(List objList) + { + EventBus.publish(objList); + } + public virtual void emptyRecycleBin(List objList) + { + if (objList.isEmpty()) + { + return; + } + + Database.emptyRecycleBin(objList); + } + } + /** + * Constructs a new UnitOfWork to support work against the given object list + * + * @param sObjectTypes A list of objects given in dependency order (least dependent first) + */ + public fflib_SObjectUnitOfWork(List sObjectTypes) + { + this(sObjectTypes,new SimpleDML()); + } + + public fflib_SObjectUnitOfWork(List sObjectTypes, IDML dml) + { + m_sObjectTypes = sObjectTypes.clone(); + + for (Schema.SObjectType sObjectType : m_sObjectTypes) + { + // register the type + handleRegisterType(sObjectType); + } + + m_relationships.put(Messaging.SingleEmailMessage.class.getName(), new Relationships()); + + m_dml = dml; + } + + // default implementations for commitWork events + public virtual void onRegisterType(Schema.SObjectType sObjectType) {} + public virtual void onCommitWorkStarting() {} + + public virtual void onPublishBeforeEventsStarting() {} + public virtual void onPublishBeforeEventsFinished() {} + + public virtual void onDMLStarting() {} + public virtual void onDMLFinished() {} + + public virtual void onDoWorkStarting() {} + public virtual void onDoWorkFinished() {} + + public virtual void onPublishAfterSuccessEventsStarting() {} + public virtual void onPublishAfterSuccessEventsFinished() {} + + public virtual void onPublishAfterFailureEventsStarting() {} + public virtual void onPublishAfterFailureEventsFinished() {} + + public virtual void onCommitWorkFinishing() {} + public virtual void onCommitWorkFinished(Boolean wasSuccessful) {} + + /** + * Registers the type to be used for DML operations + * + * @param sObjectType - The type to register + * + */ + private void handleRegisterType(Schema.SObjectType sObjectType) + { + String sObjectName = sObjectType.getDescribe().getName(); + + // add type to dml operation tracking + m_newListByType.put(sObjectName, new List()); + m_dirtyMapByType.put(sObjectName, new Map()); + m_deletedMapByType.put(sObjectName, new Map()); + m_emptyRecycleBinMapByType.put(sObjectName, new Map()); + m_relationships.put(sObjectName, new Relationships()); + + m_publishBeforeListByType.put(sObjectName, new List()); + m_publishAfterSuccessListByType.put(sObjectName, new List()); + m_publishAfterFailureListByType.put(sObjectName, new List()); + + // give derived class opportunity to register the type + onRegisterType(sObjectType); + } + + /** + * Register a generic piece of work to be invoked during the commitWork phase + **/ + public void registerWork(IDoWork work) + { + m_workList.add(work); + } + + /** + * Registers the given email to be sent during the commitWork + **/ + public void registerEmail(Messaging.Email email) + { + m_emailWork.registerEmail(email); + } + + /** + * Register an deleted record to be removed from the recycle bin during the commitWork method + * + * @param record An deleted record + **/ + public void registerEmptyRecycleBin(SObject record) + { + String sObjectType = record.getSObjectType().getDescribe().getName(); + assertForSupportedSObjectType(m_emptyRecycleBinMapByType, sObjectType); + + m_emptyRecycleBinMapByType.get(sObjectType).put(record.Id, record); + } + + /** + * Register deleted records to be removed from the recycle bin during the commitWork method + * + * @param records Deleted records + **/ + public void registerEmptyRecycleBin(List records) + { + for (SObject record : records) + { + registerEmptyRecycleBin(record); + } + } + + /** + * Register a newly created SObject instance to be inserted when commitWork is called + * + * @param record A newly created SObject instance to be inserted during commitWork + **/ + public void registerNew(SObject record) + { + registerNew(record, null, null); + } + + /** + * Register a list of newly created SObject instances to be inserted when commitWork is called + * + * @param records A list of newly created SObject instances to be inserted during commitWork + **/ + public void registerNew(List records) + { + for (SObject record : records) + { + registerNew(record, null, null); + } + } + + /** + * Register a newly created SObject instance to be inserted when commitWork is called, + * you may also provide a reference to the parent record instance (should also be registered as new separately) + * + * @param record A newly created SObject instance to be inserted during commitWork + * @param relatedToParentField A SObjectField reference to the child field that associates the child record with its parent + * @param relatedToParentRecord A SObject instance of the parent record (should also be registered as new separately) + **/ + public void registerNew(SObject record, Schema.SObjectField relatedToParentField, SObject relatedToParentRecord) + { + if (record.Id != null) + throw new UnitOfWorkException('Only new records can be registered as new'); + String sObjectType = record.getSObjectType().getDescribe().getName(); + + assertForNonEventSObjectType(sObjectType); + assertForSupportedSObjectType(m_newListByType, sObjectType); + + m_newListByType.get(sObjectType).add(record); + if (relatedToParentRecord!=null && relatedToParentField!=null) + registerRelationship(record, relatedToParentField, relatedToParentRecord); + } + + /** + * Register a relationship between two records that have yet to be inserted to the database. This information will be + * used during the commitWork phase to make the references only when related records have been inserted to the database. + * + * @param record An existing or newly created record + * @param relatedToField A SObjectField reference to the lookup field that relates the two records together + * @param relatedTo A SObject instance (yet to be committed to the database) + */ + public void registerRelationship(SObject record, Schema.SObjectField relatedToField, SObject relatedTo) + { + String sObjectType = record.getSObjectType().getDescribe().getName(); + + assertForNonEventSObjectType(sObjectType); + assertForSupportedSObjectType(m_newListByType, sObjectType); + + m_relationships.get(sObjectType).add(record, relatedToField, relatedTo); + } + + /** + * Registers a relationship between a record and a Messaging.Email where the record has yet to be inserted + * to the database. This information will be + * used during the commitWork phase to make the references only when related records have been inserted to the database. + * + * @param email a single email message instance + * @param relatedTo A SObject instance (yet to be committed to the database) + */ + public void registerRelationship( Messaging.SingleEmailMessage email, SObject relatedTo ) + { + m_relationships.get( Messaging.SingleEmailMessage.class.getName() ).add(email, relatedTo); + } + + /** + * Registers a relationship between a record and a lookup value using an external ID field and a provided value. This + * information will be used during the commitWork phase to make the lookup reference requested when inserted to the database. + * + * @param record An existing or newly created record + * @param relatedToField A SObjectField reference to the lookup field that relates the two records together + * @param externalIdField A SObjectField reference to a field on the target SObject that is marked as isExternalId + * @param externalId A Object representing the targeted value of the externalIdField in said lookup + * + * Usage Example: uow.registerRelationship(recordSObject, record_sobject__c.relationship_field__c, lookup_sobject__c.external_id__c, 'abc123'); + * + * Wraps putSObject, creating a new instance of the lookup sobject using the external id field and value. + */ + public void registerRelationship(SObject record, Schema.SObjectField relatedToField, Schema.SObjectField externalIdField, Object externalId) + { + // NOTE: Due to the lack of ExternalID references on Standard Objects, this method can not be provided a standardized Unit Test. - Rick Parker + String sObjectType = record.getSObjectType().getDescribe().getName(); + if(!m_newListByType.containsKey(sObjectType)) + throw new UnitOfWorkException(String.format('SObject type {0} is not supported by this unit of work', new String[] { sObjectType })); + m_relationships.get(sObjectType).add(record, relatedToField, externalIdField, externalId); + } + + /** + * Register an existing record to be updated during the commitWork method + * + * @param record An existing record + **/ + public void registerDirty(SObject record) + { + registerDirty(record, new List()); + } + + /** + * Registers the entire records as dirty or just only the dirty fields if the record was already registered + * + * @param records SObjects to register as dirty + * @param dirtyFields A list of modified fields + */ + public void registerDirty(List records, List dirtyFields) + { + for (SObject record : records) + { + registerDirty(record, dirtyFields); + } + } + + /** + * Registers the entire record as dirty or just only the dirty fields if the record was already registered + * + * @param record SObject to register as dirty + * @param dirtyFields A list of modified fields + */ + public void registerDirty(SObject record, List dirtyFields) + { + if (record.Id == null) + throw new UnitOfWorkException('New records cannot be registered as dirty'); + String sObjectType = record.getSObjectType().getDescribe().getName(); + + assertForNonEventSObjectType(sObjectType); + assertForSupportedSObjectType(m_dirtyMapByType, sObjectType); + + // If record isn't registered as dirty, or no dirty fields to drive a merge + if (!m_dirtyMapByType.get(sObjectType).containsKey(record.Id) || dirtyFields.isEmpty()) + { + // Register the record as dirty + m_dirtyMapByType.get(sObjectType).put(record.Id, record); + } + else + { + // Update the registered record's fields + SObject registeredRecord = m_dirtyMapByType.get(sObjectType).get(record.Id); + + for (SObjectField dirtyField : dirtyFields) { + registeredRecord.put(dirtyField, record.get(dirtyField)); + } + + m_dirtyMapByType.get(sObjectType).put(record.Id, registeredRecord); + } + } + + /** + * Register an existing record to be updated when commitWork is called, + * you may also provide a reference to the parent record instance (should also be registered as new separately) + * + * @param record A newly created SObject instance to be inserted during commitWork + * @param relatedToParentField A SObjectField reference to the child field that associates the child record with its parent + * @param relatedToParentRecord A SObject instance of the parent record (should also be registered as new separately) + **/ + public void registerDirty(SObject record, Schema.SObjectField relatedToParentField, SObject relatedToParentRecord) + { + registerDirty(record); + if (relatedToParentRecord!=null && relatedToParentField!=null) + registerRelationship(record, relatedToParentField, relatedToParentRecord); + } + + /** + * Register a list of existing records to be updated during the commitWork method + * + * @param records A list of existing records + **/ + public void registerDirty(List records) + { + for (SObject record : records) + { + this.registerDirty(record); + } + } + + /** + * Register a new or existing record to be inserted/updated during the commitWork method + * + * @param record A new or existing record + **/ + public void registerUpsert(SObject record) + { + if (record.Id == null) + { + registerNew(record, null, null); + } + else + { + registerDirty(record, new List()); + } + } + + /** + * Register a list of mix of new and existing records to be inserted updated during the commitWork method + * + * @param records A list of mix of new and existing records + **/ + public void registerUpsert(List records) + { + for (SObject record : records) + { + this.registerUpsert(record); + } + } + + /** + * Register an existing record to be deleted during the commitWork method + * + * @param record An existing record + **/ + public void registerDeleted(SObject record) + { + if (record.Id == null) + throw new UnitOfWorkException('New records cannot be registered for deletion'); + String sObjectType = record.getSObjectType().getDescribe().getName(); + + assertForNonEventSObjectType(sObjectType); + assertForSupportedSObjectType(m_deletedMapByType, sObjectType); + + m_deletedMapByType.get(sObjectType).put(record.Id, record); + } + + /** + * Register a list of existing records to be deleted during the commitWork method + * + * @param records A list of existing records + **/ + public void registerDeleted(List records) + { + for (SObject record : records) + { + this.registerDeleted(record); + } + } + + /** + * Register a list of existing records to be deleted and removed from the recycle bin during the commitWork method + * + * @param records A list of existing records + **/ + public void registerPermanentlyDeleted(List records) + { + this.registerEmptyRecycleBin(records); + this.registerDeleted(records); + } + + /** + * Register a list of existing records to be deleted and removed from the recycle bin during the commitWork method + * + * @param record A list of existing records + **/ + public void registerPermanentlyDeleted(SObject record) + { + this.registerEmptyRecycleBin(record); + this.registerDeleted(record); + } + + /** + * Register a newly created SObject (Platform Event) instance to be published when commitWork is called + * + * @param record A newly created SObject (Platform Event) instance to be inserted during commitWork + **/ + public void registerPublishBeforeTransaction(SObject record) + { + String sObjectType = record.getSObjectType().getDescribe().getName(); + + assertForEventSObjectType(sObjectType); + assertForSupportedSObjectType(m_publishBeforeListByType, sObjectType); + + m_publishBeforeListByType.get(sObjectType).add(record); + } + + /** + * Register a list of newly created SObject (Platform Event) instance to be published when commitWork is called + * + * @param records A list of existing records + **/ + public void registerPublishBeforeTransaction(List records) + { + for (SObject record : records) + { + this.registerPublishBeforeTransaction(record); + } + } + + /** + * Register a newly created SObject (Platform Event) instance to be published when commitWork is called + * + * @param record A newly created SObject (Platform Event) instance to be inserted during commitWork + **/ + public void registerPublishAfterSuccessTransaction(SObject record) + { + String sObjectType = record.getSObjectType().getDescribe().getName(); + + assertForEventSObjectType(sObjectType); + assertForSupportedSObjectType(m_publishAfterSuccessListByType, sObjectType); + + m_publishAfterSuccessListByType.get(sObjectType).add(record); + } + + /** + * Register a list of newly created SObject (Platform Event) instance to be published when commitWork is called + * + * @param records A list of existing records + **/ + public void registerPublishAfterSuccessTransaction(List records) + { + for (SObject record : records) + { + this.registerPublishAfterSuccessTransaction(record); + } + } + /** + * Register a newly created SObject (Platform Event) instance to be published when commitWork is called + * + * @param record A newly created SObject (Platform Event) instance to be inserted during commitWork + **/ + public void registerPublishAfterFailureTransaction(SObject record) + { + String sObjectType = record.getSObjectType().getDescribe().getName(); + + assertForEventSObjectType(sObjectType); + assertForSupportedSObjectType(m_publishAfterFailureListByType, sObjectType); + + m_publishAfterFailureListByType.get(sObjectType).add(record); + } + + /** + * Register a list of newly created SObject (Platform Event) instance to be published when commitWork is called + * + * @param records A list of existing records + **/ + public void registerPublishAfterFailureTransaction(List records) + { + for (SObject record : records) + { + this.registerPublishAfterFailureTransaction(record); + } + } + + /** + * Takes all the work that has been registered with the UnitOfWork and commits it to the database + **/ + public void commitWork() + { + Savepoint sp = Database.setSavepoint(); + Boolean wasSuccessful = false; + try + { + doCommitWork(); + wasSuccessful = true; + } + catch (Exception e) + { + Database.rollback(sp); + throw e; + } + finally + { + doAfterCommitWorkSteps(wasSuccessful); + } + } + + private void doCommitWork() + { + onCommitWorkStarting(); + onPublishBeforeEventsStarting(); + publishBeforeEventsStarting(); + onPublishBeforeEventsFinished(); + + onDMLStarting(); + insertDmlByType(); + updateDmlByType(); + deleteDmlByType(); + emptyRecycleBinByType(); + resolveEmailRelationships(); + onDMLFinished(); + + onDoWorkStarting(); + doWork(); + onDoWorkFinished(); + onCommitWorkFinishing(); + } + + private void doAfterCommitWorkSteps(Boolean wasSuccessful) + { + if (wasSuccessful) + { + doAfterCommitWorkSuccessSteps(); + } + else + { + doAfterCommitWorkFailureSteps(); + } + onCommitWorkFinished(wasSuccessful); + } + + private void doAfterCommitWorkSuccessSteps() + { + onPublishAfterSuccessEventsStarting(); + publishAfterSuccessEvents(); + onPublishAfterSuccessEventsFinished(); + } + + private void doAfterCommitWorkFailureSteps() + { + onPublishAfterFailureEventsStarting(); + publishAfterFailureEvents(); + onPublishAfterFailureEventsFinished(); + } + + private void publishBeforeEventsStarting() + { + for (Schema.SObjectType sObjectType : m_sObjectTypes) + { + m_dml.eventPublish(m_publishBeforeListByType.get(sObjectType.getDescribe().getName())); + } + } + + private void insertDmlByType() + { + for (Schema.SObjectType sObjectType : m_sObjectTypes) + { + m_relationships.get(sObjectType.getDescribe().getName()).resolve(); + m_dml.dmlInsert(m_newListByType.get(sObjectType.getDescribe().getName())); + } + } + + private void updateDmlByType() + { + for (Schema.SObjectType sObjectType : m_sObjectTypes) + { + m_dml.dmlUpdate(m_dirtyMapByType.get(sObjectType.getDescribe().getName()).values()); + } + } + + private void deleteDmlByType() + { + Integer objectIdx = m_sObjectTypes.size() - 1; + while (objectIdx >= 0) + { + m_dml.dmlDelete(m_deletedMapByType.get(m_sObjectTypes[objectIdx--].getDescribe().getName()).values()); + } + } + + private void emptyRecycleBinByType() + { + Integer objectIdx = m_sObjectTypes.size() - 1; + while (objectIdx >= 0) + { + m_dml.emptyRecycleBin(m_emptyRecycleBinMapByType.get(m_sObjectTypes[objectIdx--].getDescribe().getName()).values()); + } + } + + private void resolveEmailRelationships() + { + m_relationships.get(Messaging.SingleEmailMessage.class.getName()).resolve(); + } + + private void doWork() + { + m_workList.add(m_emailWork); + for (IDoWork work : m_workList) + { + work.doWork(); + } + } + + private void publishAfterSuccessEvents() + { + for (Schema.SObjectType sObjectType : m_sObjectTypes) + { + m_dml.eventPublish(m_publishAfterSuccessListByType.get(sObjectType.getDescribe().getName())); + } + } + + private void publishAfterFailureEvents() + { + for (Schema.SObjectType sObjectType : m_sObjectTypes) + { + m_dml.eventPublish(m_publishAfterFailureListByType.get(sObjectType.getDescribe().getName())); + } + } + + @TestVisible + private void assertForNonEventSObjectType(String sObjectType) + { + if (sObjectType.length() > 3 && sObjectType.right(3) == '__e') + { + throw new UnitOfWorkException( + String.format( + 'SObject type {0} must use registerPublishBeforeTransaction or ' + + 'registerPublishAfterTransaction methods to be used within this unit of work', + new List { sObjectType } + ) + ); + } + } + + @TestVisible + private void assertForEventSObjectType(String sObjectType) + { + if (sObjectType.length() > 3 && sObjectType.right(3) != '__e') + { + throw new UnitOfWorkException( + String.format( + 'SObject type {0} is invalid for publishing within this unit of work', + new List {sObjectType} + ) + ); + } + } + + @TestVisible + private void assertForSupportedSObjectType(Map theMap, String sObjectType) + { + if (!theMap.containsKey(sObjectType)) + { + throw new UnitOfWorkException( + String.format( + 'SObject type {0} is not supported by this unit of work', + new List { sObjectType } + ) + ); + } + } + + private class Relationships + { + private List m_relationships = new List(); + + public void resolve() + { + // Resolve relationships + for (IRelationship relationship : m_relationships) + { + //relationship.Record.put(relationship.RelatedToField, relationship.RelatedTo.Id); + relationship.resolve(); + } + + } + + public void add(SObject record, Schema.SObjectField relatedToField, Schema.SObjectField externalIdField, Object externalId) + { + if (relatedToField == null) { + throw new UnitOfWorkException('Invalid argument: relatedToField.'); + } + + String relationshipName = relatedToField.getDescribe().getRelationshipName(); + if (String.isBlank(relationshipName)) { + throw new UnitOfWorkException('Invalid argument: relatedToField. Field supplied is not a relationship field.'); + } + + List relatedObjects = relatedToField.getDescribe().getReferenceTo(); + Schema.SObjectType relatedObject = relatedObjects[0]; + + String externalIdFieldName = externalIdField.getDescribe().getName(); + Boolean relatedHasExternalIdField = relatedObject.getDescribe().fields.getMap().keySet().contains(externalIdFieldName.toLowerCase()); + Boolean externalIdFieldIsValid = externalIdField.getDescribe().isExternalId(); + + if (!relatedHasExternalIdField) { + throw new UnitOfWorkException('Invalid argument: externalIdField. Field supplied is not a known field on the target sObject.'); + } + + if (!externalIdFieldIsValid) { + throw new UnitOfWorkException('Invalid argument: externalIdField. Field supplied is not a marked as an External Identifier.'); + } + + RelationshipByExternalId relationship = new RelationshipByExternalId(); + relationship.Record = record; + relationship.RelatedToField = relatedToField; + relationship.RelatedTo = relatedObject; + relationship.RelationshipName = relationshipName; + relationship.ExternalIdField = externalIdField; + relationship.ExternalId = externalId; + m_relationships.add(relationship); + } + + public void add(SObject record, Schema.SObjectField relatedToField, SObject relatedTo) + { + // Relationship to resolve + Relationship relationship = new Relationship(); + relationship.Record = record; + relationship.RelatedToField = relatedToField; + relationship.RelatedTo = relatedTo; + m_relationships.add(relationship); + } + + public void add(Messaging.SingleEmailMessage email, SObject relatedTo) + { + EmailRelationship emailRelationship = new EmailRelationship(); + emailRelationship.email = email; + emailRelationship.relatedTo = relatedTo; + m_relationships.add(emailRelationship); + } + } + + private interface IRelationship + { + void resolve(); + } + + private class RelationshipByExternalId implements IRelationship + { + public SObject Record; + public Schema.SObjectField RelatedToField; + public Schema.SObjectType RelatedTo; + public String RelationshipName; + public Schema.SObjectField ExternalIdField; + public Object ExternalId; + + public void resolve() + { + SObject relationshipObject = this.RelatedTo.newSObject(); + relationshipObject.put( ExternalIdField.getDescribe().getName(), this.ExternalId ); + this.Record.putSObject( this.RelationshipName, relationshipObject ); + } + } + + private class Relationship implements IRelationship + { + public SObject Record; + public Schema.SObjectField RelatedToField; + public SObject RelatedTo; + + public void resolve() + { + this.Record.put( this.RelatedToField, this.RelatedTo.Id); + } + } + + private class EmailRelationship implements IRelationship + { + public Messaging.SingleEmailMessage email; + public SObject relatedTo; + + public void resolve() + { + this.email.setWhatId( this.relatedTo.Id ); + } + } + + /** + * UnitOfWork Exception + **/ + public class UnitOfWorkException extends Exception {} + + /** + * Internal implementation of Messaging.sendEmail, see outer class registerEmail method + **/ + public interface IEmailWork extends IDoWork + { + void registerEmail(Messaging.Email email); + } + + private class SendEmailWork implements IEmailWork + { + private List emails; + + public SendEmailWork() + { + this.emails = new List(); + } + + public void registerEmail(Messaging.Email email) + { + this.emails.add(email); + } + + public void doWork() + { + if (emails.size() > 0) Messaging.sendEmail(emails); + } + } +} diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjectUnitOfWork.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectUnitOfWork.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjectUnitOfWork.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SObjects.cls b/force-app/infrastructure/apex-common/main/classes/fflib_SObjects.cls new file mode 100644 index 00000000000..92089ace871 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjects.cls @@ -0,0 +1,368 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ +public virtual class fflib_SObjects + extends fflib_Objects + implements fflib_ISObjects +{ + + public Schema.DescribeSObjectResult SObjectDescribe {get; private set;} + + /** + * Useful during unit testing to assert at a more granular and robust level for errors raised during the various trigger events + **/ + public static ErrorFactory Errors {get; private set;} + + static + { + Errors = new ErrorFactory(); + } + + /** + * Class constructor + */ + public fflib_SObjects(List records) + { + super(records); + } + + public fflib_SObjects(List records, Schema.SObjectType sObjectType) + { + super(records); + SObjectDescribe = sObjectType.getDescribe(); + } + + public virtual List getRecords() + { + return (List) getObjects(); + } + + public virtual Set getRecordIds() + { + return new Map(getRecords()).keySet(); + } + + public virtual override Object getType() + { + return getSObjectType(); + } + + public virtual SObjectType getSObjectType() + { + return SObjectDescribe.getSObjectType(); + } + + /** + * Ensures logging of errors in the Domain context for later assertions in tests + * + * @param message + * @param record + * + * @return Returns the Error message + **/ + protected virtual String error(String message, SObject record) + { + return Errors.error(this, message, record); + } + + /** + * Ensures logging of errors in the Domain context for later assertions in tests + * + * @param message + * @param record + * @param field + * + * @return Returns the Error message + **/ + protected virtual String error(String message, SObject record, SObjectField field) + { + return fflib_SObjects.Errors.error(this, message, record, field); + } + + protected virtual Set getFieldValues(Schema.SObjectField sObjectField) + { + Set result = new Set(); + for (SObject record : getRecords()) + { + result.add(record.get(sObjectField)); + } + return result; + } + + /** + * @param sObjectField The Schema.SObjectField to compare against the given value + * @param value The given value of the records sObjectField to include in the return + * + * @return A list with only the SObjects where the given sObjectField has the provided value + */ + protected virtual List getRecordsByFieldValue(Schema.SObjectField sObjectField, Object value) + { + return getRecordsByFieldValues(sObjectField, new Set{value}); + } + + /** + * @param sObjectField The Schema.SObjectField to compare against the given value + * @param values The given values of the records sObjectField to include in the return + * + * @return A list with only the SObjects where the given sObjectField value is part of the provided values + */ + protected virtual List getRecordsByFieldValues(Schema.SObjectField sObjectField, Set values) + { + List result = new List(); + for (SObject record : getRecords()) + { + if (values?.contains(record.get(sObjectField))) + { + result.add(record); + } + } + return result; + } + + /** + * @param sObjectField The Schema.SObjectField to check its value for a Blank value + * + * @return A list with only the SObjects where the given sObjectField value is either null or '') + */ + protected virtual List getRecordsWithBlankFieldValues(Schema.SObjectField sObjectField) + { + return getRecordsWithBlankFieldValues( + new Set {sObjectField} + ); + } + + /** + * @param sObjectFields The Schema.SObjectFields to check their value for a Blank value + * + * @return A list with only the SObjects where the at least one given sObjectField value is either null or '') + */ + protected virtual List getRecordsWithBlankFieldValues(Set sObjectFields) + { + List result = new List(); + for (SObject record : getRecords()) + { + for (SObjectField sObjectField : sObjectFields) + { + if (String.isBlank((String) record.get(sObjectField))) + { + result.add(record); + break; + } + } + } + return result; + } + + /** + * @param sObjectFields The Schema.SObjectFields to check their value for a Blank value + * + * @return A list with only the SObjects where all given sObjectField values are either null or '' + */ + protected virtual List getRecordsWithAllBlankFieldValues(Set sObjectFields) + { + List result = new List(); + for (SObject record : getRecords()) + { + Boolean allBlank = true; + for (SObjectField sObjectField : sObjectFields) + { + if (String.isNotBlank((String) record.get(sObjectField))) + { + allBlank = false; + break; + } + } + if (allBlank) result.add(record); + } + return result; + } + + /** + * @param sObjectField The Schema.SObjectField to check its value for a Non-Blank value + * + * @return A list with only the SObjects where the given sObjectField value is not null or '' + */ + protected virtual List getRecordsWithNotBlankFieldValues(Schema.SObjectField sObjectField) + { + return getRecordsWithNotBlankFieldValues( + new Set {sObjectField} + ); + } + + /** + * @param sObjectFields The Schema.SObjectFields to check their value for a Non-Blank value + * + * @return A list with only the SObjects where the at least one given sObjectField value not null or '' + */ + protected virtual List getRecordsWithNotBlankFieldValues(Set sObjectFields) + { + List result = new List(); + for (SObject record : getRecords()) + { + for (SObjectField sObjectField : sObjectFields) + { + if (String.isNotBlank((String) record.get(sObjectField))) + { + result.add(record); + break; + } + } + } + return result; + } + + /** + * @param sObjectFields The Schema.SObjectFields to check their value for a Non-Blank value + * + * @return A list with only the SObjects where all given sObjectField values are not null or '' + */ + protected virtual List getRecordsWithAllNotBlankFieldValues(Set sObjectFields) + { + List result = new List(); + for (SObject record : getRecords()) + { + Boolean allNonBlank = true; + for (SObjectField sObjectField : sObjectFields) + { + if (String.isBlank((String) record.get(sObjectField))) + { + allNonBlank = false; + break; + } + } + if (allNonBlank) result.add(record); + } + return result; + } + + + protected virtual void setFieldValue(Schema.SObjectField sObjectField, Object value) + { + for (SObject record : getRecords()) + { + record.put(sObjectField, value); + } + } + + /** + * @param sObjectFieldToCheck The SObjectField to match the key against in the provided map + * @param sObjectFieldToUpdate The SObjectField to store the mapped value when the key matches the value in the sObjectFieldToUpdate field + * @param values Map of values to store by the sObjectFieldToCheck fields value + */ + protected virtual void setFieldValueByMap( + Schema.SObjectField sObjectFieldToCheck, + Schema.SObjectField sObjectFieldToUpdate, + Map values) + { + for (SObject record : getRecords()) + { + Object keyValue = record.get(sObjectFieldToCheck); + if (values?.containsKey(keyValue)) + { + record.put(sObjectFieldToUpdate, values.get(keyValue)); + } + } + } + + /** + * Ensures logging of errors in the Domain context for later assertions in tests + **/ + public virtual class ErrorFactory + { + private List errorList = new List(); + + private ErrorFactory() { } + + public String error(String message, SObject record) + { + return error(null, message, record); + } + + public String error(fflib_SObjects domain, String message, SObject record) + { + ObjectError objectError = new ObjectError(); + objectError.domain = domain; + objectError.message = message; + objectError.record = record; + errorList.add(objectError); + return message; + } + + public String error(String message, SObject record, SObjectField field) + { + return error(null, message, record, field); + } + + public String error(fflib_ISObjects domain, String message, SObject record, SObjectField field) + { + FieldError fieldError = new FieldError(); + fieldError.domain = domain; + fieldError.message = message; + fieldError.record = record; + fieldError.field = field; + errorList.add(fieldError); + return message; + } + + public List getAll() + { + return errorList.clone(); + } + + public void clearAll() + { + errorList.clear(); + } + } + + /** + * Ensures logging of errors in the Domain context for later assertions in tests + **/ + public virtual class FieldError extends ObjectError + { + public SObjectField field; + + public FieldError() { } + } + + /** + * Ensures logging of errors in the Domain context for later assertions in tests + **/ + public virtual class ObjectError extends Error + { + public SObject record; + + public ObjectError() { } + } + + /** + * Ensures logging of errors in the Domain context for later assertions in tests + **/ + public abstract class Error + { + public String message; + public fflib_ISObjects domain; + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/fflib_SObjects.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_SObjects.cls-meta.xml similarity index 80% rename from force-app/main/default/classes/fflib_SObjects.cls-meta.xml rename to force-app/infrastructure/apex-common/main/classes/fflib_SObjects.cls-meta.xml index d75b0582fba..f928c8e56bc 100644 --- a/force-app/main/default/classes/fflib_SObjects.cls-meta.xml +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SObjects.cls-meta.xml @@ -1,5 +1,5 @@ - 51.0 + 53.0 Active diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SecurityUtils.cls b/force-app/infrastructure/apex-common/main/classes/fflib_SecurityUtils.cls new file mode 100644 index 00000000000..eaa84cc032b --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SecurityUtils.cls @@ -0,0 +1,348 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * Utility class for checking FLS/CRUD. NOTE: all "check" methods will throw a SecurityException (or subclass) if the + * user does not have the proper security granted. + **/ +public class fflib_SecurityUtils +{ + @TestVisible + private enum OperationType { CREATE, READ, MODIFY, DEL } //UPDATE and DELETE are reserved words + + /** + * SecurityException is never be thrown directly by fflib_SecurityUtils, instead all + * forms of CRUD and FLD violations throw subclasses of it. It is provided as a convenience + * in the event you wish to handle CRUD and FLS violations the same way (e.g. die and display an error) + **/ + public virtual class SecurityException extends Exception { + protected OperationType m_operation; + protected Schema.SObjectType m_objectType; + } + + /** + * CrudException represents a running user's lack of read/create/update/delete access at a profile (or permission set) + * level. Sharing and field level security issues will never cause this. + **/ + public class CrudException extends SecurityException{ + + private CrudException(OperationType operation, Schema.SObjectType objectType){ + this.m_operation = operation; + this.m_objectType = objectType; + if(operation == OperationType.CREATE) + this.setMessage(System.Label.fflib_security_error_object_not_insertable); + else if(operation == OperationType.READ) + this.setMessage(System.Label.fflib_security_error_object_not_readable); + else if(operation == OperationType.MODIFY) + this.setMessage(System.Label.fflib_security_error_object_not_updateable); + else if(operation == OperationType.DEL) + this.setMessage(System.Label.fflib_security_error_object_not_deletable); + + this.setMessage( + String.format( + this.getMessage(), + new List{ + objectType.getDescribe().getName() + } + ) + ); + } + } + /** + * FlsException represents a running user's lack of field level security to a specific field at a profile (or permission set) level + * Sharing and CRUD security issues will never cause this to be thrown. + **/ + public class FlsException extends SecurityException{ + private Schema.SObjectField m_fieldToken; + + private FlsException(OperationType operation, Schema.SObjectType objectType, Schema.SObjectField fieldToken){ + this.m_operation = operation; + this.m_objectType = objectType; + this.m_fieldToken = fieldToken; + if(operation == OperationType.CREATE) + this.setMessage(System.Label.fflib_security_error_field_not_insertable); + else if(operation == OperationType.READ) + this.setMessage(System.Label.fflib_security_error_field_not_readable); + else if(operation == OperationType.MODIFY) + this.setMessage(System.Label.fflib_security_error_field_not_updateable); + + this.setMessage( + String.format( + this.getMessage(), + new List{ + objectType.getDescribe().getName(), + fieldToken.getDescribe().getName() + } + ) + ); + } + } + + /** + * If set to true all check methods will always return void, and never throw exceptions. + * This should really only be set to true if an app-wide setting to disable in-apex + * FLS and CRUD checks exists and is enabled. + * Per security best practices setting BYPASS should be an a opt-in, and not the default behavior. + **/ + public static Boolean BYPASS_INTERNAL_FLS_AND_CRUD = false; + + /** + * Check{Insert,Read,Update} methods check both FLS and CRUD + **/ + + /** + * Checks both insert FLS and CRUD for the specified object type and fields. + * @exception FlsException if the running user does not have insert rights to any fields in {@code fieldNames}. + * @exception CrudException if the running user does not have insert rights to {@code objType} + **/ + public static void checkInsert(SObjectType objType, List fieldNames) + { + checkObjectIsInsertable(objType); + for (String fieldName : fieldNames) + { + checkFieldIsInsertable(objType, fieldName); + } + } + + /** + * Identical to {@link #checkInsert(SObjectType,List)}, except with SObjectField instead of String field references. + * @exception FlsException if the running user does not have insert rights to any fields in {@code fieldTokens}. + * @exception CrudException if the running user does not have insert rights to {@code objType} + **/ + public static void checkInsert(SObjectType objType, List fieldTokens) + { + checkObjectIsInsertable(objType); + for (SObjectField fieldToken : fieldTokens) + { + checkFieldIsInsertable(objType, fieldToken); + } + } + + /** + * Checks both read FLS and CRUD for the specified object type and fields. + * @exception FlsException if the running user does not have read rights to any fields in {@code fieldNames}. + * @exception CrudException if the running user does not have read rights to {@code objType} + **/ + public static void checkRead(SObjectType objType, List fieldNames) + { + checkObjectIsReadable(objType); + for (String fieldName : fieldNames) + { + checkFieldIsReadable(objType, fieldName); + } + } + + /** + * Identical to {@link #checkRead(SObjectType,List)}, except with SObjectField instead of String field references. + * @exception FlsException if the running user does not have read rights to any fields in {@code fieldTokens}. + * @exception CrudException if the running user does not have read rights to {@code objType} + **/ + public static void checkRead(SObjectType objType, List fieldTokens) + { + checkObjectIsReadable(objType); + for (SObjectField fieldToken : fieldTokens) + { + checkFieldIsReadable(objType, fieldToken); + } + } + + /** + * Checks both update FLS and CRUD for the specified object type and fields. + * @exception FlsException if the running user does not have update rights to any fields in {@code fieldNames}. + * @exception CrudException if the running user does not have update rights to {@code objType} + **/ + public static void checkUpdate(SObjectType objType, List fieldNames) + { + checkObjectIsUpdateable(objType); + for (String fieldName : fieldNames) + { + checkFieldIsUpdateable(objType, fieldName); + } + } + + /** + * Identical to {@link #checkUpdate(SObjectType,List)}, except with SObjectField instead of String field references. + * @exception FlsException if the running user does not have update rights to any fields in {@code fieldTokens}. + * @exception CrudException if the running user does not have update rights to {@code objType} + **/ + public static void checkUpdate(SObjectType objType, List fieldTokens) + { + checkObjectIsUpdateable(objType); + for (SObjectField fieldToken : fieldTokens) + { + checkFieldIsUpdateable(objType, fieldToken); + } + } + + /** + * CheckFieldIs* method check only FLS + **/ + + /** + * Checks insert field level security only (no CRUD) for the specified fields on {@code objType} + * @exception FlsException if the running user does not have insert rights to the {@code fieldName} field. + **/ + public static void checkFieldIsInsertable(SObjectType objType, String fieldName) + { + checkFieldIsInsertable(objType, fflib_SObjectDescribe.getDescribe(objType).getField(fieldName)); + } + + /** + * Identical to {@link #checkFieldIsInsertable(SObjectType,String)}, except with SObjectField instead of String field reference. + * @exception FlsException if the running user does not have insert rights to the {@code fieldName} field. + **/ + public static void checkFieldIsInsertable(SObjectType objType, SObjectField fieldToken) + { + checkFieldIsInsertable(objType, fieldToken.getDescribe()); + } + + /** + * Identical to {@link #checkFieldIsInsertable(SObjectType,String)}, except with DescribeFieldResult instead of String field reference. + * @exception FlsException if the running user does not have insert rights to the {@code fieldName} field. + **/ + public static void checkFieldIsInsertable(SObjectType objType, DescribeFieldResult fieldDescribe) + { + if (BYPASS_INTERNAL_FLS_AND_CRUD) + return; + if (!fieldDescribe.isCreateable()) + throw new FlsException(OperationType.CREATE, objType, fieldDescribe.getSObjectField()); + } + + /** + * Checks read field level security only (no CRUD) for the specified fields on {@code objType} + * @exception FlsException if the running user does not have read rights to the {@code fieldName} field. + **/ + public static void checkFieldIsReadable(SObjectType objType, String fieldName) + { + checkFieldIsReadable(objType, fflib_SObjectDescribe.getDescribe(objType).getField(fieldName)); + } + + /** + * Identical to {@link #checkFieldIsReadable(SObjectType,String)}, except with SObjectField instead of String field reference. + * @exception FlsException if the running user does not have read rights to the {@code fieldName} field. + **/ + public static void checkFieldIsReadable(SObjectType objType, SObjectField fieldToken) + { + checkFieldIsReadable(objType, fieldToken.getDescribe()); + } + + /** + * Identical to {@link #checkFieldIsReadable(SObjectType,String)}, except with DescribeFieldResult instead of String field reference. + * @exception FlsException if the running user does not have read rights to the {@code fieldName} field. + **/ + public static void checkFieldIsReadable(SObjectType objType, DescribeFieldResult fieldDescribe) + { + if (BYPASS_INTERNAL_FLS_AND_CRUD) + return; + if (!fieldDescribe.isAccessible()) + throw new FlsException(OperationType.READ, objType, fieldDescribe.getSObjectField()); + } + + + /** + * Checks update field level security only (no CRUD) for the specified fields on {@code objType} + * @exception FlsException if the running user does not have update rights to the {@code fieldName} field. + **/ + public static void checkFieldIsUpdateable(SObjectType objType, String fieldName) + { + checkFieldIsUpdateable(objType, fflib_SObjectDescribe.getDescribe(objType).getField(fieldName)); + } + + /** + * Identical to {@link #checkFieldIsUpdateable(SObjectType,String)}, except with SObjectField instead of String field reference. + * @exception FlsException if the running user does not have update rights to the {@code fieldName} field. + **/ + public static void checkFieldIsUpdateable(SObjectType objType, SObjectField fieldToken) + { + checkFieldIsUpdateable(objType, fieldToken.getDescribe()); + } + + /** + * Identical to {@link #checkFieldIsUpdateable(SObjectType,String)}, except with DescribeFieldResult instead of String field reference. + * @exception FlsException if the running user does not have update rights to the {@code fieldName} field. + **/ + public static void checkFieldIsUpdateable(SObjectType objType, DescribeFieldResult fieldDescribe) + { + if (BYPASS_INTERNAL_FLS_AND_CRUD) + return; + if (!fieldDescribe.isUpdateable()) + throw new FlsException(OperationType.MODIFY, objType, fieldDescribe.getSObjectField()); + } + + /** + * CheckObjectIs* methods check only CRUD + **/ + + /** + * Checks insert CRUD for the specified object type. + * @exception CrudException if the running user does not have insert rights to the {@code objType} SObject. + **/ + public static void checkObjectIsInsertable(SObjectType objType) + { + if (BYPASS_INTERNAL_FLS_AND_CRUD) + return; + if (!objType.getDescribe().isCreateable()) + { + throw new CrudException(OperationType.CREATE, objType); + } + } + + /** + * Checks read CRUD for the specified object type. + * @exception CrudException if the running user does not have read rights to the {@code objType} SObject. + **/ + public static void checkObjectIsReadable(SObjectType objType) + { + if (BYPASS_INTERNAL_FLS_AND_CRUD) + return; + if (!objType.getDescribe().isAccessible()) + throw new CrudException(OperationType.READ, objType); + } + + /** + * Checks update CRUD for the specified object type. + * @exception CrudException if the running user does not have update rights to the {@code objType} SObject. + **/ + public static void checkObjectIsUpdateable(SObjectType objType) + { + if (BYPASS_INTERNAL_FLS_AND_CRUD) + return; + if (!objType.getDescribe().isUpdateable()) + throw new CrudException(OperationType.MODIFY, objType); + } + + /** + * Checks delete CRUD for the specified object type. + * @exception CrudException if the running user does not have delete rights to the {@code objType} SObject. + **/ + public static void checkObjectIsDeletable(SObjectType objType) + { + if (BYPASS_INTERNAL_FLS_AND_CRUD) + return; + if (!objType.getDescribe().isDeletable()) + throw new CrudException(OperationType.DEL, objType); + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_SecurityUtils.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_SecurityUtils.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_SecurityUtils.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_StringBuilder.cls b/force-app/infrastructure/apex-common/main/classes/fflib_StringBuilder.cls new file mode 100644 index 00000000000..cac2e8ab2a0 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_StringBuilder.cls @@ -0,0 +1,157 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + * Helper class, roughly based on the Java version, but subclassed to assist in a number of use cases in this library + * + * NOTE: Aspects of this where developed before recent improvements to String handling, as such could likely be enhanced at this stage. + **/ +public virtual class fflib_StringBuilder +{ + protected List buffer = new List(); + + /** + * Construct an empty StringBuilder + **/ + public fflib_StringBuilder() {} + + /** + * Construct a StringBuilder with the given values + **/ + public fflib_StringBuilder(List values) + { + add(values); + } + + /** + * Add the given values to the StringBuilder + **/ + public virtual void add(List values) + { + buffer.addAll(values); + } + + /** + * Add the given value to the StringBuilder + **/ + public virtual void add(String value) + { + buffer.add(value); + } + + public virtual override String toString() + { + return String.join(buffer, ''); + } + + /** + * Return the state of the StringBuilder + **/ + public virtual String getStringValue() + { + return toString(); + } + + /** + * Subclasses the StringBuilder to produce a comma delimited concatenation of strings + **/ + public virtual with sharing class CommaDelimitedListBuilder extends fflib_StringBuilder + { + String itemPrefix = ''; + String delimiter = ','; + + public CommaDelimitedListBuilder() {} + + public CommaDelimitedListBuilder(List values) + { + super(values); + } + + public void setItemPrefix(String itemPrefix) + { + this.itemPrefix = itemPrefix; + } + + public void setDelimiter(String delimiter) + { + this.delimiter = delimiter; + } + + public String getStringValue(String itemPrefix) + { + setItemPrefix(itemPrefix); + return toString(); + } + + public override String toString() + { + return itemPrefix + String.join(buffer, delimiter + itemPrefix); + } + } + + /** + * Subclasses the StringCommaDelimitedBuilder to accept native SObjectField tokens and optional FieldSet definitions to concatinate when building queries + **/ + public virtual with sharing class FieldListBuilder extends CommaDelimitedListBuilder + { + public FieldListBuilder(List values) + { + this(values, null); + } + + public FieldListBuilder(List values, List fieldSets) + { + // Create a distinct set of fields (or field paths) to select + for(Schema.SObjectField value : values) + add(String.valueOf(value)); // Alternative to value.getDescribe().getName() + + if(fieldSets!=null) + for(Schema.Fieldset fieldSet : fieldSets) + for(Schema.FieldSetMember fieldSetMember : fieldSet.getFields()) + add(fieldSetMember.getFieldPath()); + } + } + + /** + * Subclasses the FieldListBuilder to auto sense and include when needed the CurrencyIsoCode field in the field list + **/ + public with sharing class MultiCurrencyFieldListBuilder extends FieldListBuilder + { + public MultiCurrencyFieldListBuilder(List values) + { + this(values, null); + } + + public MultiCurrencyFieldListBuilder(List values, List fieldSets) + { + super(values, fieldSets); + + // Dynamically add CurrencyIsoCode field for multi-currency organisations + if(UserInfo.isMultiCurrencyOrganization()) + add('CurrencyIsoCode'); + } + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/main/classes/fflib_StringBuilder.cls-meta.xml b/force-app/infrastructure/apex-common/main/classes/fflib_StringBuilder.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/main/classes/fflib_StringBuilder.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/main/labels/fflib-Apex-Common-CustomLabels.labels-meta.xml b/force-app/infrastructure/apex-common/main/labels/fflib-Apex-Common-CustomLabels.labels-meta.xml new file mode 100644 index 00000000000..d4002b0a0d3 --- /dev/null +++ b/force-app/infrastructure/apex-common/main/labels/fflib-Apex-Common-CustomLabels.labels-meta.xml @@ -0,0 +1,86 @@ + + + + fflib_QueryFactory_crossobject_fieldsets_not_allowed_error + en_US + false + Error when selecting a cross-object fieldsset is not allowed and attempted. + Cross-object fields not allowed and field "{0}" is a cross-object field. + + + fflib_QueryFactory_fieldset_wrong_sobject_error + en_US + false + Error when selecting a field set for a different sobject type. + Field set "{0}" is not for SObject type "{1}" + + + fflib_QueryFactory_recursive_subquery_error + en_US + false + Presented when attempting to nest subqueries. + Invalid call to subselectQuery. You may not add a subselect query to a subselect query. + + + fflib_QueryFactory_subquery_invalid_relationship + en_US + false + Presented when attempting to create a subquery from an invalid relationship. + Invalid call to subselectQuery. Invalid relationship for table. + + + fflib_security_error_field_not_insertable + security,error + en_US + true + fflib_security_error_field_not_insertable + You do not have permission to insert field {1} on {0} + + + fflib_security_error_field_not_readable + security,error + en_US + true + fflib_security_error_field_not_readable + You do not have permission to read the field {1} on {0} + + + fflib_security_error_field_not_updateable + security,error + en_US + true + fflib_security_error_field_not_updateable + You do not have permission to update the field {1} on {0} + + + fflib_security_error_object_not_deletable + en_US + true + fflib_security_error_object_not_deletable + You do not have permission to delete {0} + + + fflib_security_error_object_not_insertable + security,error + en_US + true + fflib_security_error_object_not_insertable + You do not have permission to insert {0} + + + fflib_security_error_object_not_readable + security,error + en_US + true + fflib_security_error_object_not_readable + You do not have permission to read {0} + + + fflib_security_error_object_not_updateable + security,error + en_US + true + fflib_security_error_object_not_updateable + You do not have permission to update {0} + + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_ApplicationTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_ApplicationTest.cls new file mode 100644 index 00000000000..40ec2ffc4f8 --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_ApplicationTest.cls @@ -0,0 +1,166 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@IsTest +private class fflib_ApplicationTest +{ + @IsTest + private static void callingUnitOfWorkFactoryShouldGivenStandardImplsAndMockImpls() + { + // Standard behaviour + System.assert(UnitOfWork.newInstance() instanceof fflib_SObjectUnitOfWork); + + // Mocking behaviour + UnitOfWork.setMock(new fflib_SObjectMocks.SObjectUnitOfWork(new fflib_ApexMocks())); + System.assert(UnitOfWork.newInstance() instanceof fflib_SObjectMocks.SObjectUnitOfWork); + } + + @IsTest + private static void callingUnitOfWorkFactoryWithCustomTypesShouldGivenStandardImplsAndMockImpls() + { + // Standard behaviour + System.assert( + UnitOfWork.newInstance( + new List{ Account.SObjectType} + ) instanceof fflib_SObjectUnitOfWork); + + // Mocking behaviour + UnitOfWork.setMock(new fflib_SObjectMocks.SObjectUnitOfWork(new fflib_ApexMocks())); + System.assert( + UnitOfWork.newInstance( + new List{ Account.SObjectType} + ) instanceof fflib_SObjectMocks.SObjectUnitOfWork); + } + + @IsTest + private static void callingUnitOfWorkWithCustomDML() + { + // Given a custom DML class and a new record + CustomDML customDML = new CustomDML(); + Account myAccount = new Account(Name = 'Test Account'); + + // When the unit of work is instantiated from the Application Class and the record is registered and commited + fflib_ISObjectUnitOfWork unitOfWork = UnitOfWork.newInstance(customDML); + unitOfWork.registerNew(myAccount); + unitOfWork.commitWork(); + + // Then the Custom DML is used by the unit of Work + System.assert(customDML.isInsertCalled, 'Oops, custom DML was not called'); + } + + @IsTest + private static void callingMockedUnitOfWorkWithCustomDML() + { + // Given a custom DML class and a new record + CustomDML customDML = new CustomDML(); + Account myAccount = new Account(Name = 'Test Account'); + + // When the unit of work is instantiated from the Application Class and the record is registered and commited + UnitOfWork.setMock(new fflib_SObjectMocks.SObjectUnitOfWork(new fflib_ApexMocks())); + fflib_ISObjectUnitOfWork uow = UnitOfWork.newInstance(customDML); + + uow.registerNew(myAccount); + uow.commitWork(); + + // Then the Custom DML should not be used by the unit of Work + System.assert(!customDML.isInsertCalled, 'Oops, custom DML was called'); + } + + @IsTest + private static void callingUnitOfWorkWithCustomObjectTypesAndDML() + { + // Given a custom DML class and a new record + CustomDML customDML = new CustomDML(); + Account myAccount = new Account(Name = 'Test Account'); + + // When the unit of work is instantiated from the Application Class and the record is registered and commited + fflib_ISObjectUnitOfWork unitOfWork = UnitOfWork.newInstance( + new List{ Account.SObjectType }, + customDML + ); + unitOfWork.registerNew(myAccount); + unitOfWork.commitWork(); + + // Then the Custom DML is used by the unit of Work + System.assert(customDML.isInsertCalled, 'Oops, custom DML was not called'); + } + + @IsTest + private static void callingMockedUnitOfWorkWithCustomObjectTypesAndDML() + { + // Given a custom DML class and a new record + CustomDML customDML = new CustomDML(); + Account myAccount = new Account(Name = 'Test Account'); + + // When the unit of work is instantiated from the Application Class and the record is registered and commited + UnitOfWork.setMock(new fflib_SObjectMocks.SObjectUnitOfWork(new fflib_ApexMocks())); + fflib_ISObjectUnitOfWork uow = UnitOfWork.newInstance( + new List{ Account.SObjectType }, + customDML + ); + uow.registerNew(myAccount); + uow.commitWork(); + + // Then the Custom DML should not be used by the unit of Work + System.assert(!customDML.isInsertCalled, 'Oops, custom DML was called'); + } + + public class CustomDML implements fflib_SObjectUnitOfWork.IDML + { + public boolean isInsertCalled = false; + public boolean isUpdateCalled = false; + public boolean isDeleteCalled = false; + public boolean isPublishCalled = false; + public Boolean isEmptyRecycleBinCalled = false; + + public void dmlInsert(List objList){ + this.isInsertCalled = true; + } + public void dmlUpdate(List objList){ + this.isUpdateCalled = true; + } + public void dmlDelete(List objList){ + this.isDeleteCalled = true; + } + public void eventPublish(List objList) + { + this.isPublishCalled = true; + } + public void emptyRecycleBin(List objList) + { + this.isEmptyRecycleBinCalled = true; + } + } + + // Configure and create the UnitOfWorkFactory for this Application + public static final fflib_Application.UnitOfWorkFactory UnitOfWork = + new fflib_Application.UnitOfWorkFactory( + new List { + Account.SObjectType, + Opportunity.SObjectType, + OpportunityLineItem.SObjectType }); + +} diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_ApplicationTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_ApplicationTest.cls-meta.xml new file mode 100644 index 00000000000..ecd550068a4 --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_ApplicationTest.cls-meta.xml @@ -0,0 +1,4 @@ + + + 53.0 + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_ObjectsTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_ObjectsTest.cls new file mode 100644 index 00000000000..f565afa3ebc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_ObjectsTest.cls @@ -0,0 +1,193 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@IsTest +private class fflib_ObjectsTest +{ + private static final fflib_ObjectsTest.DataTransferObject TRANSFER_OBJECT_A = new DataTransferObject('Test A'); + private static final fflib_ObjectsTest.DataTransferObject TRANSFER_OBJECT_B = new DataTransferObject('Test B'); + private static final fflib_ObjectsTest.DataTransferObject TRANSFER_OBJECT_C = new DataTransferObject('Test C'); + private static final fflib_ObjectsTest.DataTransferObject TRANSFER_OBJECT_D = new DataTransferObject('Test D'); + + @IsTest + static void itShouldReturnTheCorrectType() + { + fflib_ObjectsTest.Domain dtoDomain = new Domain( + new List + { + TRANSFER_OBJECT_A, + TRANSFER_OBJECT_B + } + ); + + System.assertEquals( + DataTransferObject.class, + dtoDomain.getType(), + 'Wrong domain type' + ); + } + + @IsTest + static void itShouldContainTheObject() + { + fflib_ObjectsTest.DataTransferObject transferObject = TRANSFER_OBJECT_A; + fflib_ObjectsTest.Domain dtoDomain = new Domain( + new List + { + transferObject, + TRANSFER_OBJECT_B + } + ); + + System.assert( + dtoDomain.contains(transferObject), + 'The object should have been part of the domain' + ); + } + + @IsTest + static void itShouldNotContainTheObject() + { + fflib_ObjectsTest.Domain dtoDomain = generateDomain(); + + System.assert( + dtoDomain.containsNot(TRANSFER_OBJECT_D), + 'The object should not have been part of the domain' + ); + } + + @IsTest + static void itShouldNotContainTheObjects() + { + fflib_ObjectsTest.Domain dtoDomain = new Domain( + new List + { + TRANSFER_OBJECT_A, + TRANSFER_OBJECT_B + }); + + System.assert( + dtoDomain.containsNot( + new Set + { + TRANSFER_OBJECT_C, + TRANSFER_OBJECT_D + }), + 'The set of objects should not have been part of the domain' + ); + System.assert( + dtoDomain.containsNot( + new List + { + TRANSFER_OBJECT_C, + TRANSFER_OBJECT_D + }), + 'The list of objects should not have been part of the domain' + ); + } + + @IsTest + static void itShouldHaveAnEmptyDomain() + { + fflib_ObjectsTest.Domain dtoDomain = new Domain(new List()); + System.assert(dtoDomain.isEmpty(), 'Domain should be empty'); + } + + @IsTest + static void itShouldNotBeAnEmptyDomain() + { + fflib_ObjectsTest.Domain dtoDomain = generateDomain(); + System.assert(dtoDomain.isNotEmpty(), 'Domain should not be empty'); + } + + + @IsTest + static void itShouldContainAllTheObjects() + { + fflib_ObjectsTest.Domain dtoDomain = generateDomain(); + + System.assert( + dtoDomain.containsAll( + new List + { + TRANSFER_OBJECT_A, + TRANSFER_OBJECT_B + } + ), + 'Domain should contain the whole List of objects' + ); + System.assert( + dtoDomain.containsAll( + new Set + { + TRANSFER_OBJECT_A, + TRANSFER_OBJECT_B + } + ), + 'Domain should contain the whole Set of objects' + ); + } + + private static Domain generateDomain() + { + return new Domain(generateDataTransferObjects()); + } + + + private static List generateDataTransferObjects() + { + return new List + { + TRANSFER_OBJECT_A, + TRANSFER_OBJECT_B, + TRANSFER_OBJECT_C + }; + } + + + private class Domain extends fflib_Objects + { + public Domain(List objects) + { + super(objects); + } + + public override Object getType() + { + return DataTransferObject.class; + } + } + + private class DataTransferObject + { + public String MyProperty { get; set; } + + public DataTransferObject(String property) + { + this.MyProperty = property; + } + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_ObjectsTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_ObjectsTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_ObjectsTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_QueryFactoryTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_QueryFactoryTest.cls new file mode 100644 index 00000000000..2e4ce67540e --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_QueryFactoryTest.cls @@ -0,0 +1,735 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@isTest( isParallel = true ) +private class fflib_QueryFactoryTest { + + @isTest + static void fieldSelections(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('firstName'); + qf.selectField(Schema.Contact.SObjectType.fields.lastName); + qf.selectFields( new Set{'acCounTId', 'account.name'} ); + qf.selectFields( new List{'homePhonE','fAX'} ); + qf.selectFields( new List{ Contact.Email, Contact.Title } ); + System.assertEquals(new Set{ + 'FirstName', + 'LastName', + 'AccountId', + 'Account.Name', + 'HomePhone', + 'Fax', + 'Email', + 'Title'}, + qf.getSelectedFields()); + } + + @isTest + static void simpleFieldSelection() { + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('NAMe').selectFields( new Set{'naMe', 'email'}); + String query = qf.toSOQL(); + System.assert( Pattern.matches('SELECT.*Name.*FROM.*',query), 'Expected Name field in query, got '+query); + System.assert( Pattern.matches('SELECT.*Email.*FROM.*',query), 'Expected Name field in query, got '+query); + qf.setLimit(100); + System.assertEquals(100,qf.getLimit()); + System.assert( qf.toSOQL().endsWithIgnoreCase('LIMIT '+qf.getLimit()), 'Failed to respect limit clause:'+qf.toSOQL() ); + } + + @isTest + static void simpleFieldCondition(){ + String whereClause = 'name = \'test\''; + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + qf.selectField('email'); + qf.setCondition( whereClause ); + System.assertEquals(whereClause,qf.getCondition()); + String query = qf.toSOQL(); + System.assert(query.endsWith('WHERE name = \'test\''),'Query should have ended with a filter on name, got: '+query); + } + + @isTest + static void duplicateFieldSelection() { + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('NAMe').selectFields( new Set{'naMe', 'email'}); + String query = qf.toSOQL(); + System.assertEquals(1, query.countMatches('Name'), 'Expected one name field in query: '+query ); + } + + @isTest + static void equalityCheck(){ + fflib_QueryFactory qf1 = new fflib_QueryFactory(Contact.SObjectType); + fflib_QueryFactory qf2 = new fflib_QueryFactory(Contact.SObjectType); + System.assertEquals(qf1,qf2); + qf1.selectField('name'); + System.assertNotEquals(qf1,qf2); + qf2.selectField('NAmE'); + System.assertEquals(qf1,qf2); + qf1.selectField('name').selectFields( new Set{ 'NAME', 'name' }).selectFields( new Set{ Contact.Name, Contact.Name} ); + System.assertEquals(qf1,qf2); + } + + @isTest + static void nonReferenceField(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + fflib_QueryFactory.NonReferenceFieldException e; + try{ + qf.selectField('name.title'); + }catch(fflib_QueryFactory.NonReferenceFieldException ex){ + e = ex; + } + System.assertNotEquals(null,e,'Cross-object notation on a non-reference field should throw NonReferenceFieldException.'); + } + + @isTest + static void invalidCrossObjectField(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + fflib_QueryFactory.InvalidFieldException e; + try{ + qf.selectField('account.NOT_A_REAL_FIELD'); + }catch(fflib_QueryFactory.InvalidFieldException ex){ + e = ex; + } + System.assertNotEquals(null,e,'Cross-object notation on a non-reference field should throw NonReferenceFieldException.'); + } + + @isTest + static void invalidFieldTests(){ + List exceptions = new List(); + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + try{ + qf.selectField('Not_a_field'); + }catch(fflib_QueryFactory.InvalidFieldException e){ + exceptions.add(e); + } + try{ + qf.selectFields( new Set{ 'Not_a_field','alsoNotreal'}); + }catch(fflib_QueryFactory.InvalidFieldException e){ + exceptions.add(e); + } + try{ + qf.selectFields( new Set{ null }); + }catch(fflib_QueryFactory.InvalidFieldException e){ + exceptions.add(e); + } + try{ + qf.selectFields( new List{ null, Contact.title }); + }catch(fflib_QueryFactory.InvalidFieldException e){ + exceptions.add(e); + } + System.assertEquals(4,exceptions.size()); + } + + @isTest + static void ordering(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + qf.selectField('email'); + qf.setCondition( 'name = \'test\'' ); + qf.addOrdering( new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING) ).addOrdering( new fflib_QueryFactory.Ordering('Contact','CreatedDATE',fflib_QueryFactory.SortOrder.DESCENDING) ); + String query = qf.toSOQL(); + + System.assertEquals(2,qf.getOrderings().size()); + System.assertEquals('Name',qf.getOrderings()[0].getField() ); + System.assertEquals(fflib_QueryFactory.SortOrder.DESCENDING,qf.getOrderings()[1].getDirection() ); + + + System.assert( Pattern.matches('SELECT.*Name.*FROM.*',query), 'Expected Name field in query, got '+query); + System.assert( Pattern.matches('SELECT.*Email.*FROM.*',query), 'Expected Name field in query, got '+query); + } + + @isTest + static void setOrdering_ReplacesPreviousOrderingsWithExpectedOrdering(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + qf.selectField('email'); + qf.setCondition( 'name = \'test\'' ); + + //test base method with ordeting by OwnerId Descending + qf.setOrdering( new fflib_QueryFactory.Ordering('Contact','OwnerId',fflib_QueryFactory.SortOrder.DESCENDING) ); + + System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace default Orderings'); + System.assertEquals(Contact.OwnerId.getDescribe().getName(), qf.getOrderings()[0].getField(), 'Unexpected order field - should have been resolved from the field OwnerId'); + System.assertEquals(fflib_QueryFactory.SortOrder.DESCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.'); + + //test method overload with ordering by LastModifiedDate Ascending + qf.setOrdering('LastModifiedDate', fflib_QueryFactory.SortOrder.ASCENDING, true); + + System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace previous Orderings'); + System.assertEquals(Contact.LastModifiedDate.getDescribe().getName(), qf.getOrderings()[0].getField(), 'Unexpected order field - should have been resolved from the field LastModifiedDate'); + System.assertEquals(fflib_QueryFactory.SortOrder.ASCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.'); + + //test method overload with ordering by CreatedDate Descending + qf.setOrdering(Contact.CreatedDate, fflib_QueryFactory.SortOrder.DESCENDING, true); + + System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace previous Orderings'); + System.assertEquals(Contact.CreatedDate.getDescribe().getName(), qf.getOrderings()[0].getField(), 'Unexpected order field - should have been resolved from the field CreatedDate'); + System.assertEquals(fflib_QueryFactory.SortOrder.DESCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.'); + + //test method overload with ordering by CreatedBy.Name Descending + qf.setOrdering('CreatedBy.Name', fflib_QueryFactory.SortOrder.DESCENDING); + + System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace previous Orderings'); + System.assertEquals(fflib_QueryFactory.SortOrder.DESCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.'); + + //test method overload with ordering by Birthdate Ascending + qf.setOrdering(Contact.Birthdate, fflib_QueryFactory.SortOrder.ASCENDING); + + System.assertEquals(1, qf.getOrderings().size(), 'Unexpected order size - setOrder should replace previous Orderings'); + System.assertEquals(Contact.Birthdate.getDescribe().getName(), qf.getOrderings()[0].getField(), 'Unexpected order field - should have been resolved from the field Birthdate'); + System.assertEquals(fflib_QueryFactory.SortOrder.ASCENDING, qf.getOrderings()[0].getDirection(), 'Unexpected order direction.'); + + String query = qf.toSOQL(); + } + + @isTest + static void invalidField_string(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + Exception e; + try{ + qf.selectField('not_a__field'); + }catch(fflib_QueryFactory.InvalidFieldException ex){ + e = ex; + } + System.assertNotEquals(null,e); + } + + @isTest + static void invalidFields_string(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + Exception e; + try{ + qf.selectFields( new List{'not_a__field'} ); + }catch(fflib_QueryFactory.InvalidFieldException ex){ + e = ex; + } + System.assertNotEquals(null,e); + } + + @isTest + static void invalidField_nullToken(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + Exception e; + Schema.SObjectField token = null; + try{ + qf.selectField( token ); + }catch(fflib_QueryFactory.InvalidFieldException ex){ + e = ex; + } + System.assertNotEquals(null,e); + } + + @isTest + static void invalidFields_nullToken(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + Exception e; + List token = new List{ + null + }; + try{ + qf.selectFields( token ); + }catch(fflib_QueryFactory.InvalidFieldException ex){ + e = ex; + } + System.assertNotEquals(null,e); + } + + @isTest + static void invalidFields_noQueryField(){ + try { + String path = fflib_QueryFactory.getFieldTokenPath(null); + System.assert(false,'Expected InvalidFieldException; none was thrown'); + } + catch (fflib_QueryFactory.InvalidFieldException ife) { + //Expected + } + catch (Exception e){ + System.assert(false,'Expected InvalidFieldException; ' + e.getTypeName() + ' was thrown instead: ' + e); + } + } + + @isTest + static void queryFieldsNotEquals(){ + String qfld = fflib_QueryFactory.getFieldTokenPath(Contact.Name); + String qfld2 = fflib_QueryFactory.getFieldTokenPath(Contact.LastName); + System.assert(!qfld.equals(qfld2)); + } + + @isTest + static void addChildQueriesWithChildRelationship_success(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name').selectField('Id').setCondition('name=\'test\'').addOrdering('CreatedDate', fflib_QueryFactory.SortOrder.DESCENDING, true); + Schema.DescribeSObjectResult descResult = Contact.SObjectType.getDescribe(); + //explicitly assert object accessibility when creating the subselect + qf.subselectQuery('Tasks', true).selectField('Id').selectField('Subject').setCondition(' IsDeleted = false '); + List queries = qf.getSubselectQueries(); + System.assert(queries != null); + System.assert( + Pattern.matches('SELECT.*(SELECT.*FROM Tasks WHERE.*).*FROM Contact WHERE.*', qf.toSOQL()), + 'Incorrect returned query' + ); + } + + @isTest + static void addChildQueriesWithChildRelationshipNoAccessibleCheck_success(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name').selectField('Id').setCondition('name=\'test\'').addOrdering('CreatedDate', fflib_QueryFactory.SortOrder.DESCENDING, true); + //explicitly assert object accessibility when creating the subselect + qf.subselectQuery('Tasks').selectField('Id').selectField('Subject').setCondition(' IsDeleted = false '); + List queries = qf.getSubselectQueries(); + System.assert(queries != null); + System.assert( + Pattern.matches('SELECT.*(SELECT.*FROM Tasks WHERE.*).*FROM Contact WHERE.*', qf.toSOQL()), + 'Incorrect returned query' + ); + } + + @isTest + static void addChildQueriesWithChildRelationshipObjCheckIsAccessible_success(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name').selectField('Id').setCondition('name=\'test\'').addOrdering('CreatedDate', fflib_QueryFactory.SortOrder.DESCENDING, true); + Schema.DescribeSObjectResult descResult = Contact.SObjectType.getDescribe(); + Schema.ChildRelationship relationship; + for (Schema.ChildRelationship childRow : descResult.getChildRelationships()){ + //occasionally on some standard objects (Like Contact child of Contact) do not have a relationship name. + //if there is no relationship name, we cannot query on it, so throw an exception. + if (childRow.getRelationshipName() == 'Tasks'){ + relationship = childRow; + } + } + //explicitly assert object accessibility when creating the subselect + qf.subselectQuery(relationship, true).selectField('Id').selectField('Subject').setCondition(' IsDeleted = false '); + List queries = qf.getSubselectQueries(); + System.assert(queries != null); + System.assert( + Pattern.matches('SELECT.*(SELECT.*FROM Tasks WHERE.*).*FROM Contact WHERE.*', qf.toSOQL()), + 'Incorrect returned query' + ); + } + + @isTest + static void addChildQueriesWithChildRelationshipObj_success(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name').selectField('Id').setCondition( 'name=\'%test%\'' ).addOrdering('CreatedDate',fflib_QueryFactory.SortOrder.DESCENDING, true); + Schema.DescribeSObjectResult descResult = Contact.SObjectType.getDescribe(); + Schema.ChildRelationship relationship; + for (Schema.ChildRelationship childRow : descResult.getChildRelationships()){ + //occasionally on some standard objects (Like Contact child of Contact) do not have a relationship name. + //if there is no relationship name, we cannot query on it, so throw an exception. + if (childRow.getRelationshipName() == 'Tasks'){ + relationship = childRow; + } + } + //explicitly assert object accessibility when creating the subselect + qf.subselectQuery(relationship).selectField('Id').selectField('Subject').setCondition(' IsDeleted = false '); + List queries = qf.getSubselectQueries(); + System.assert(queries != null); + System.assert( + Pattern.matches('SELECT.*(SELECT.*FROM Tasks WHERE.*).*FROM Contact WHERE.*', qf.toSOQL()), + 'Incorrect returned query' + ); + } + + @isTest + static void addChildQueriesWithChildRelationshipNoAccessibleCheck_fail(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name').selectField('Id').setCondition( 'name=\'test\'' ).addOrdering('CreatedDate',fflib_QueryFactory.SortOrder.DESCENDING, true); + Schema.DescribeSObjectResult descResult = Contact.SObjectType.getDescribe(); + //explicitly assert object accessibility when creating the subselect + // + Exception e; + try { + qf.subselectQuery('Tas').selectField('Id').selectField('Subject').setCondition(' IsDeleted = false '); + } catch (fflib_QueryFactory.InvalidSubqueryRelationshipException ex) { + e = ex; + } + System.assertNotEquals(e, null); + } + + @isTest + static void addChildQueries_success(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name').selectField('Id').setCondition( 'name like \'%test%\'' ).addOrdering('CreatedDate',fflib_QueryFactory.SortOrder.DESCENDING, true); + Schema.DescribeSObjectResult descResult = Contact.SObjectType.getDescribe(); + //explicitly assert object accessibility when creating the subselect + qf.subselectQuery(Task.SObjectType, true).selectField('Id').selectField('Subject').setCondition(' IsDeleted = false '); + List queries = qf.getSubselectQueries(); + System.assert(queries != null); + System.assert( + Pattern.matches('SELECT.*(SELECT.*FROM Tasks WHERE.*).*FROM Contact WHERE.*', qf.toSOQL()), + 'Incorrect returned query' + ); + } + + @isTest + static void addChildQuerySameRelationshipAgain_success(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + qf.selectField('Id'); + qf.setCondition( 'name like \'%test%\'' ); + qf.addOrdering( new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING) ).addOrdering('CreatedBy.Name',fflib_QueryFactory.SortOrder.DESCENDING); + Schema.DescribeSObjectResult descResult = Contact.SObjectType.getDescribe(); + Schema.ChildRelationship relationship; + for (Schema.ChildRelationship childRow : descResult.getChildRelationships()) { + if (childRow.getRelationshipName() == 'Tasks') { + relationship = childRow; + } + } + System.assert(qf.getSubselectQueries() == null); + fflib_QueryFactory childQf = qf.subselectQuery(Task.SObjectType); + childQf.assertIsAccessible(); + childQf.setEnforceFLS(true); + childQf.selectField('Id'); + fflib_QueryFactory childQf2 = qf.subselectQuery(Task.SObjectType); + List queries = qf.getSubselectQueries(); + System.assert(queries != null); + System.assert(queries.size() == 1); + } + + @isTest + static void addChildQueries_invalidChildRelationship(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + qf.selectField('email'); + qf.setCondition( 'name like \'%test%\'' ); + qf.addOrdering( new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING) ).addOrdering( 'CreatedDATE',fflib_QueryFactory.SortOrder.DESCENDING); + Schema.DescribeSObjectResult descResult = Account.SObjectType.getDescribe(); + Exception e; + try { + SObjectType invalidType = null; + fflib_QueryFactory childQf = qf.subselectQuery(invalidType); + childQf.selectField('Id'); + } catch (fflib_QueryFactory.InvalidSubqueryRelationshipException ex) { + e = ex; + } + System.assertNotEquals(e, null); + } + + @isTest + static void addChildQueries_invalidChildRelationshipTooDeep(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.selectField('name'); + qf.selectField('email'); + qf.setCondition( 'name like \'%test%\'' ); + qf.addOrdering( new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING) ).addOrdering('CreatedDATE',fflib_QueryFactory.SortOrder.DESCENDING); + Schema.DescribeSObjectResult descResult = Contact.SObjectType.getDescribe(); + + fflib_QueryFactory childQf = qf.subselectQuery(Task.SObjectType); + childQf.selectField('Id'); + childQf.selectField('Subject'); + Exception e; + try { + fflib_QueryFactory subChildQf = childQf.subselectQuery(Task.SObjectType); + } catch (fflib_QueryFactory.InvalidSubqueryRelationshipException ex) { + e = ex; + } + System.assertNotEquals(e, null); + } + + @isTest + static void checkFieldObjectReadSort_success(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.assertIsAccessible() + .setEnforceFLS(true) + .selectField('createdby.name') + .selectField(Contact.LastModifiedById) + .selectFields(new List{Contact.LastModifiedDate}) + .setEnforceFLS(false) + .selectField(Contact.LastName) + .selectFields(new List{Contact.Id}) + .setCondition( 'name like \'%test%\'' ) + .setEnforceFLS(true) + .selectFields(new Set{Contact.FirstName}) + .addOrdering(new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING) ) + .addOrdering(Contact.LastModifiedDate,fflib_QueryFactory.SortOrder.DESCENDING) + .addOrdering(Contact.CreatedDate,fflib_QueryFactory.SortOrder.DESCENDING, true); + Set fields = qf.getSelectedFields(); + fflib_QueryFactory.Ordering ordering = new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING); + System.assertEquals('Name',ordering.getField()); + + System.assertEquals(new Set{ + 'CreatedBy.Name', + 'LastModifiedById', + 'LastModifiedDate', + 'LastName', + 'Id', + 'FirstName'}, + fields); + + System.assert(qf.toSOQL().containsIgnoreCase('NULLS LAST')); + } + + @isTest + static void checkObjectRead_fail(){ + User usr = createTestUser_noAccess(); + if (usr != null){ + System.runAs(usr){ + //create a query factory object for Account. + fflib_QueryFactory qf = new fflib_QueryFactory(Account.SObjectType); + Boolean excThrown = false; + try { + //check to see if this record is accessible, it isn't. + qf.assertIsAccessible(); + } catch (fflib_SecurityUtils.CrudException e) { + excThrown = true; + } + System.assert(excThrown); + } + } + } + + @isTest + static void checkFieldRead_fail(){ + User usr = createTestUser_noAccess(); + if (usr != null){ + System.runAs(usr){ + //create a query factory object for Account. + fflib_QueryFactory qf = new fflib_QueryFactory(Account.SObjectType); + Boolean excThrown = false; + try { + //set field to enforce FLS, then try to add a field. + qf.setEnforceFLS(true); + qf.selectField('Name'); + } catch (fflib_SecurityUtils.FlsException e) { + excThrown = true; + } + System.assert(excThrown); + } + } + } + + @isTest + static void queryWith_noFields(){ + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType); + qf.assertIsAccessible().setEnforceFLS(true).setCondition( 'name like \'%test%\'' ).addOrdering('CreatedDate',fflib_QueryFactory.SortOrder.DESCENDING); + String query = qf.toSOQL(); + System.assert(query.containsIgnoreCase('SELECT Id FROM Contact'),'Expected \'SELECT Id FROM Contact\' in the SOQL, found: ' + query); + } + + @isTest + static void deterministic_toSOQL(){ + fflib_QueryFactory qf1 = new fflib_QueryFactory(User.SObjectType); + fflib_QueryFactory qf2 = new fflib_QueryFactory(User.SObjectType); + for(fflib_QueryFactory qf:new Set{qf1,qf2}){ + qf.selectFields(new List{ + 'Id', + 'FirstName', + 'LastName', + 'CreatedBy.Name', + 'CreatedBy.Manager', + 'LastModifiedBy.Email' + }); + } + String expectedQuery = + 'SELECT CreatedBy.ManagerId, CreatedBy.Name, ' + +'FirstName, Id, LastModifiedBy.Email, LastName ' + +'FROM User'; + System.assertEquals(qf1.toSOQL(), qf2.toSOQL()); + System.assertEquals(expectedQuery, qf1.toSOQL()); + System.assertEquals(expectedQuery, qf2.toSOQL()); + } + + @isTest + static void deepCloneBasicNoChanges() { + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType) + .setLimit(10) + .setCondition('id=12345') + .selectField('Description') + .addOrdering(new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING) ) + .addOrdering( new fflib_QueryFactory.Ordering('Contact','CreatedDATE',fflib_QueryFactory.SortOrder.DESCENDING)) + .setEnforceFLS(true); + + fflib_QueryFactory qf2 = qf.deepClone(); + + System.assertEquals(qf2, qf); + + System.assertEquals(qf.getLimit(), qf2.getLimit()); + System.assertEquals(qf.getCondition(), qf2.getCondition()); + System.assertEquals(qf.toSOQL(), qf2.toSOQL()); + System.assertEquals(qf.getOrderings(), qf2.getOrderings()); + } + + @isTest + static void deepCloneSubqueryNoChanges() { + fflib_QueryFactory qf = new fflib_QueryFactory(Account.SObjectType) + .setLimit(10) + .setCondition('id=12345') + .selectField('Description') + .addOrdering(new fflib_QueryFactory.Ordering('Account','Name',fflib_QueryFactory.SortOrder.ASCENDING) ) + .addOrdering( new fflib_QueryFactory.Ordering('Account','Description',fflib_QueryFactory.SortOrder.DESCENDING)) + .setEnforceFLS(true); + + qf.subselectQuery('Contacts', true); + + fflib_QueryFactory qf2 = qf.deepClone(); + + System.assertEquals(qf, qf2); + + System.assertEquals(qf.getLimit(), qf2.getLimit()); + System.assertEquals(qf.getCondition(), qf2.getCondition()); + System.assertEquals(qf.toSOQL(), qf2.toSOQL()); + System.assertEquals(qf.getOrderings(), qf2.getOrderings()); + System.assertEquals(qf.getSubselectQueries(), qf2.getSubselectQueries()); + } + + @isTest + static void deepCloneBasic() { + fflib_QueryFactory qf = new fflib_QueryFactory(Contact.SObjectType) + .setLimit(10) + .setCondition('id=12345') + .selectField('Description') + .addOrdering(new fflib_QueryFactory.Ordering('Contact','name',fflib_QueryFactory.SortOrder.ASCENDING) ) + .addOrdering( new fflib_QueryFactory.Ordering('Contact','CreatedDATE',fflib_QueryFactory.SortOrder.DESCENDING)) + .setEnforceFLS(true); + + + fflib_QueryFactory qf2 = qf.deepClone() + .setLimit(200) + .setCondition('id=54321') + .selectField('Fax') + .addOrdering( new fflib_QueryFactory.Ordering('Contact','Fax',fflib_QueryFactory.SortOrder.ASCENDING)) + .setEnforceFLS(false); + + qf2.getOrderings().remove(0); + + System.assertEquals(10, qf.getLimit()); + System.assertEquals(200, qf2.getLimit()); + + System.assertEquals('id=12345', qf.getCondition()); + System.assertEquals('id=54321', qf2.getCondition()); + + String query = qf.toSOQL(); + String query2 = qf2.toSOQL(); + + System.assert(query.containsIgnoreCase('Fax') == false); + System.assert(query.containsIgnoreCase('Description')); + System.assert(query2.containsIgnoreCase('Description')); + System.assert(query2.containsIgnoreCase('Fax')); + + System.assertEquals(2, qf.getOrderings().size()); + System.assertEquals('Name', qf.getOrderings()[0].getField() ); + System.assertEquals(fflib_QueryFactory.SortOrder.DESCENDING, qf.getOrderings()[1].getDirection()); + + System.assertEquals(2, qf2.getOrderings().size()); + System.assertEquals('Fax', qf2.getOrderings()[1].getField()); + System.assertEquals(fflib_QueryFactory.SortOrder.ASCENDING, qf2.getOrderings()[1].getDirection()); + + } + + @isTest + static void deepCloneSubquery() { + fflib_QueryFactory qf = new fflib_QueryFactory(Account.SObjectType); + qf.subselectQuery('Contacts', true); + + fflib_QueryFactory qf2 = qf.deepClone(); + qf2.subselectQuery('Opportunities', true); + + List subqueries = qf.getSubselectQueries(); + List subqueries2 = qf2.getSubselectQueries(); + + fflib_QueryFactory subquery2_0 = subqueries2.get(0); + + subquery2_0.addOrdering(new fflib_QueryFactory.Ordering('Contact','Name',fflib_QueryFactory.SortOrder.ASCENDING)); + + System.assert(subqueries.size() == 1); + System.assert(subqueries2.size() == 2); + + System.assert(qf.getSubselectQueries().get(0).getOrderings().size() == 0); + System.assert(qf2.getSubselectQueries().get(0).getOrderings().size() == 1); + } + + @isTest + static void testSoql_unsortedSelectFields(){ + //Given + fflib_QueryFactory qf = new fflib_QueryFactory(User.SObjectType); + qf.selectFields(new List{ + 'Id', + 'FirstName', + 'LastName', + 'CreatedBy.Name', + 'CreatedBy.Manager', + 'LastModifiedBy.Email' + }); + + qf.setSortSelectFields(false); + + String orderedQuery = + 'SELECT ' + +'FirstName, Id, LastName, ' //less joins come first, alphabetically + +'CreatedBy.ManagerId, CreatedBy.Name, LastModifiedBy.Email ' //alphabetical on the same number of joins' + +'FROM User'; + + //When + String actualSoql = qf.toSOQL(); + + //Then + System.assertNotEquals(orderedQuery, actualSoql); + } + + + public static User createTestUser_noAccess(){ + User usr; + try { + //look for a profile that does not have access to the Account object + PermissionSet ps = + [SELECT Profile.Id, profile.name + FROM PermissionSet + WHERE IsOwnedByProfile = true + AND Profile.UserType = 'Standard' + AND Id NOT IN (SELECT ParentId + FROM ObjectPermissions + WHERE SObjectType = 'Account' + AND PermissionsRead = true) + LIMIT 1]; + + if (ps != null){ + //create a user with the profile found that doesn't have access to the Account object + usr = new User( + firstName = 'testUsrF', + LastName = 'testUsrL', + Alias = 'tstUsr', + Email = 'testy.test@test.com', + UserName='test'+ Math.random().format()+'user99@test.com', + EmailEncodingKey = 'ISO-8859-1', + LanguageLocaleKey = 'en_US', + TimeZoneSidKey = 'America/Los_Angeles', + LocaleSidKey = 'en_US', + ProfileId = ps.Profile.Id, + IsActive=true + ); + insert usr; + } + } catch (Exception e) { + //do nothing, just return null User because this test case won't work in this org. + return null; + } + return usr; + } +} diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_QueryFactoryTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_QueryFactoryTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_QueryFactoryTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDescribeTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDescribeTest.cls new file mode 100644 index 00000000000..1333a3207e0 --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDescribeTest.cls @@ -0,0 +1,183 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/** + This class adapted from https://github.com/capeterson/Apex-Util + Used under a BSD license: https://github.com/capeterson/Apex-Util/blob/master/LICENSE +**/ +@isTest +private class fflib_SObjectDescribeTest { + + /** + * Verify that the different ways of getting your hands on an fflib_SObjectDescribe instance all handle null inputs + * (and blank/empty strings, why not?) by returning null - since there's no possible way to resolve it. + **/ + @isTest + static void getDescribe_badInput(){ + String nullStr = null; //having it as a string var makes for unambiguous selection of overloads + Schema.SObjectType nullType = null; + Schema.DescribeSObjectResult nullDescribe = null; + SObject nullSObject = null; + System.assertEquals(null, fflib_SObjectDescribe.getDescribe(nullStr)); + System.assertEquals(null, fflib_SObjectDescribe.getDescribe('')); + System.assertEquals(null, fflib_SObjectDescribe.getDescribe(' ')); + System.assertEquals(null, fflib_SObjectDescribe.getDescribe(nullType)); + System.assertEquals(null, fflib_SObjectDescribe.getDescribe(nullDescribe)); + System.assertEquals(null, fflib_SObjectDescribe.getDescribe(nullSObject)); + } + + @isTest + static void NamespacedAttributeMap_implementations(){ + fflib_SObjectDescribe.GlobalDescribeMap gdm = fflib_SObjectDescribe.getGlobalDescribe(); + Schema.SObjectType accountObjType = gdm.get('AccOunT'); + System.assertEquals(accountObjType, Account.SobjectType); + System.assertEquals(Schema.getGlobalDescribe().size(), gdm.size()); + + fflib_SObjectDescribe acccountDescribe = fflib_SObjectDescribe.getDescribe(accountObjType); + fflib_SObjectDescribe.FieldsMap fields = acccountDescribe.getFields(); + System.assert( fields.keySet().containsAll(acccountDescribe.getFieldsMap().keySet()) ); + + System.assertEquals(fields.get('name'), Account.SObjectType.fields.name); //behavior of FieldsMap is tested in another method + System.assertEquals(Schema.SObjectType.Account.fields.getMap().size(), fields.size()); + + System.assertEquals(null, fields.get(null), 'Null input should result in null ouput.'); + System.assertEquals(null, fields.get(''), 'Invalid fieldname input should result in null ouput.'); + } + + @isTest + static void FieldsMap(){ + String fakeNamespace = 'fflib_test'; + Map fakeFieldData = new Map{ + 'name__c' => Contact.SObjectType.fields.name, //re-use standard field types since we can't mock them + fakeNamespace+'__name__c' => Account.SObjectType.fields.name, + fakeNamespace+'__otherField__c' => Account.SObjectType.fields.name, + 'createddate' => Contact.SObjectType.fields.CreatedDate + }; + fflib_SObjectDescribe.FieldsMap fields = new fflib_SObjectDescribe.FieldsMap(fakeFieldData); + fields.currentNamespace = fakeNamespace; + System.assertEquals(true, fields.containsKey('name__c') ); + System.assertEquals(true, fields.containsKey(fakeNamespace.toUpperCase()+'__nAMe__c') ); + System.assert(fields.get('NAme__c') === fields.get(fakeNamespace+'__namE__c')); + + System.assert(!fields.keySet(false).contains('otherField__c')); + System.assert(fields.keySet(false).contains(fakeNamespace+'__otherField__c')); + + System.assert(fields.keySet(true).contains('otherField__c')); + System.assert(!fields.keySet(true).contains(fakeNamespace+'__otherField__c')); + + fields.currentNamespace = 'someOtherNamespace'; + System.assertNotEquals(fields.get('name__C'), fields.get(fakeNamespace.capitalize()+'__nAme__c')); + } + + @isTest + static void GlobalDescribeMap(){ + String fakeNamespace = 'fflib_test'; + Map fakeFieldData = new Map{ + 'name__c' => Contact.SObjectType, //re-use stndard object types since we can't mock them + fakeNamespace+'__name__c' => Account.SObjectType, + 'createddate' => Lead.SObjectType + }; + fflib_SObjectDescribe.GlobalDescribeMap gdm = new fflib_SObjectDescribe.GlobalDescribeMap(fakeFieldData); + gdm.currentNamespace = fakeNamespace; + System.assertEquals(true, gdm.containsKey('name__c') ); + System.assertEquals(true, gdm.containsKey(fakeNamespace+'__name__c') ); + System.assert(gdm.get('name__c') === gdm.get(fakeNamespace+'__name__c')); + + gdm.currentNamespace = 'someOtherNamespace'; + System.assertNotEquals(gdm.get('name__c'), gdm.get(fakeNamespace+'__name__c')); + } + + @isTest //Tests all forms of the getDescribe static + static void getAccountDescribes(){ + fflib_SObjectDescribe d = fflib_SObjectDescribe.getDescribe('Account'); + fflib_SObjectDescribe d2 = fflib_SObjectDescribe.getDescribe(Account.SObjectType); + fflib_SObjectDescribe d3 = fflib_SObjectDescribe.getDescribe(Schema.SObjectType.Account); + System.assertEquals('Account', d.getDescribe().getName()); + System.assert( (d === d2 && d2 === d3) ,'All three getDescribe calls should return the same cached instance.'); + } + + @isTest + static void simpleAccountFieldDescribe(){ + fflib_SObjectDescribe d = fflib_SObjectDescribe.getDescribe(Account.SObjectType); + Map fields; + for(integer i = 0; i < 10; i++){ + fields = d.getFieldsMap(); + } + + // Describe Limits removed since Summer ’14. + // https://developer.salesforce.com/releases/release/Summer14/New+Apex+Enhancements + + // Because describe limits are no longer enforced in any API version, this method is no longer available. + // In API version 30.0 and earlier, this method is available but is deprecated. + + // System.assertEquals(1, Limits.getFieldsDescribes() ); + + System.assertEquals(false,fields.isEmpty()); + System.assertEquals(Account.SObjectType, d.getSObjectType()); + } + + @isTest + static void simpleAccountFieldSetDescribe(){ + fflib_SObjectDescribe d = fflib_SObjectDescribe.getDescribe(Account.SObjectType); + Map fields; + for(integer i = 0; i < 10; i++){ + fields = d.getFieldSetsMap(); + } + + // Describe Limits removed since Summer ’14. + // https://developer.salesforce.com/releases/release/Summer14/New+Apex+Enhancements + + // Because describe limits are no longer enforced in any API version, this method is no longer available. + // In API version 30.0 and earlier, this method is available but is deprecated. + + // System.assertEquals(1, Limits.getFieldSetsDescribes() ); + + // no asserts on result size to avoid a requirement on field sets existing + } + + @isTest + static void simpleAccountGetNameField(){ + fflib_SObjectDescribe d = fflib_SObjectDescribe.getDescribe(Account.SObjectType); + Schema.SObjectField nameField = d.getNameField(); + System.assertEquals('Name', nameField.getDescribe().getName()); + } + + @isTest + static void flushCache(){ + fflib_SObjectDescribe d = fflib_SObjectDescribe.getDescribe('Account'); + fflib_SObjectDescribe.flushCache(); + fflib_SObjectDescribe d2 = fflib_SObjectDescribe.getDescribe('Account'); + System.assert(d !== d2, 'Second object should be a fresh instance after a cache flush.' ); + } + + @isTest + static void rawGlobalDescribeCheck(){ + Map systemGd = Schema.getGlobalDescribe(); + Map cachedGd = fflib_SObjectDescribe.getRawGlobalDescribe(); + System.assertEquals(systemGd.size(),cachedGd.size()); + } + +} diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDescribeTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDescribeTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDescribeTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDomainTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDomainTest.cls new file mode 100644 index 00000000000..96555833d3c --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDomainTest.cls @@ -0,0 +1,504 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@IsTest +private with sharing class fflib_SObjectDomainTest +{ + @IsTest + private static void testValidationWithoutDML() + { + fflib_SObjectDomain.TestSObjectDomain opps = new fflib_SObjectDomain.TestSObjectDomain(new Opportunity[] { new Opportunity ( Name = 'Test', Type = 'Existing Account' ) } ); + opps.onValidate(); + System.assertEquals(1, fflib_SObjectDomain.Errors.getAll().size()); + System.assertEquals('You must provide an Account for Opportunities for existing Customers.', fflib_SObjectDomain.Errors.getAll()[0].message); + System.assertEquals(Opportunity.AccountId, ((fflib_SObjectDomain.FieldError)fflib_SObjectDomain.Errors.getAll()[0]).field); + + opps = new fflib_SObjectDomain.TestSObjectDomain(new SObject[] { new Opportunity ( Name = 'Test', Type = 'Existing Account' ) }, Opportunity.SObjectType ); + System.assertEquals(1, fflib_SObjectDomain.Errors.getAll().size()); + System.assertEquals('You must provide an Account for Opportunities for existing Customers.', fflib_SObjectDomain.Errors.getAll()[0].message); + System.assertEquals(Opportunity.AccountId, ((fflib_SObjectDomain.FieldError)fflib_SObjectDomain.Errors.getAll()[0]).field); + } + + @IsTest + private static void testInsertValidationFailedWithoutDML() + { + Opportunity opp = new Opportunity ( Name = 'Test', Type = 'Existing Account' ); + System.assertEquals(false, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.Test.Database.onInsert(new Opportunity[] { opp } ); + System.assertEquals(true, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDomainConstructor.class); + System.assertEquals(1, fflib_SObjectDomain.Errors.getAll().size()); + System.assertEquals('You must provide an Account for Opportunities for existing Customers.', fflib_SObjectDomain.Errors.getAll()[0].message); + System.assertEquals(Opportunity.AccountId, ((fflib_SObjectDomain.FieldError)fflib_SObjectDomain.Errors.getAll()[0]).field); + } + + @IsTest + private static void testUpdateValidationFailedWithoutDML() + { + Opportunity oldOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + oldOpp.Name = 'Test'; + oldOpp.Type = 'Existing Account'; + Opportunity newOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + newOpp.Name = 'Test'; + newOpp.Type = 'New Account'; + System.assertEquals(false, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.Test.Database.onUpdate(new Opportunity[] { newOpp }, new Map { newOpp.Id => oldOpp } ); + System.assertEquals(true, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDomainConstructor.class); + System.assertEquals(1, fflib_SObjectDomain.Errors.getAll().size()); + System.assertEquals('You cannot change the Opportunity type once it has been created.', fflib_SObjectDomain.Errors.getAll()[0].message); + System.assertEquals(Opportunity.Type, ((fflib_SObjectDomain.FieldError)fflib_SObjectDomain.Errors.getAll()[0]).field); + } + + @IsTest + private static void testOnBeforeDeleteWithoutDML() + { + Opportunity opp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + opp.Name = 'Test'; + opp.Type = 'Existing Account'; + System.assertEquals(false, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.Test.Database.onDelete(new Map { opp.Id => opp } ); + System.assertEquals(true, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDomainConstructor.class); + System.assertEquals(1, fflib_SObjectDomain.Errors.getAll().size()); + System.assertEquals('You cannot delete this Opportunity.', fflib_SObjectDomain.Errors.getAll()[0].message); + } + + @IsTest + private static void testOnAfterUndeleteWithoutDML() + { + Opportunity opp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + opp.Name = 'Test'; + opp.Type = 'Existing Account'; + System.assertEquals(false, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.Test.Database.onUndelete(new list { opp } ); + System.assertEquals(true, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDomainConstructor.class); + } + + @IsTest + private static void testObjectSecurity() + { + // Create a user which will not have access to the test object type + User testUser = createChatterExternalUser(); + if(testUser==null) + return; // Abort the test if unable to create a user with low enough acess + System.runAs(testUser) + { + // Test Create object security + Opportunity opp = new Opportunity ( Name = 'Test', Type = 'Existing Account' ); + fflib_SObjectDomain.Test.Database.onInsert(new Opportunity[] { opp } ); + try { + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDomainConstructor.class); + System.assert(false, 'Expected access denied exception'); + } catch (Exception e) { + System.assertEquals('Permission to create an Opportunity denied.', e.getMessage()); + } + + // Test Update object security + Opportunity existingOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + existingOpp.Name = 'Test'; + existingOpp.Type = 'Existing Account'; + fflib_SObjectDomain.Test.Database.onUpdate(new List { opp }, new Map { opp.Id => opp } ); + try { + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDomainConstructor.class); + System.assert(false, 'Expected access denied exception'); + } catch (Exception e) { + System.assertEquals('Permission to update an Opportunity denied.', e.getMessage()); + } + + // Test Delete object security + fflib_SObjectDomain.Test.Database.onDelete(new Map { opp.Id => opp }); + try { + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDomainConstructor.class); + System.assert(false, 'Expected access denied exception'); + } catch (Exception e) { + System.assertEquals('Permission to delete an Opportunity denied.', e.getMessage()); + } + } + } + + @IsTest + private static void testErrorLogging() + { + // Test static helpers for raise none domain object instance errors + Opportunity opp = new Opportunity ( Name = 'Test', Type = 'Existing Account' ); + fflib_SObjectDomain.Errors.error('Error', opp); + fflib_SObjectDomain.Errors.error('Error', opp, Opportunity.Type); + System.assertEquals(2, fflib_SObjectDomain.Errors.getAll().size()); + System.assertEquals('Error', fflib_SObjectDomain.Errors.getAll()[0].message); + System.assertEquals('Error', fflib_SObjectDomain.Errors.getAll()[1].message); + System.assertEquals(Opportunity.Type, ((fflib_SObjectDomain.FieldError)fflib_SObjectDomain.Errors.getAll()[1]).field); + fflib_SObjectDomain.Errors.clearAll(); + System.assertEquals(0, fflib_SObjectDomain.Errors.getAll().size()); + } + + @IsTest + private static void testTriggerState() + { + Opportunity opp = new Opportunity ( Name = 'Test', Type = 'Existing Account' ); + fflib_SObjectDomain.Test.Database.onInsert(new Opportunity[] { opp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectStatefulDomainConstructor.class); + System.assertEquals(1, fflib_SObjectDomain.Errors.getAll().size()); + System.assertEquals('Error on Record Test', fflib_SObjectDomain.Errors.getAll()[0].message); + } + + @IsTest + private static void testRecursiveTriggerState() + { + Opportunity opp = new Opportunity ( Name = 'Test Recursive 1', Type = 'Existing Account' ); + fflib_SObjectDomain.Test.Database.onInsert(new Opportunity[] { opp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectStatefulDomainConstructor.class); + System.assertEquals(2, fflib_SObjectDomain.Errors.getAll().size()); + System.assertEquals('Error on Record Test Recursive 2', fflib_SObjectDomain.Errors.getAll()[0].message); + System.assertEquals('Error on Record Test Recursive 1', fflib_SObjectDomain.Errors.getAll()[1].message); + } + + @IsTest + private static void testOnValidateBehaviorDefault() + { + Opportunity oldOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + oldOpp.Name = 'Test Default Behaviour'; + oldOpp.Type = 'Existing Account'; + Opportunity newOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + newOpp.Name = 'Test Default Behaviour'; + newOpp.Type = 'New Account'; + fflib_SObjectDomain.Test.Database.onUpdate(new Opportunity[] { newOpp }, new Map { newOpp.Id => oldOpp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectOnValidateBehaviourConstructor.class); + } + + @IsTest + private static void testOnValidateBehaviorOld() + { + Opportunity oldOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + oldOpp.Name = 'Test Enable Old Behaviour'; + oldOpp.Type = 'Existing Account'; + Opportunity newOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + newOpp.Name = 'Test Enable Old Behaviour'; + newOpp.Type = 'New Account'; + fflib_SObjectDomain.Test.Database.onUpdate(new Opportunity[] { newOpp }, new Map { newOpp.Id => oldOpp } ); + try { + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectOnValidateBehaviourConstructor.class); + System.assert(false, 'Expected exception'); + } catch (Exception e) { + System.assertEquals('onValidate called', e.getMessage()); + } + } + + @IsTest + private static void testGetChangedFieldsAsStrings() + { + Account acct1 = new Account ( Id = '001E0000006mkRP', Name = 'Test1', AccountNumber = '12345', Site = 'Here'), + acct2 = new Account ( Id = '001E0000006mkRQ', Name = 'Test2', AccountNumber = '54321', Site = 'There'); + System.assertEquals(false, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.Test.Database.onInsert(new Account[] { acct1, acct2 } ); + System.assertEquals(true, fflib_SObjectDomain.Test.Database.hasRecords()); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectChangedRecordsConstructor.class); + Account acct1changed = acct1.clone(), + acct2changed = acct2.clone(); + acct1changed.Name = 'Test1changed'; + fflib_SObjectDomain.Test.Database.onUpdate( + new Account[]{ acct1, acct2}, + new Map{ + acct1changed.Id => acct1, + acct2changed.Id => acct2 + } + ); + } + + + @IsTest + private static void itShouldReturnTheChangedRecordsBySObjectFields() + { + // GIVEN a domain with old and changed records + + Id idLuke = fflib_IDGenerator.generate(Account.SObjectType); + Id idHan = fflib_IDGenerator.generate(Account.SObjectType); + Id idLeia = fflib_IDGenerator.generate(Account.SObjectType); + List oldRecords = new List + { + new Account(Id = idLuke, Name = 'Luke', Description = 'Jedi'), + new Account(Id = idHan, Name = 'Han', Description = 'Pilot'), + new Account(Id = idLeia, Name = 'Leia') + }; + + List newRecords = oldRecords.deepClone(true, true, true); + newRecords.get(0).Name = 'Luke SkyWalker'; + newRecords.get(0).Description = 'Jedi Master'; + newRecords.get(1).Name = 'Han Solo'; + Accounts accounts = new Accounts(newRecords); + accounts.ExistingRecords = new Map(oldRecords); + + // WHEN we create a domain with ExistingRecords and request the changed records + List result = accounts.getChangedRecords( + new Set + { + Account.Name, + Account.Description + } + ); + + // THEN it should only return the changed records + Map resultMap = new Map(result); + System.assertEquals(2, result.size()); + System.assert(resultMap.containsKey(idLuke)); + System.assert(resultMap.containsKey(idHan)); + } + + @IsTest + private static void itShouldReturnTheChangedRecordsByStringFields() + { + // GIVEN a domain with old and changed records + + Id idLuke = fflib_IDGenerator.generate(Account.SObjectType); + Id idHan = fflib_IDGenerator.generate(Account.SObjectType); + Id idLeia = fflib_IDGenerator.generate(Account.SObjectType); + List oldRecords = new List + { + new Account(Id = idLuke, Name = 'Luke', Description = 'Jedi'), + new Account(Id = idHan, Name = 'Han', Description = 'Pilot'), + new Account(Id = idLeia, Name = 'Leia') + }; + + List newRecords = oldRecords.deepClone(true, true, true); + newRecords.get(0).Name = 'Luke SkyWalker'; + newRecords.get(0).Description = 'Jedi Master'; + newRecords.get(1).Name = 'Han Solo'; + Accounts accounts = new Accounts(newRecords); + fflib_SObjectDomain.Test.Database.onUpdate(newRecords, new Map(oldRecords)); + + // WHEN we create a domain with ExistingRecords and request the changed records + List result = accounts.getChangedRecords( + new Set + { + 'Name', + 'Description' + } + ); + + // THEN it should only return the changed records + Map resultMap = new Map(result); + System.assertEquals(2, result.size()); + System.assert(resultMap.containsKey(idLuke)); + System.assert(resultMap.containsKey(idHan)); + } + + + /** + * Create test user + **/ + private static User createChatterExternalUser() + { + // Can only proceed with test if we have a suitable profile - Chatter External license has no access to Opportunity + List testProfiles = [Select Id From Profile where UserLicense.Name='Chatter External' limit 1]; + if(testProfiles.size()!=1) + return null; + + // Can only proceed with test if we can successfully insert a test user + String testUsername = System.now().format('yyyyMMddhhmmss') + '@testorg.com'; + User testUser = new User(Alias = 'test1', Email='testuser1@testorg.com', EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', LocaleSidKey='en_US', ProfileId = testProfiles[0].Id, TimeZoneSidKey='America/Los_Angeles', UserName=testUsername); + try { + insert testUser; + } catch (Exception e) { + return null; + } + return testUser; + } + + /** + * The following tests that the ability to enable/disable all trigger events works as required + **/ + @IsTest + private static void testDisableTriggerEventsBehaviour() + { + boolean bError = false; + + String sErrorMessage = ''; + + Opportunity oldOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + oldOpp.Name = 'Test'; + oldOpp.Type = 'Existing'; + Opportunity newOpp = (Opportunity) Opportunity.sObjectType.newSObject('006E0000006mkRQ'); + newOpp.Name = 'Test'; + newOpp.Type = 'New'; + + + + // these will be called multiple times making sure the correct error message comes back out + // so... there are alot of tests to do here sadly and remember everything is reversed and you need to run backwards! + // 1 - all disabled + try + { + fflib_SObjectDomain.getTriggerEvent(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class).disableAll(); + fflib_SObjectDomain.Test.Database.onInsert(new Opportunity[] { newOpp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class); + } + catch (Exception e) + { + bError = true; + } + + System.AssertEquals(false, bError, 'Error - Trigger events have been fired when they are disabled'); + + //////////////////////////// + // Insert! + try + { + // now lets go after insert and then before + fflib_SObjectDomain.getTriggerEvent(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class).enableAfterInsert(); + fflib_SObjectDomain.Test.Database.onInsert(new Opportunity[] { newOpp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class); + } + catch (Exception e) + { + sErrorMessage = e.getMessage(); + System.Debug('Exception Fired :' + e.getMEssage()); + } + + System.AssertEquals('onAfterInsert called', sErrorMessage, 'Error - After Insert Event is enabled but did not run'); + + try + { + // now lets go after insert and then before + fflib_SObjectDomain.getTriggerEvent(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class).enableBeforeInsert(); + fflib_SObjectDomain.Test.Database.onInsert(new Opportunity[] { newOpp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class); + } + catch (Exception e) + { + sErrorMessage = e.getMessage(); + } + + System.AssertEquals('onBeforeInsert called', sErrorMessage, 'Error - Before Insert Event is enabled but did not run'); + + //////////////////////////// + // Update! + try + { + // now lets go after insert and then before + fflib_SObjectDomain.getTriggerEvent(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class).enableAfterUpdate(); + fflib_SObjectDomain.Test.Database.onUpdate(new Opportunity[] { newOpp }, new Map { newOpp.Id => oldOpp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class); + } + catch (Exception e) + { + sErrorMessage = e.getMessage(); + } + + System.AssertEquals('onAfterUpdate called', sErrorMessage, 'Error - After Update Event is enabled but did not run'); + + try + { + // now lets go after insert and then before + fflib_SObjectDomain.getTriggerEvent(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class).enableBeforeUpdate(); + fflib_SObjectDomain.Test.Database.onUpdate(new Opportunity[] { newOpp }, new Map { newOpp.Id => oldOpp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class); + } + catch (Exception e) + { + sErrorMessage = e.getMessage(); + } + + System.AssertEquals('onBeforeUpdate called', sErrorMessage, 'Error - Before Update Event is enabled but did not run'); + + //////////////////////////// + // Delete! + try + { + // now lets go after insert and then before + fflib_SObjectDomain.getTriggerEvent(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class).enableAfterDelete(); + fflib_SObjectDomain.Test.Database.onDelete(new Map { newOpp.Id => newOpp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class); + } + catch (Exception e) + { + sErrorMessage = e.getMessage(); + } + + System.AssertEquals('onAfterDelete called', sErrorMessage, 'Error - After Delete Event is enabled but did not run'); + + try + { + // now lets go after insert and then before + fflib_SObjectDomain.getTriggerEvent(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class).enableBeforeDelete(); + fflib_SObjectDomain.Test.Database.onDelete(new Map { newOpp.Id => newOpp } ); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class); + } + catch (Exception e) + { + sErrorMessage = e.getMessage(); + } + + System.AssertEquals('onBeforeDelete called', sErrorMessage, 'Error - Before Delete Event is enabled but did not run'); + + //////////////////////////// + // Undelete! + try + { + // now lets go after insert and then before + fflib_SObjectDomain.getTriggerEvent(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class).enableAfterUndelete(); + fflib_SObjectDomain.Test.Database.onUndelete(new Opportunity[] { newOpp }); + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectDisableBehaviourConstructor.class); + } + catch (Exception e) + { + sErrorMessage = e.getMessage(); + } + + System.AssertEquals('onAfterUndelete called', sErrorMessage, 'Error - After Undelete Event is enabled but did not run'); + + /* + + fflib_SObjectDomain.Test.Database.onInsert(new Opportunity[] { opp } ); + + + + fflib_SObjectDomain.Test.Database.onUpdate(new Opportunity[] { newOpp }, new Map { newOpp.Id => oldOpp } ); + + fflib_SObjectDomain.Test.Database.onDelete(new Map { opp.Id => opp } ); + + fflib_SObjectDomain.Test.Database.onUndelete(new list { opp } ); + + + try { + fflib_SObjectDomain.triggerHandler(fflib_SObjectDomain.TestSObjectOnValidateBehaviourConstructor.class); + System.assert(false, 'Expected exception'); + } catch (Exception e) { + System.assertEquals('onValidate called', e.getMessage()); + } + */ + } + + + + private class Accounts extends fflib_SObjectDomain + { + public Accounts(List records) + { + super(records); + } + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDomainTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDomainTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectDomainTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectSelectorTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectSelectorTest.cls new file mode 100644 index 00000000000..6d18d374488 --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectSelectorTest.cls @@ -0,0 +1,330 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@IsTest +private with sharing class fflib_SObjectSelectorTest +{ + + static testMethod void testGetSObjectName() + { + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + system.assertEquals(null, selector.getSObjectFieldSetList()); + system.assertEquals('Account',selector.getSObjectName()); + } + + static testMethod void testSelectSObjectsById() + { + // Inserting in reverse order so that we can test the order by of select + List accountList = new List { + new Account(Name='TestAccount2',AccountNumber='A2',AnnualRevenue=12345.67), + new Account(Name='TestAccount1',AccountNumber='A1',AnnualRevenue=76543.21) }; + insert accountList; + Set idSet = new Set(); + for(Account item : accountList) + idSet.add(item.Id); + + Test.startTest(); + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + List result = (List) selector.selectSObjectsById(idSet); + Test.stopTest(); + + system.assertEquals(2,result.size()); + system.assertEquals('TestAccount2',result[0].Name); + system.assertEquals('A2',result[0].AccountNumber); + system.assertEquals(12345.67,result[0].AnnualRevenue); + system.assertEquals('TestAccount1',result[1].Name); + system.assertEquals('A1',result[1].AccountNumber); + system.assertEquals(76543.21,result[1].AnnualRevenue); + } + + static testMethod void testQueryLocatorById() + { + // Inserting in reverse order so that we can test the order by of select + List accountList = new List { + new Account(Name='TestAccount2',AccountNumber='A2',AnnualRevenue=12345.67), + new Account(Name='TestAccount1',AccountNumber='A1',AnnualRevenue=76543.21) }; + insert accountList; + Set idSet = new Set(); + for(Account item : accountList) + idSet.add(item.Id); + + Test.startTest(); + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + Database.QueryLocator result = selector.queryLocatorById(idSet); + System.Iterator iteratorResult = result.iterator(); + Test.stopTest(); + + System.assert(true, iteratorResult.hasNext()); + Account account = (Account) iteratorResult.next(); + system.assertEquals('TestAccount2',account.Name); + system.assertEquals('A2',account.AccountNumber); + system.assertEquals(12345.67,account.AnnualRevenue); + System.assert(true, iteratorResult.hasNext()); + account = (Account) iteratorResult.next(); + system.assertEquals('TestAccount1',account.Name); + system.assertEquals('A1',account.AccountNumber); + system.assertEquals(76543.21,account.AnnualRevenue); + System.assertEquals(false, iteratorResult.hasNext()); + } + + static testMethod void testAssertIsAccessible() + { + List accountList = new List { + new Account(Name='TestAccount2',AccountNumber='A2',AnnualRevenue=12345.67), + new Account(Name='TestAccount1',AccountNumber='A1',AnnualRevenue=76543.21) }; + insert accountList; + Set idSet = new Set(); + for(Account item : accountList) + idSet.add(item.Id); + + // Create a user which will not have access to the test object type + User testUser = createChatterExternalUser(); + if(testUser==null) + return; // Abort the test if unable to create a user with low enough acess + System.runAs(testUser) + { + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + try + { + List result = (List) selector.selectSObjectsById(idSet); + System.assert(false,'Expected exception was not thrown'); + } + catch(fflib_SObjectDomain.DomainException e) + { + System.assertEquals('Permission to access an Account denied.',e.getMessage()); + } + } + } + + static testMethod void testCRUDOff() + { + List accountList = new List { + new Account(Name='TestAccount2',AccountNumber='A2',AnnualRevenue=12345.67), + new Account(Name='TestAccount1',AccountNumber='A1',AnnualRevenue=76543.21) }; + insert accountList; + Set idSet = new Set(); + for(Account item : accountList) + idSet.add(item.Id); + + // Create a user which will not have access to the test object type + User testUser = createChatterExternalUser(); + if(testUser==null) + return; // Abort the test if unable to create a user with low enough acess + System.runAs(testUser) + { + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(false, false, false, true); + try + { + List result = (List) selector.selectSObjectsById(idSet); + } + catch(fflib_SObjectDomain.DomainException e) + { + System.assert(false,'Did not expect an exception to be thrown'); + } + } + } + + static testMethod void testSOQL() + { + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + String soql = selector.newQueryFactory().toSOQL(); + Pattern p = Pattern.compile(expectedQueryString); + Matcher m = p.matcher(soql); + System.assert(m.matches(), 'Generated SOQL does not match expected pattern. Here is the generated SOQL: ' + soql); + System.assertEquals(1, m.groupCount(), 'Unexpected number of groups captured.'); + String fieldListString = m.group(1); + assertFieldListString(fieldListString, null); + } + + static testMethod void testDefaultConfig() + { + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(); + System.assertEquals(false, selector.isEnforcingFLS()); + System.assertEquals(true, selector.isEnforcingCRUD()); + System.assertEquals(false, selector.isIncludeFieldSetFields()); + + System.assertEquals('Account', selector.getSObjectName()); + System.assertEquals(Account.SObjectType, selector.getSObjectType2()); + } + + private static void assertFieldListString(String fieldListString, String prefix) { + String prefixString = (!String.isBlank(prefix)) ? prefix + '.' : ''; + List fieldList = fieldListString.split(',{1}\\s?'); + System.assertEquals(UserInfo.isMultiCurrencyOrganization() ? 5 : 4, fieldList.size()); + Set fieldSet = new Set(); + fieldSet.addAll(fieldList); + String expected = prefixString + 'AccountNumber'; + System.assert(fieldSet.contains(expected), expected + ' missing from field list string: ' + fieldListString); + expected = prefixString + 'AnnualRevenue'; + System.assert(fieldSet.contains(expected), expected + ' missing from field list string: ' + fieldListString); + expected = prefixString + 'Id'; + System.assert(fieldSet.contains(expected), expected + ' missing from field list string: ' + fieldListString); + expected = prefixString + 'Name'; + System.assert(fieldSet.contains(expected), expected + ' missing from field list string: ' + fieldListString); + if (UserInfo.isMultiCurrencyOrganization()) { + expected = prefixString + 'CurrencyIsoCode'; + System.assert(fieldSet.contains(expected), expected + ' missing from field list string: ' + fieldListString); + } + } + + + @isTest + static void testWithoutSorting() + { + //Given + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(false, false, false, false); + fflib_QueryFactory qf = selector.newQueryFactory(); + + Set expectedSelectFields = new Set{ 'Name', 'Id', 'AccountNumber', 'AnnualRevenue' }; + if (UserInfo.isMultiCurrencyOrganization()) + { + expectedSelectFields.add('CurrencyIsoCode'); + } + + //When + String soql = qf.toSOQL(); + + //Then + Pattern soqlPattern = Pattern.compile(expectedQueryString); + Matcher soqlMatcher = soqlPattern.matcher(soql); + soqlMatcher.matches(); + + List actualSelectFields = soqlMatcher.group(1).deleteWhiteSpace().split(','); + System.assertEquals(expectedSelectFields, new Set(actualSelectFields)); + } + + // Test case of ordering with NULLS LAST option passed into the ordering method + @isTest + static void testWithOrderingNullsLast() + { + // Build the selector to test with + Testfflib_SObjectSelector selector = new Testfflib_SObjectSelector(false, false, false, false); + fflib_QueryFactory qf = selector.newQueryFactory(); + + // Add in the expected fields + Set expectedSelectFields = new Set{ 'Name', 'Id', 'AccountNumber', 'AnnualRevenue' }; + if (UserInfo.isMultiCurrencyOrganization()) + { + expectedSelectFields.add('CurrencyIsoCode'); + } + + // Generate the SOQL string + String soql = qf.toSOQL(); + + // Assert that the + Pattern soqlPattern = Pattern.compile(expectedQueryString); + Matcher soqlMatcher = soqlPattern.matcher(soql); + system.assert(soqlMatcher.matches(), 'The SOQL should have that expected.'); + } + + private static void assertEqualsSelectFields(String expectedSelectFields, String actualSelectFields) + { + Set expected = new Set(expectedSelectFields.deleteWhiteSpace().split(',')); + Set actual = new Set(actualSelectFields.deleteWhiteSpace().split(',')); + + System.assertEquals(expected, actual); + } + + private class Testfflib_SObjectSelector extends fflib_SObjectSelector + { + public Testfflib_SObjectSelector() + { + super(); + } + + public Testfflib_SObjectSelector(Boolean includeFieldSetFields, Boolean enforceCRUD, Boolean enforceFLS, Boolean sortSelectFields) + { + super(includeFieldSetFields, enforceCRUD, enforceFLS, sortSelectFields); + } + + public List getSObjectFieldList() + { + return new List { + Account.Name, + Account.Id, + Account.AccountNumber, + Account.AnnualRevenue + }; + } + + public Schema.SObjectType getSObjectType() + { + return Account.sObjectType; + } + + public override String getOrderBy() + { + String sortVal = 'AnnualRevenue ASC NULLS LAST'; + if (isAccountNameSortable) { + sortVal = 'Name DESC, ' + sortVal; + } + return sortVal; + } + } + + /** + * Create test user + **/ + private static User createChatterExternalUser() + { + // Can only proceed with test if we have a suitable profile - Chatter External license has no access to Opportunity + List testProfiles = [Select Id From Profile where UserLicense.Name='Chatter External' limit 1]; + if(testProfiles.size()!=1) + return null; + + // Can only proceed with test if we can successfully insert a test user + String testUsername = System.now().format('yyyyMMddhhmmss') + '@testorg.com'; + User testUser = new User(Alias = 'test1', Email='testuser1@testorg.com', EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', LocaleSidKey='en_US', ProfileId = testProfiles[0].Id, TimeZoneSidKey='America/Los_Angeles', UserName=testUsername); + try { + insert testUser; + } catch (Exception e) { + return null; + } + return testUser; + } + + public static Boolean isAccountNameSortable { + get { + if (isAccountNameSortable == null) { + isAccountNameSortable = Account.Name.getDescribe().isSortable(); + } + return isAccountNameSortable; + } private set; + } + + public static String expectedQueryString { + get { + if (expectedQueryString == null) { + expectedQueryString = 'SELECT (.*) FROM Account ORDER BY'; + if (isAccountNameSortable) { + expectedQueryString += ' Name DESC NULLS FIRST ,'; + } + expectedQueryString += ' AnnualRevenue ASC NULLS LAST '; + } + return expectedQueryString; + } private set; + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectSelectorTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectSelectorTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectSelectorTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls new file mode 100644 index 00000000000..22081f9c968 --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls @@ -0,0 +1,806 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@IsTest(IsParallel=true) +private with sharing class fflib_SObjectUnitOfWorkTest +{ + // SObjects (in order of dependency) used by UnitOfWork in tests bellow + private static List MY_SOBJECTS = + new Schema.SObjectType[] { + Product2.SObjectType, + PricebookEntry.SObjectType, + Opportunity.SObjectType, + OpportunityLineItem.SObjectType }; + + @IsTest + private static void testUnitOfWorkEmail() + { + String testRecordName = 'UoW Test Name 1'; + + Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage(); + email.setToAddresses(new List{ 'foobar@test.com' }); + email.setPlainTextBody('See Spot run.'); + + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + + uow.m_emailWork = new Mock_SendEmailWork(); + + Opportunity opp = new Opportunity(); + opp.Name = testRecordName; + opp.StageName = 'Open'; + opp.CloseDate = System.today(); + uow.registerNew( opp ); + + uow.registerEmail( email ); + + uow.registerRelationship( email, opp ); + + uow.commitWork(); + + // assert that mock email functionality was called + System.assert(((Mock_SendEmailWork) uow.m_emailWork).doWorkWasCalled); + + System.assertEquals(1, mockDML.recordsForInsert.size()); + } + + @IsTest + private static void testRegisterNew_ThrowExceptionOnDirtyRecord() + { + // GIVEN an existing record + Opportunity opportunity = new Opportunity(Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType)); + fflib_SObjectUnitOfWork unitOfWork = new fflib_SObjectUnitOfWork(MY_SOBJECTS); + + // WHEN we register the existing record as new + Boolean exceptionThrown = false; + try + { + unitOfWork.registerNew(opportunity); + } + catch (Exception e) + { + exceptionThrown = true; + System.assertEquals( + 'Only new records can be registered as new', + e.getMessage(), + 'Incorrect exception message thrown' + ); + } + + // THEN it should have thrown an exception + System.assert(exceptionThrown); + } + + @IsTest + private static void testRegisterDirty_ThrowExceptionOnNewRecord() + { + // GIVEN an new record + Opportunity opportunity = new Opportunity(); + fflib_SObjectUnitOfWork unitOfWork = new fflib_SObjectUnitOfWork(MY_SOBJECTS); + + // WHEN we register the existing record as new + Boolean exceptionThrown = false; + try + { + unitOfWork.registerDirty(opportunity); + } + catch (Exception e) + { + exceptionThrown = true; + System.assertEquals( + 'New records cannot be registered as dirty', + e.getMessage(), + 'Incorrect exception message thrown' + ); + } + + // THEN it should have thrown an exception + System.assert(exceptionThrown); + } + + @IsTest + private static void testRegisterEmptyRecycleBin() + { + // GIVEN - an existing record of the recycle bin + Opportunity opportunity = new Opportunity(Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType)); + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork unitOfWork = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + + // WHEN - we empty the record from the recycle bin + unitOfWork.registerEmptyRecycleBin(new List{ opportunity }); + unitOfWork.commitWork(); + + // THEN - the emptyRecycleBin action should be invoked + System.assertEquals(1, mockDML.recordsForRecycleBin.size()); + } + + @IsTest + private static void testAssertForNonEventSObjectType() + { + fflib_SObjectUnitOfWork unitOfWork = new fflib_SObjectUnitOfWork(MY_SOBJECTS); + unitOfWork.assertForNonEventSObjectType('CustomObject__c'); + } + + @IsTest + private static void testAssertForNonEventSObjectType_ThrowExceptionOnEventObject() + { + Boolean exceptionThrown = false; + fflib_SObjectUnitOfWork unitOfWork = new fflib_SObjectUnitOfWork(MY_SOBJECTS); + try + { + unitOfWork.assertForNonEventSObjectType('PlatformEventObject__e'); + } + catch (Exception e) + { + exceptionThrown = true; + System.assert( + e.getMessage().contains('registerPublishBeforeTransaction'), + 'Incorrect exception message thrown' + ); + } + + System.assert(exceptionThrown); + } + + @IsTest + private static void testAssertForEventSObjectType() + { + fflib_SObjectUnitOfWork unitOfWork = new fflib_SObjectUnitOfWork(MY_SOBJECTS); + unitOfWork.assertForEventSObjectType('PlatformEventObject__e'); + } + + @IsTest + private static void testAssertForEventSObjectType_ThrowExceptionOnNonEventObject() + { + Boolean exceptionThrown = false; + fflib_SObjectUnitOfWork unitOfWork = new fflib_SObjectUnitOfWork(MY_SOBJECTS); + try + { + unitOfWork.assertForEventSObjectType('CustomObject__c'); + } + catch (Exception e) + { + exceptionThrown = true; + System.assert( + e.getMessage().contains('invalid for publishing'), + 'Incorrect exception message thrown' + ); + } + + System.assert(exceptionThrown); + } + + @IsTest + private static void testAssertForSupportedSObjectType_throwExceptionOnUnsupportedType() + { + Boolean exceptionThrown = false; + fflib_SObjectUnitOfWork unitOfWork = new fflib_SObjectUnitOfWork(MY_SOBJECTS); + try + { + unitOfWork.registerNew(new Account()); + } + catch (Exception e) + { + exceptionThrown = true; + System.assert( + e.getMessage().contains('not supported by this unit of work'), + 'Incorrect exception message thrown' + ); + } + + System.assert(exceptionThrown); + } + + /** + * Create uow with new records and commit + * + * Testing: + * + * - Correct events are fired when commitWork completes successfully + * + */ + @IsTest + private static void testDerivedUnitOfWork_CommitSuccess() + { + // Insert Opportunities with UnitOfWork + MockDML mockDML = new MockDML(); + DerivedUnitOfWork uow = new DerivedUnitOfWork(MY_SOBJECTS, mockDML); + for(Integer o=0; o<10; o++) + { + Opportunity opp = new Opportunity(); + opp.Name = 'UoW Test Name ' + o; + opp.StageName = 'Open'; + opp.CloseDate = System.today(); + uow.registerNew(new List{opp}); + for(Integer i=0; i{product}); + PricebookEntry pbe = new PricebookEntry(); + pbe.UnitPrice = 10; + pbe.IsActive = true; + pbe.UseStandardPrice = false; + uow.registerNew(pbe, PricebookEntry.Product2Id, product); + OpportunityLineItem oppLineItem = new OpportunityLineItem(); + oppLineItem.Quantity = 1; + oppLineItem.TotalPrice = 10; + uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe); + uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp); + } + } + uow.commitWork(); + + // Assert Results + System.assertEquals(175, mockDML.recordsForInsert.size(), 'Incorrect of new records'); + + assertEvents(new List { + 'onCommitWorkStarting' + , 'onPublishBeforeEventsStarting' + , 'onPublishBeforeEventsFinished' + , 'onDMLStarting' + , 'onDMLFinished' + , 'onDoWorkStarting' + , 'onDoWorkFinished' + , 'onCommitWorkFinishing' + , 'onPublishAfterSuccessEventsStarting' + , 'onPublishAfterSuccessEventsFinished' + , 'onCommitWorkFinished - true' + } + , uow.getCommitWorkEventsFired(), new Set(MY_SOBJECTS), uow.getRegisteredTypes()); + } + + /** + * Create uow with data that results in DML Exception + * + * Testing: + * + * - Correct events are fired when commitWork fails during DML processing + * + */ + @IsTest + private static void testDerivedUnitOfWork_CommitDMLFail() + { + // Insert Opportunities with UnitOfWork forcing a failure on DML by not setting 'Name' field + DerivedUnitOfWork uow = new DerivedUnitOfWork(MY_SOBJECTS); + Opportunity opp = new Opportunity(); + uow.registerNew(new List{opp}); + Boolean didFail = false; + System.DmlException caughtEx = null; + + try { + uow.commitWork(); + } + catch (System.DmlException dmlex) { + didFail = true; + caughtEx = dmlex; + } + + // Assert Results + System.assertEquals(didFail, true, 'didFail'); + System.assert(caughtEx.getMessage().contains('REQUIRED_FIELD_MISSING'), String.format('Exception message was ', new List { caughtEx.getMessage() })); + + assertEvents(new List { + 'onCommitWorkStarting' + , 'onPublishBeforeEventsStarting' + , 'onPublishBeforeEventsFinished' + , 'onDMLStarting' + , 'onPublishAfterFailureEventsStarting' + , 'onPublishAfterFailureEventsFinished' + , 'onCommitWorkFinished - false' + } + , uow.getCommitWorkEventsFired(), new Set(MY_SOBJECTS), uow.getRegisteredTypes()); + } + + /** + * Create uow with work that fails + * + * Testing: + * + * - Correct events are fired when commitWork fails during DoWork processing + * + */ + @isTest + private static void testDerivedUnitOfWork_CommitDoWorkFail() + { + // Insert Opportunities with UnitOfWork + MockDML mockDML = new MockDML(); + DerivedUnitOfWork uow = new DerivedUnitOfWork(MY_SOBJECTS, mockDML); + Opportunity opp = new Opportunity(); + opp.Name = 'UoW Test Name 1'; + opp.StageName = 'Open'; + opp.CloseDate = System.today(); + uow.registerNew(new List{opp}); + + // register work that will fail during processing + FailDoingWork fdw = new FailDoingWork(); + uow.registerWork(fdw); + + Boolean didFail = false; + FailDoingWorkException caughtEx = null; + + try { + uow.commitWork(); + } + catch (FailDoingWorkException fdwe) { + didFail = true; + caughtEx = fdwe; + } + + // Assert Results + System.assertEquals(didFail, true, 'didFail'); + System.assert(caughtEx.getMessage().contains('Work failed.'), String.format('Exception message was ', new List { caughtEx.getMessage() })); + + assertEvents(new List { + 'onCommitWorkStarting' + , 'onPublishBeforeEventsStarting' + , 'onPublishBeforeEventsFinished' + , 'onDMLStarting' + , 'onDMLFinished' + , 'onDoWorkStarting' + , 'onPublishAfterFailureEventsStarting' + , 'onPublishAfterFailureEventsFinished' + , 'onCommitWorkFinished - false' + } + , uow.getCommitWorkEventsFired(), new Set(MY_SOBJECTS), uow.getRegisteredTypes()); + } + + /** + * Try registering two instances of the same record as dirty. Second register should overwrite first. + * + * Testing: + * + * - Exception is thrown stopping second registration + */ + @IsTest + private static void testRegisterDirty_ExpectReplacement() + { + final Opportunity insertedOpp = new Opportunity( + Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType), + Name = 'Original', + StageName = 'Open', + CloseDate = System.today()); + + Opportunity opp = new Opportunity(Id = insertedOpp.Id, Name = 'Never'); + Opportunity opp2 = new Opportunity(Id = insertedOpp.Id, Name = 'Expected'); + + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + uow.registerDirty(opp); + uow.registerDirty(opp2); + uow.commitWork(); + + System.assertEquals(1, mockDML.recordsForUpdate.size()); + System.assertEquals('Expected', mockDML.recordsForUpdate.get(0).get(Schema.Opportunity.Name)); + } + + /** + * Try registering a single field as dirty. + * + * Testing: + * + * - field is updated + */ + @IsTest + private static void testRegisterDirty_field() { + Opportunity opp = new Opportunity( + Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType), + Name = 'test name', + StageName = 'Open', + CloseDate = System.today()); + + Opportunity nameUpdate = new Opportunity(Id = opp.Id, Name = 'UpdateName'); + Opportunity amountUpdate = new Opportunity(Id = opp.Id, Amount = 250); + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + uow.registerDirty(nameUpdate); + uow.registerDirty(amountUpdate, new List { Opportunity.Amount } ); + uow.commitWork(); + + System.assertEquals(1, mockDML.recordsForUpdate.size()); + System.assertEquals(nameUpdate.Name, mockDML.recordsForUpdate.get(0).get(Schema.Opportunity.Name)); + System.assertEquals(amountUpdate.Amount, mockDML.recordsForUpdate.get(0).get(Schema.Opportunity.Amount)); + } + + /** + * Try registering a single field as dirty on multiple records. + * + */ + @IsTest + private static void testRegisterDirtyRecordsWithDirtyFields() + { + // GIVEN a list of existing records + Opportunity opportunityA = new Opportunity( + Id = fflib_IDGenerator.generate(Opportunity.SObjectType), + Name = 'test name A', + StageName = 'Open', + CloseDate = System.today()); + Opportunity opportunityB = new Opportunity( + Id = fflib_IDGenerator.generate(Opportunity.SObjectType), + Name = 'test name B', + StageName = 'Open', + CloseDate = System.today()); + + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + uow.registerDirty(new List{ opportunityA, opportunityB }); + + // WHEN we register the records again with different fields updated + List recordsWithStageUpdate = new List + { + new Opportunity(Id = opportunityA.Id, StageName = 'Closed'), + new Opportunity(Id = opportunityB.Id, StageName = 'Closed') + }; + List recordsWithAmountUpdate = new List + { + new Opportunity(Id = opportunityA.Id, Amount = 250), + new Opportunity(Id = opportunityB.Id, Amount = 250) + }; + uow.registerDirty(recordsWithStageUpdate, new List { Opportunity.StageName }); + uow.registerDirty(recordsWithAmountUpdate, new List { Opportunity.Amount }); + uow.commitWork(); + + // THEN the records should be registered with both changed values for Amount and StageName + System.assert( + new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => opportunityA.Id, + Opportunity.Amount => 250, + Opportunity.StageName => 'Closed' + }, + new Map + { + Opportunity.Id => opportunityB.Id, + Opportunity.Amount => 250, + Opportunity.StageName => 'Closed' + } + } + ) + .matches(mockDML.recordsForUpdate), + 'Records not registered with the correct values' + ); + } + + /** + * Try registering a single field as dirty on multiple records. + * + */ + @IsTest + private static void testRegisterDirtyRecordsWithDirtyFields_failing() + { + // GIVEN a list of existing records + Opportunity opportunityA = new Opportunity( + Id = fflib_IDGenerator.generate(Opportunity.SObjectType), + Name = 'test name A', + StageName = 'Open', + CloseDate = System.today()); + Opportunity opportunityB = new Opportunity( + Id = fflib_IDGenerator.generate(Opportunity.SObjectType), + Name = 'test name B', + StageName = 'Open', + CloseDate = System.today()); + + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + uow.registerDirty(new List{ opportunityA, opportunityB }); + + // WHEN we register the records again with different fields updated + List recordsWithStageUpdate = new List + { + new Opportunity(Id = opportunityA.Id, StageName = 'Closed'), + new Opportunity(Id = opportunityB.Id, StageName = 'Closed') + }; + List recordsWithAmountUpdate = new List + { + new Opportunity(Id = opportunityA.Id, Amount = 250), + new Opportunity(Id = opportunityB.Id, Amount = 250) + }; + uow.registerDirty(recordsWithStageUpdate, new List { Opportunity.StageName }); + uow.registerDirty(recordsWithAmountUpdate, new List { Opportunity.Amount }); + uow.registerDirty( // Register again the original record, should overwrite the one with the dirty fields + new Opportunity( + Id = opportunityB.Id, + Name = 'test name B', + StageName = 'Open', + CloseDate = System.today()) + ); + uow.commitWork(); + + // THEN only the first record should be registered with both changed values for Amount and StageName and the second should be the original + System.assert( + !new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => opportunityA.Id, + Opportunity.Amount => 250, + Opportunity.StageName => 'Closed' + }, + new Map + { + Opportunity.Id => opportunityB.Id, + Opportunity.Amount => 250, + Opportunity.StageName => 'Closed' + } + } + ) + .matches(mockDML.recordsForUpdate), + 'Not all records should not be registered with the dirty values' + ); + System.assert( + new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => opportunityA.Id, + Opportunity.Amount => 250, + Opportunity.StageName => 'Closed' + }, + new Map + { + Opportunity.Id => opportunityB.Id, + Opportunity.StageName => 'Open' + } + } + ) + .matches(mockDML.recordsForUpdate), + 'The second record should be registered with the original values' + ); + } + + @IsTest + private static void testRegisterUpsert() { + Opportunity existingOpp = new Opportunity( + Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType), + Name = 'Existing Opportunity', + StageName = 'Closed', + CloseDate = System.today()); + + Opportunity newOpportunity = new Opportunity(Name = 'New Opportunity', StageName = 'Closed', CloseDate = System.today()); + + Test.startTest(); + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + uow.registerUpsert(new List{existingOpp, newOpportunity}); + uow.commitWork(); + Test.stopTest(); + + System.assertEquals(1, mockDML.recordsForUpdate.size()); + System.assertEquals(1, mockDML.recordsForInsert.size()); + } + + /** + * Assert that actual events exactly match expected events (size, order and name) + * and types match expected types + */ + private static void assertEvents(List expectedEvents, List actualEvents, Set expectedTypes, Set actualTypes) + { + // assert that events match + System.assertEquals(expectedEvents.size(), actualEvents.size(), 'events size'); + for (Integer i = 0; i < expectedEvents.size(); i++) + { + System.assertEquals(expectedEvents[i], actualEvents[i], String.format('Event {0} was not fired in order expected.', new List { expectedEvents[i] })); + } + + // assert that types match + System.assertEquals(expectedTypes.size(), actualTypes.size(), 'types size'); + for (Schema.SObjectType sObjectType :expectedTypes) + { + System.assertEquals(true, actualTypes.contains(sObjectType), String.format('Type {0} was not registered.', new List { sObjectType.getDescribe().getName() })); + } + } + + /** + * DoWork implementation that throws exception during processing + */ + private class FailDoingWork implements fflib_SObjectUnitOfWork.IDoWork + { + public void doWork() + { + throw new FailDoingWorkException('Work failed.'); + } + } + + /** + * Derived unit of work that tracks event notifications and handle registration of type + */ + private class DerivedUnitOfWork extends fflib_SObjectUnitOfWork + { + private List m_commitWorkEventsFired = new List(); + private Set m_registeredTypes = new Set(); + + public List getCommitWorkEventsFired() + { + return m_commitWorkEventsFired.clone(); + } + + public Set getRegisteredTypes() + { + return m_registeredTypes.clone(); + } + + public DerivedUnitOfWork(List sObjectTypes) + { + super(sObjectTypes); + } + + public DerivedUnitOfWork(List sObjectTypes, IDML dml) + { + super(sObjectTypes, dml); + } + + private void addEvent(String event) + { + // events should only be fired one time + // ensure that this event has not been fired already + for (String eventName :m_commitWorkEventsFired) + { + if (event == eventName) + { + throw new DerivedUnitOfWorkException(String.format('Event {0} has already been fired.', new List { event })); + } + } + m_commitWorkEventsFired.add(event); + } + + public override void onRegisterType(Schema.SObjectType sObjectType) + { + if (m_registeredTypes.contains(sObjectType)) + { + throw new DerivedUnitOfWorkException(String.format('Type {0} has already been registered.', new List { sObjectType.getDescribe().getName() })); + } + m_registeredTypes.add(sObjectType); + } + + public override void onCommitWorkStarting() + { + addEvent('onCommitWorkStarting'); + } + + public override void onPublishBeforeEventsStarting() + { + addEvent('onPublishBeforeEventsStarting'); + } + + public override void onPublishBeforeEventsFinished() + { + addEvent('onPublishBeforeEventsFinished'); + } + + public override void onDMLStarting() + { + addEvent('onDMLStarting'); + } + + public override void onDMLFinished() + { + addEvent('onDMLFinished'); + } + + public override void onDoWorkStarting() + { + addEvent('onDoWorkStarting'); + } + + public override void onDoWorkFinished() + { + addEvent('onDoWorkFinished'); + } + + public override void onCommitWorkFinishing() + { + addEvent('onCommitWorkFinishing'); + } + + public override void onPublishAfterSuccessEventsStarting() + { + addEvent('onPublishAfterSuccessEventsStarting'); + } + + public override void onPublishAfterSuccessEventsFinished() + { + addEvent('onPublishAfterSuccessEventsFinished'); + } + + public override void onPublishAfterFailureEventsStarting() + { + addEvent('onPublishAfterFailureEventsStarting'); + } + + public override void onPublishAfterFailureEventsFinished() + { + addEvent('onPublishAfterFailureEventsFinished'); + } + + public override void onCommitWorkFinished(Boolean wasSuccessful) + { + addEvent('onCommitWorkFinished - ' + wasSuccessful); + } + } + + /** + * Mock implementation of fflib_SObjectUnitOfWork.SendEmailWork + **/ + private class Mock_SendEmailWork implements fflib_SObjectUnitOfWork.IEmailWork + { + public Mock_SendEmailWork() + { + } + + public void registerEmail(Messaging.Email email) + { + } + + public void doWork() + { + doWorkWasCalled = true; + // The code in the fflib_SObjectUnitOfWork class + // causes unit test failures in Orgs that do not + // have email enabled. + } + + private Boolean doWorkWasCalled = false; + } + + private class MockDML implements fflib_SObjectUnitOfWork.IDML + { + public List recordsForInsert = new List(); + public List recordsForUpdate = new List(); + public List recordsForDelete = new List(); + public List recordsForRecycleBin = new List(); + public List recordsForEventPublish = new List(); + + public void dmlInsert(List objList) + { + this.recordsForInsert.addAll(objList); + } + + public void dmlUpdate(List objList) + { + this.recordsForUpdate.addAll(objList); + } + + public void dmlDelete(List objList) + { + this.recordsForDelete.addAll(objList); + } + + public void eventPublish(List objList) + { + this.recordsForEventPublish.addAll(objList); + } + + public void emptyRecycleBin(List objList) + { + this.recordsForRecycleBin.addAll(objList); + } + } + + public class DerivedUnitOfWorkException extends Exception {} + public class FailDoingWorkException extends Exception {} +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectsTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectsTest.cls new file mode 100644 index 00000000000..d9dcd756b1d --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectsTest.cls @@ -0,0 +1,218 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@IsTest +private class fflib_SObjectsTest +{ + + @IsTest + static void itShouldReturnTheDomainsType() + { + System.assertEquals( + (Object) Schema.Account.SObjectType, + new DomainAccounts(new List()) + .getType(), + 'Unexpected Domain Type' + ); + System.assertEquals( + Schema.Account.SObjectType, + new DomainAccounts(new List()) + .getSObjectType(), + 'Unexpected Domain SObjectType' + ); + } + + @IsTest + static void itShouldReturnRecordsWithFieldValues() + { + DomainAccounts domain = generateDomain(); + + System.assert(domain.selectByShippingCountry('USA').size() == 1); + } + + @IsTest + static void itShouldReturnRecordsWithoutFieldValues() + { + DomainAccounts domain = generateDomain(); + + System.assert(domain.selectWithoutShippingCountry().size() == 3); + } + + @IsTest + static void itShouldReturnRecordsWithoutAllFieldValues() + { + DomainAccounts domain = generateDomain(); + + System.assert(domain.selectWithEmptyRecord().size() == 1); + } + + @IsTest + static void itShouldReturnRecordsWithShippingCountry() + { + DomainAccounts domain = generateDomain(); + + System.assert(domain.selectWithShippingCountry().size() == 4); + } + + @IsTest + static void itShouldReturnRecordsWithAllFieldValues() + { + DomainAccounts domain = generateDomain(); + + System.assert(domain.selectPopulatedRecords().size() == 4); + } + + @IsTest + static void itShouldSetFieldValue() + { + DomainAccounts domain = generateDomain(); + String country = 'Holland'; + domain.setShippingCountry(country); + + System.assert(domain.selectByShippingCountry(country).size() == 7); + } + + + @IsTest + static void itShouldSetFieldValueByCondition() + { + DomainAccounts domain = generateDomain(); + domain.setRatingByShippingCountry( + new Map + { + 'USA' => 'Hot' + } + ); + + System.assert(domain.selectByRating('Hot').size() == 1); + } + + @IsTest + static void testErrorLogging() + { + // Test static helpers for raise none domain object instance errors + Opportunity opp = new Opportunity ( Name = 'Test', Type = 'Existing Account' ); + fflib_SObjects.Errors.error('Error', opp); + fflib_SObjects.Errors.error('Error', opp, Opportunity.Type); + System.assertEquals(2, fflib_SObjects.Errors.getAll().size()); + System.assertEquals('Error', fflib_SObjects.Errors.getAll()[0].message); + System.assertEquals('Error', fflib_SObjects.Errors.getAll()[1].message); + System.assertEquals(Opportunity.Type, ((fflib_SObjects.FieldError) fflib_SObjects.Errors.getAll()[1]).field); + fflib_SObjects.Errors.clearAll(); + System.assertEquals(0, fflib_SObjects.Errors.getAll().size()); + } + + private static DomainAccounts generateDomain() + { + DomainAccounts domain = new DomainAccounts( + new List + { + new Account(Name = 'A', ShippingCountry = 'USA'), + new Account(Name = 'B', ShippingCountry = 'Ireland'), + new Account(Name = 'C', ShippingCountry = 'UK'), + new Account(Name = 'D', ShippingCountry = ''), + new Account(Name = 'E'), + new Account(), + new Account(Name = 'G', ShippingCountry = 'Canada') + } + ); + return domain; + } + + + private class DomainAccounts extends fflib_SObjects + { + public DomainAccounts(List records) + { + super(records, Schema.Account.SObjectType); + } + + public List selectByShippingCountry(String country) + { + return (List) getRecordsByFieldValues( + Schema.Account.ShippingCountry, + new Set{ country } + ); + } + + public List selectByRating(String rating) + { + return (List) getRecordsByFieldValue( + Schema.Account.Rating, + rating + ); + } + + public List selectWithoutShippingCountry() + { + return (List) getRecordsWithBlankFieldValues( + Schema.Account.ShippingCountry + ); + } + + public List selectWithShippingCountry() + { + return (List) getRecordsWithNotBlankFieldValues( + Schema.Account.ShippingCountry + ); + } + + public List selectWithEmptyRecord() + { + return (List) getRecordsWithAllBlankFieldValues( + new Set + { + Schema.Account.Name, + Schema.Account.ShippingCountry + } + ); + } + + public List selectPopulatedRecords() + { + return (List) getRecordsWithAllNotBlankFieldValues( + new Set + { + Schema.Account.Name, + Schema.Account.ShippingCountry + } + ); + } + + public void setShippingCountry(String country) + { + setFieldValue(Schema.Account.ShippingCountry, country); + } + + public void setRatingByShippingCountry(Map ratingByCountry) + { + setFieldValueByMap( + Schema.Account.ShippingCountry, + Schema.Account.Rating, + ratingByCountry); + } + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SObjectsTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectsTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SObjectsTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SecurityUtilsTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_SecurityUtilsTest.cls new file mode 100644 index 00000000000..6cd0aede414 --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SecurityUtilsTest.cls @@ -0,0 +1,342 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@isTest +private class fflib_SecurityUtilsTest { + + @TestSetup + static void testSetup() { + // #315 Create a Permission Set that grants "Read" access to Account, Contact and Lead. We will use this in + // Spring '21 orgs that lack the "Read Only" Profile. See: + // https://help.salesforce.com/articleView?id=release-notes.rn_profiles_and_perms_read_only_new.htm&release=230&type=5). + PermissionSet ps = new PermissionSet(Label = 'Read Only Permission Set', Name = 'ReadOnlyPermissionSet'); + insert ps; + + // Grant Read access to the SObjects we use for CRUD tests + List objectPerms = new List(); + objectPerms.add(createObjectPermissions(ps.Id, 'Account', false, true, false, false)); + objectPerms.add(createObjectPermissions(ps.Id, 'Contact', false, true, false, false)); + objectPerms.add(createObjectPermissions(ps.Id, 'Lead', false, true, false, false)); + insert objectPerms; + + // Grant Read/Edit access to the SObject fields we use for FLS tests + List fieldPerms = new List(); + fieldPerms.add(createFieldPermissions(ps.Id, 'Contact', 'Birthdate', true, false)); + fieldPerms.add(createFieldPermissions(ps.Id, 'Contact', 'Email', true, false)); + insert fieldPerms; + } + + static Profile getProfile(String profileName) { + return [SELECT Id, Name FROM Profile WHERE Name = :profileName]; + } + + static ObjectPermissions createObjectPermissions( + Id permSetId, String objectType, Boolean canCreate, Boolean canRead, Boolean canUpdate, Boolean canDelete + ) { + return new ObjectPermissions( + ParentId = permSetId, + SobjectType = objectType, + PermissionsCreate = canCreate, + PermissionsRead = canRead, + PermissionsEdit = canUpdate, + PermissionsDelete = canDelete + ); + } + + static FieldPermissions createFieldPermissions( + Id permSetId, String objectType, String fieldName, Boolean canRead, Boolean canEdit + ) { + return new FieldPermissions( + ParentId = permSetId, + SobjectType = objectType, + Field = objectType + '.' + fieldName, + PermissionsRead = canRead, + PermissionsEdit = canEdit + ); + } + + static User setupTestUser(String profileName){ + Profile p; + Boolean usedMinimumAccessProfile = false; + if (profileName == 'Read Only') { + try { + p = getProfile(profileName); + } catch (QueryException ex) { + if (ex.getMessage().contains('List has no rows for assignment to SObject')) { + // #315 If the "Read Only" Profile is absent, then assume it's a Spring '21 org and see if there's a + // "Minimum Access - Salesforce" Profile we can use instead. + p = getProfile('Minimum Access - Salesforce'); + usedMinimumAccessProfile = true; + } + } + } else { + p = getProfile(profileName); + } + + //username global uniqueness is still enforced in tests + //make sure we get something unique to avoid issues with parallel tests + String uniqueness = DateTime.now()+':'+Math.random(); + try{ + throw new NullPointerException(); + }catch(Exception e){ + uniqueness += e.getStackTraceString(); //includes the top level test method name without having to pass it + } + User usr = new User( + username=UserInfo.getUserId()+'.'+uniqueness.HashCode()+'@'+UserInfo.getOrganizationId()+'.sfdcOrg', + alias = 'testExec', + email='apextests@example.com', + emailencodingkey='UTF-8', + lastname='Testing', + languagelocalekey='en_US', + localesidkey='en_US', + profileid = p.Id, + timezonesidkey='America/Los_Angeles' + ); + insert usr; + + if (usedMinimumAccessProfile) { + // #315 We need to assign the Perm Set to grant Account "Read" access + PermissionSet accountReadPS = [SELECT Id FROM PermissionSet WHERE Name = 'ReadOnlyPermissionSet']; + PermissionSetAssignment psa = new PermissionSetAssignment(AssigneeId = usr.Id, PermissionSetId = accountReadPS.Id); + insert psa; + } + return usr; + } + + @isTest + static void readonly_field_access() { + User testUser = setupTestUser('Read Only'); + System.runAs(testUser){ + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkFieldIsInsertable(Account.SObjectType, 'naMe'); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertNotEquals(null, ex, 'Read only profile should not be able to insert Account.Name'); + System.assert(ex instanceof fflib_SecurityUtils.FlsException, 'Expected an FlsException, got '+ex.getTypeName()); + } + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkFieldIsReadable(Contact.SObjectType, 'LastNAME'); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertEquals(null, ex, 'Read only profile should be able to read Contact.LastName'); + } + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkFieldIsUpdateable(Lead.SObjectType, 'cOMPANY'); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertNotEquals(null, ex, 'Read only profile should not be able to update Lead.Company'); + System.assert(ex instanceof fflib_SecurityUtils.FlsException, 'Expected an FlsException, got '+ex.getTypeName()); + } + + fflib_SecurityUtils.BYPASS_INTERNAL_FLS_AND_CRUD = true; + { //no exceptions, despite no rights + fflib_SecurityUtils.checkFieldIsInsertable(Account.SObjectType, 'naMe'); + fflib_SecurityUtils.checkFieldIsReadable(Contact.SObjectType, 'LastNAME'); + fflib_SecurityUtils.checkFieldIsUpdateable(Lead.SObjectType, 'cOMPANY'); + } + } + } + + @isTest + static void readonly_object_access() { + User testUser = setupTestUser('Read Only'); + System.runAs(testUser){ + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkObjectIsInsertable(Account.SObjectType); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertNotEquals(null, ex, 'Read only profile should not be able to insert Account'); + System.assert(ex instanceof fflib_SecurityUtils.CrudException, 'Expected an CrudException, got '+ex.getTypeName()); + } + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkObjectIsReadable(Contact.SObjectType); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertEquals(null, ex, 'Read only profile should be able to read Contact'); + } + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkObjectIsUpdateable(Lead.SObjectType); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertNotEquals(null, ex, 'Read only profile should not be able to update Lead'); + System.assert(ex instanceof fflib_SecurityUtils.CrudException, 'Expected an CrudException, got '+ex.getTypeName()); + } + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkObjectIsDeletable(Opportunity.SObjectType); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertNotEquals(null, ex, 'Read only profile should not be able to delete Opportunity'); + System.assert(ex instanceof fflib_SecurityUtils.CrudException, 'Expected an CrudException, got '+ex.getTypeName()); + } + + fflib_SecurityUtils.BYPASS_INTERNAL_FLS_AND_CRUD = true; + { //no exceptions, despite no rights + fflib_SecurityUtils.checkObjectIsInsertable(Account.SObjectType); + fflib_SecurityUtils.checkObjectIsReadable(Contact.SObjectType); + fflib_SecurityUtils.checkObjectIsUpdateable(Lead.SObjectType); + fflib_SecurityUtils.checkObjectIsDeletable(Opportunity.SObjectType); + } + } + } + + @isTest + static void readonly_objectAndField_access() { + User testUser = setupTestUser('Read Only'); + System.runAs(testUser){ + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkInsert( + Account.SObjectType, + new List{ + 'Name', + 'ParentId', + 'ownerId' + } + ); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertNotEquals(null, ex, 'Read only profile should not be able to insert Account'); + System.assert(ex instanceof fflib_SecurityUtils.CrudException, 'Expected an CrudException, got '+ex.getTypeName()); + } + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkRead( + Contact.SObjectType, + new List{ + 'LastName', + 'eMaiL', + 'BirthDATE' + } + ); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertEquals(null, ex, 'Read only profile should be able to read Contact'); + } + { + fflib_SecurityUtils.SecurityException ex; + try{ + fflib_SecurityUtils.checkUpdate( + Lead.SObjectType, + new List{ + 'LastName', + 'FirstNAMe', + 'cOMPANY' + } + ); + }catch(fflib_SecurityUtils.SecurityException e){ + ex = e; + } + System.assertNotEquals(null, ex, 'Read only profile should not be able to update Lead'); + System.assert(ex instanceof fflib_SecurityUtils.CrudException, 'Expected an CrudException, got '+ex.getTypeName()); + } + + fflib_SecurityUtils.BYPASS_INTERNAL_FLS_AND_CRUD = true; + { //no exceptions, despite no rights + fflib_SecurityUtils.checkInsert( + Account.SObjectType, + new List{ + 'Name', + 'Type', + 'ownerId' + } + ); + fflib_SecurityUtils.checkRead( + Contact.SObjectType, + new List{ + 'LastName', + 'accountId', + 'ownerId' + } + ); + fflib_SecurityUtils.checkUpdate( + Lead.SObjectType, + new List{ + 'LastName', + 'FirstNAMe', + 'cOMPANY' + } + ); + } + } + } + + @isTest + static void sysadmin_objectAndField_access() { + User testUser = setupTestUser(UTIL_Profile.SYSTEM_ADMINISTRATOR); + System.runAs(testUser){ + fflib_SecurityUtils.checkInsert( + Account.SObjectType, + new List{ + Account.SObjectType.fields.Name, + Account.SObjectType.fields.ParentId, + Account.SObjectType.fields.ownerId + } + ); + fflib_SecurityUtils.checkRead( + Contact.SObjectType, + new List{ + Contact.SObjectType.fields.LastName, + Contact.SObjectType.fields.accountId, + Contact.SObjectType.fields.ownerId + } + ); + fflib_SecurityUtils.checkUpdate( + Lead.SObjectType, + new List{ + Lead.SObjectType.fields.LastName, + Lead.SObjectType.fields.FirstNAMe, + Lead.SObjectType.fields.cOMPANY + } + ); + } + } + +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_SecurityUtilsTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_SecurityUtilsTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_SecurityUtilsTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_StringBuilderTest.cls b/force-app/infrastructure/apex-common/test/classes/fflib_StringBuilderTest.cls new file mode 100644 index 00000000000..4b48e377e44 --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_StringBuilderTest.cls @@ -0,0 +1,115 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +@IsTest +private with sharing class fflib_StringBuilderTest +{ + static testMethod void testfflib_StringBuilder1() + { + fflib_StringBuilder sb = new fflib_StringBuilder(); + sb.add('this is a string'); + sb.add(new List{', which is made',' up from\r ','a number of smaller strings', '. 5 in this case!'}); + system.assertEquals(sb.getStringValue(),'this is a string, which is made up from\r a number of smaller strings. 5 in this case!'); + } + + static testMethod void testfflib_StringBuilder2() + { + fflib_StringBuilder sb = new fflib_StringBuilder(new List{'apples',' and ','pears',': stairs. '}); + sb.add('this is a string'); + sb.add(new List{', which is made',' up from\r ','a number of smaller strings', '. 5 in this case!'}); + system.assertEquals(sb.getStringValue(),'apples and pears: stairs. this is a string, which is made up from\r a number of smaller strings. 5 in this case!'); + } + + static testMethod void testCommaDelimitedBuilder1() + { + fflib_StringBuilder.CommaDelimitedListBuilder sb = new fflib_StringBuilder.CommaDelimitedListBuilder(); + sb.add('a'); + sb.add(new List{'b','c','d'}); + system.assertEquals(sb.getStringValue(),'a,b,c,d'); + } + + static testMethod void testCommaDelimitedBuilder2() + { + fflib_StringBuilder.CommaDelimitedListBuilder sb = new fflib_StringBuilder.CommaDelimitedListBuilder(new List{'x','y'}); + sb.add('a'); + sb.add(new List{'b','c','d'}); + system.assertEquals(sb.getStringValue(),'x,y,a,b,c,d'); + } + + static testMethod void testCommanDelimitedBuilderWithItemPrefix() + { + fflib_StringBuilder.CommaDelimitedListBuilder sb = new fflib_StringBuilder.CommaDelimitedListBuilder(new List{'x','y'}); + sb.add('a'); + sb.add(new List{'b','c','d'}); + system.assertEquals(sb.getStringValue('$'),'$x,$y,$a,$b,$c,$d'); + } + + static testMethod void testCommanDelimitedBuilderWithAlternativeDelimiter() + { + fflib_StringBuilder.CommaDelimitedListBuilder sb = new fflib_StringBuilder.CommaDelimitedListBuilder(new List{'x','y'}); + sb.setDelimiter(';'); + sb.add('a'); + sb.add(new List{'b','c','d'}); + system.assertEquals(sb.getStringValue(),'x;y;a;b;c;d'); + } + + static testMethod void testCommanDelimitedBuilderWithAlternativeDelimiterAndPrefix() + { + fflib_StringBuilder.CommaDelimitedListBuilder sb = new fflib_StringBuilder.CommaDelimitedListBuilder(new List{'x','y'}); + sb.setItemPrefix('#'); + sb.setDelimiter(':'); + sb.add('a'); + sb.add(new List{'b','c','d'}); + system.assertEquals(sb.getStringValue(),'#x:#y:#a:#b:#c:#d'); + } + + static testMethod void testFieldListBuilder() + { + List fields = new List { Account.Name, Account.Id, Account.AccountNumber, Account.AccountNumber, Account.AnnualRevenue }; + fflib_StringBuilder.FieldListBuilder sb = new fflib_StringBuilder.FieldListBuilder(fields); + List fieldList = sb.getStringValue().split(','); + Set fieldSet = new Set(fieldList); + system.assertEquals(4, fieldSet.size()); + system.assert(fieldSet.contains('Name')); + system.assert(fieldSet.contains('Id')); + system.assert(fieldSet.contains('AccountNumber')); + system.assert(fieldSet.contains('AnnualRevenue')); + } + + static testMethod void testMultiCurrencyFieldListBuilder() + { + List fields = new List { Account.Name, Account.Id, Account.AccountNumber, Account.AnnualRevenue }; + fflib_StringBuilder.MultiCurrencyFieldListBuilder sb = new fflib_StringBuilder.MultiCurrencyFieldListBuilder(fields); + List fieldList = sb.getStringValue().split(','); + Set fieldSet = new Set(fieldList); + system.assert(fieldSet.contains('Name')); + system.assert(fieldSet.contains('Id')); + system.assert(fieldSet.contains('AccountNumber')); + system.assert(fieldSet.contains('AnnualRevenue')); + if(UserInfo.isMultiCurrencyOrganization()) + system.assert(fieldSet.contains('CurrencyIsoCode')); + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/test/classes/fflib_StringBuilderTest.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/fflib_StringBuilderTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/fflib_StringBuilderTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-common/test/classes/mocks/fflib_SObjectMocks.cls b/force-app/infrastructure/apex-common/test/classes/mocks/fflib_SObjectMocks.cls new file mode 100644 index 00000000000..b9e13ab12cb --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/mocks/fflib_SObjectMocks.cls @@ -0,0 +1,237 @@ +/** + * Copyright (c), FinancialForce.com, inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the FinancialForce.com, inc nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/* Generated by apex-mocks-generator version 4.0.0 */ +@isTest +public class fflib_SObjectMocks +{ + public virtual class SObjectDomain implements fflib_ISObjectDomain + { + private fflib_ApexMocks mocks; + + public SObjectDomain(fflib_ApexMocks mocks) + { + this.mocks = mocks; + } + + public Schema.SObjectType sObjectType() + { + return (Schema.SObjectType) mocks.mockNonVoidMethod(this, 'sObjectType', new List {}, new List {}); + } + + public List getRecords() + { + return (List) mocks.mockNonVoidMethod(this, 'getRecords', new List {}, new List {}); + } + + public Object getType() + { + return sObjectType(); + } + + public List getObjects() + { + return getRecords(); + } + } + + public virtual class SObjectSelector implements fflib_ISObjectSelector + { + private fflib_ApexMocks mocks; + + public SObjectSelector(fflib_ApexMocks mocks) + { + this.mocks = mocks; + } + + public Schema.SObjectType sObjectType() + { + return (Schema.SObjectType) mocks.mockNonVoidMethod(this, 'sObjectType', new List {}, new List {}); + } + + public List selectSObjectsById(Set idSet) + { + return (List) mocks.mockNonVoidMethod(this, 'selectSObjectsById', new List {Set.class}, new List {idSet}); + } + } + + public virtual class SObjectUnitOfWork implements fflib_ISObjectUnitOfWork + { + private fflib_ApexMocks mocks; + + public SObjectUnitOfWork(fflib_ApexMocks mocks) + { + this.mocks = mocks; + } + + public void registerNew(SObject record) + { + mocks.mockVoidMethod(this, 'registerNew', new List {SObject.class}, new List {record}); + } + + public void registerNew(List records) + { + mocks.mockVoidMethod(this, 'registerNew', new List {List.class}, new List {records}); + } + + public void registerNew(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord) + { + mocks.mockVoidMethod(this, 'registerNew', new List {SObject.class, Schema.sObjectField.class, SObject.class}, new List {record, relatedToParentField, relatedToParentRecord}); + } + + public void registerRelationship(SObject record, Schema.sObjectField relatedToField, SObject relatedTo) + { + mocks.mockVoidMethod(this, 'registerRelationship', new List {SObject.class, Schema.sObjectField.class, SObject.class}, new List {record, relatedToField, relatedTo}); + } + + public void registerRelationship(Messaging.SingleEmailMessage email, SObject relatedTo) + { + mocks.mockVoidMethod(this, 'registerRelationship', new List {Messaging.SingleEmailMessage.class, SObject.class}, new List {email, relatedTo}); + } + + public void registerRelationship(SObject record, Schema.sObjectField relatedToField, Schema.sObjectField externalIdField, Object externalId) + { + mocks.mockVoidMethod(this, 'registerRelationship', new List {SObject.class, Schema.sObjectField.class, Schema.sObjectField.class, Object.class}, new List {record, relatedToField, externalIdField, externalId}); + } + + public void registerDirty(SObject record) + { + mocks.mockVoidMethod(this, 'registerDirty', new List {SObject.class}, new List {record}); + } + + public void registerDirty(List records, List dirtyFields) + { + mocks.mockVoidMethod(this, 'registerDirty', new List { + SObject.class, System.Type.forName('List') + }, new List { + records, dirtyFields + }); + } + + public void registerDirty(SObject record, List dirtyFields) + { + mocks.mockVoidMethod(this, 'registerDirty', new List { + SObject.class, System.Type.forName('List') + }, new List { + record, dirtyFields + }); + } + + public void registerDirty(SObject record, Schema.sObjectField relatedToParentField, SObject relatedToParentRecord) + { + mocks.mockVoidMethod(this, 'registerDirty', new List {SObject.class}, new List {record}); + } + + public void registerDirty(List records) + { + mocks.mockVoidMethod(this, 'registerDirty', new List {List.class}, new List {records}); + } + + public void registerUpsert(SObject record) + { + mocks.mockVoidMethod(this, 'registerUpsert', new List {List.class}, new List {record}); + } + + public void registerEmptyRecycleBin(SObject record) + { + mocks.mockVoidMethod(this, 'registerEmptyRecycleBin', new List {List.class}, new List {record}); + } + + public void registerEmptyRecycleBin(List records) + { + mocks.mockVoidMethod(this, 'registerEmptyRecycleBin', new List {List.class}, new List {records}); + } + + public void registerUpsert(List records) + { + mocks.mockVoidMethod(this, 'registerUpsert', new List {List.class}, new List {records}); + } + + public void registerDeleted(SObject record) + { + mocks.mockVoidMethod(this, 'registerDeleted', new List {SObject.class}, new List {record}); + } + + public void registerDeleted(List records) + { + mocks.mockVoidMethod(this, 'registerDeleted', new List {List.class}, new List {records}); + } + + public void registerPermanentlyDeleted(SObject record) + { + mocks.mockVoidMethod(this, 'registerPermanentlyDeleted', new List {SObject.class}, new List {record}); + } + + public void registerPermanentlyDeleted(List records) + { + mocks.mockVoidMethod(this, 'registerPermanentlyDeleted', new List {SObject.class}, new List {records}); + } + + public void registerPublishBeforeTransaction(SObject record) + { + mocks.mockVoidMethod(this, 'registerPublishBeforeTransaction', new List {SObject.class}, new List {record}); + } + + public void registerPublishBeforeTransaction(List records) + { + mocks.mockVoidMethod(this, 'registerPublishBeforeTransaction', new List {List.class}, new List {records}); + } + + public void registerPublishAfterSuccessTransaction(SObject record) + { + mocks.mockVoidMethod(this, 'registerPublishAfterSuccessTransaction', new List {SObject.class}, new List {record}); + } + public void registerPublishAfterSuccessTransaction(List records) + { + mocks.mockVoidMethod(this, 'registerPublishAfterSuccessTransaction', new List {List.class}, new List {records}); + } + public void registerPublishAfterFailureTransaction(SObject record) + { + mocks.mockVoidMethod(this, 'registerPublishAfterFailureTransaction', new List {SObject.class}, new List {record}); + } + public void registerPublishAfterFailureTransaction(List records) + { + mocks.mockVoidMethod(this, 'registerPublishAfterFailureTransaction', new List {List.class}, new List {records}); + } + + + public void commitWork() + { + mocks.mockVoidMethod(this, 'commitWork', new List {}, new List {}); + } + + public void registerWork(fflib_SObjectUnitOfWork.IDoWork work) + { + mocks.mockVoidMethod(this, 'registerWork', new List {fflib_SObjectUnitOfWork.IDoWork.class}, new List {work}); + } + + public void registerEmail(Messaging.Email email) + { + mocks.mockVoidMethod(this, 'registerEmail', new List {Messaging.Email.class}, new List {email}); + } + } + +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-common/test/classes/mocks/fflib_SObjectMocks.cls-meta.xml b/force-app/infrastructure/apex-common/test/classes/mocks/fflib_SObjectMocks.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-common/test/classes/mocks/fflib_SObjectMocks.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/classic/classes/fflib_ClassicUnitOfWorkFactory.cls b/force-app/infrastructure/apex-extensions/main/application/classic/classes/fflib_ClassicUnitOfWorkFactory.cls new file mode 100644 index 00000000000..1395dbf0bb8 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/classic/classes/fflib_ClassicUnitOfWorkFactory.cls @@ -0,0 +1,57 @@ +/** + * File Name: fflib_ClassicUnitOfWorkFactory + * @description + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual class fflib_ClassicUnitOfWorkFactory + extends fflib_Application.UnitOfWorkFactory + implements fflib_IUnitOfWorkFactory +{ + /** + * Constructs a Unit Of Work factory + **/ + public fflib_ClassicUnitOfWorkFactory() + { + super(); + } + + /** + * Constructs a Unit Of Work factory + * + * @param objectTypes List of SObjectTypes in dependency order + **/ + public fflib_ClassicUnitOfWorkFactory(List objectTypes) + { + super(objectTypes); + } + + public virtual override void setMock(fflib_ISObjectUnitOfWork mockUow) + { + super.setMock(mockUow); + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/classic/classes/fflib_ClassicUnitOfWorkFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/classic/classes/fflib_ClassicUnitOfWorkFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/classic/classes/fflib_ClassicUnitOfWorkFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBinding.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBinding.cls new file mode 100644 index 00000000000..5cc5f3876e5 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBinding.cls @@ -0,0 +1,220 @@ +/** + * File Name: fflib_AppBinding + * @description Definition for an Application Binding + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual class fflib_AppBinding + implements fflib_IAppBinding, Comparable +{ + protected Object bindingObject { get; set; } + protected Decimal bindingSequence { get; set; } + protected fflib_AppBindingType bindingType { get; set; } + protected System.Type implementationType { get; set; } + protected System.Type interfaceType { get; set; } + protected String namespacePrefix { get; set; } + protected fflib_SharingMode sharingMode { get; set; } + + public Integer compareTo(Object compareTo) + { + fflib_IAppBinding compareToEmp = (fflib_IAppBinding) compareTo; + if (getSequence() == compareToEmp.getSequence()) return 0; + if (getSequence() > compareToEmp.getSequence()) return 1; + return -1; + } + + /** + * @return Returns the Object to which the binding is bound to. + */ + public Object getBindingObject() + { + return this.bindingObject; + } + + /** + * @return Returns the enum value fflib_AppBindingType that defines the type of the binding + * + * @see fflib_AppBindingType + */ + public virtual fflib_AppBindingType getBindingType() + { + return this.bindingType; + } + + /** + * @return Returns the System.Type of the implementation class for the binding + */ + public virtual System.Type getImplementationType() + { + return this.implementationType; + } + + /** + * @return Returns the System.Type of the interface for the binding + */ + public virtual System.Type getInterfaceType() + { + return this.interfaceType; + } + + /** + * @return Returns the Namespace for the binding + */ + public virtual String getNamespacePrefix() + { + return this.namespacePrefix; + } + + /** + * @return Returns the number of the order sequence in which the binding should be returned + */ + public virtual Decimal getSequence() + { + return this.bindingSequence; + } + + /** + * @return Returns an enum value fflib_SharingMode that defines the type of sharing used in the implementation class + * + * @see fflib_SharingMode + */ + public virtual fflib_SharingMode getSharingMode() + { + return this.sharingMode; + } + + /** + * Creates a new instance of the implementation for this binding. + * + * @return The enum value fflib_SharingMode that defines the type of sharing used in the implementation class + */ + public virtual Object newImplInstance() + { + return implementationType.newInstance(); + } + + /** + * Sets the Object to which the binding is bound to + * + * @param bindingObject An object representing the binding type, e.g.; + * `MyCustomerInfo.class` or `Schema.Account.SObjectType` + * + * @return An instance of itself to enable method chaining + */ + public virtual fflib_IAppBinding setBindingObject(Object bindingObject) + { + this.bindingObject = bindingObject; + return this; + } + + /** + * Sets the bindingType to an enum value of fflib_AppBindingType + * + * @param bindingType An enum value of fflib_AppBindingType to define the type of binding, e.g.; + * `Service` or `Domain` + * + * @return An instance of itself to enable method chaining + * + * @see fflib_AppBindingType + */ + public virtual fflib_IAppBinding setBindingType(fflib_AppBindingType bindingType) + { + this.bindingType = bindingType; + return this; + } + + /** + * Sets the System.Type of the implementation class for the binding + * + * @param implementationType The System.Type of the implementation class. e.g.; `AccountsServiceImpl.class` + * + * @return An instance of itself to enable method chaining + */ + public virtual fflib_IAppBinding setImplementationType(System.Type implementationType) + { + this.implementationType = implementationType; + return this; + } + + /** + * Sets the System.Type of the interface for the binding + * + * @param interfaceType The System.Type of the interface class. e.g.; `IAccountsService.class`. + * This must match the type of the implementation. + * + * @return An instance of itself to enable method chaining + */ + public virtual fflib_IAppBinding setInterfaceType(System.Type interfaceType) + { + this.interfaceType = interfaceType; + return this; + } + + /** + * Sets the Namespace for the binding. This is typically used when Apps use Namespaces + * or have multiple packages inside a single project + * + * @param namespacePrefix The name of the Namespace for the binding + * + * @return An instance of itself to enable method chaining + */ + public virtual fflib_IAppBinding setNamespacePrefix(String namespacePrefix) + { + this.namespacePrefix = namespacePrefix; + return this; + } + + /** + * Set the order number so that the resolver can sort the bindings. + * The higher the number, the more priority. + * The binding with the highest number will be returned first by the resolver. + * + * @param sequence A decimal number representing the priority of the binding + * + * @return An instance of itself to enable method chaining + */ + public virtual fflib_IAppBinding setSequence(Decimal sequence) + { + this.bindingSequence = sequence; + return this; + } + + /** + * Sets an enum value fflib_SharingMode that defines the type of sharing used in the implementation class + * + * @param sharingMode A enum value of fflib_SharingMode representing the sharing mode + * + * @return An instance of itself to enable method chaining + * + * @see fflib_SharingMode + */ + public virtual fflib_IAppBinding setSharingMode(fflib_SharingMode sharingMode) + { + this.sharingMode = sharingMode; + return this; + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBinding.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBinding.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBinding.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingMetaDataModule.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingMetaDataModule.cls new file mode 100644 index 00000000000..100dbabd900 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingMetaDataModule.cls @@ -0,0 +1,139 @@ +/** + * File Name: fflib_AppBindingMetaDataModule + * @description An application module responsible for loading the application bindings + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public with sharing class fflib_AppBindingMetaDataModule + extends fflib_AppBindingModule +{ + public override void init() + { + this.bindings = new List(); + for (fflib_AppBinding__mdt binding : fflib_AppBindingsSelector.newInstance().selectAll()) + { + addBinding( + new fflib_AppBinding() + .setBindingObject(getBindingObject(binding)) + .setBindingType(getBindingType(binding)) + .setImplementationType(getSystemType(binding, fflib_AppBinding__mdt.ImplementationType__c)) + .setInterfaceType(getSystemType(binding, fflib_AppBinding__mdt.InterfaceType__c)) + .setNamespacePrefix(binding.NamespacePrefix) + .setSequence(binding.BindingSequence__c) + .setSharingMode(getSharingModeFromString(binding.SharingMode__c)) + ); + } + } + + private Object getBindingObject(fflib_AppBinding__mdt binding) + { + if (String.isBlank(binding.BindingObject__c) && String.isBlank(binding.BindingObjectAlternate__c)) return null; + + String bindingObjectApiName = String.isNotBlank(binding.BindingObject__c) + ? binding.BindingObject__r.QualifiedApiName.toLowerCase().trim() + : binding.BindingObjectAlternate__c.toLowerCase().trim(); + + // Assume its an SObjectType + List schemaDescribes = Schema.describeSObjects(new List {bindingObjectApiName}); + if (schemaDescribes.size() == 1) + { + return schemaDescribes.get(0).getSObjectType(); + } + else + { + // check if its an Object (class) + Type objectType = System.Type.forName(bindingObjectApiName); + if (objectType == null) + throw new DeveloperException('Failed to find (S)Object ' + bindingObjectApiName + ' referenced by binding ' + binding.DeveloperName); + + return objectType; + } + } + + private fflib_AppBindingType getBindingType(fflib_AppBinding__mdt binding) + { + Map bindingTypeByStrings = new Map(); + for (fflib_AppBindingType enumValue : fflib_AppBindingType.values()) + { + bindingTypeByStrings.put(String.valueOf(enumValue).toUpperCase(), enumValue); + } + + if (bindingTypeByStrings.containsKey(binding.Type__c.toUpperCase()) == false) + throw new DeveloperException( + String.format( + 'Unable to identify binding type {0} referenced by binding {1}', + new List{ binding.Type__c, binding.DeveloperName} + ) + ); + + return bindingTypeByStrings.get(binding.Type__c.toUpperCase()); + } + + private fflib_SharingMode getSharingModeFromString(String sharingMode) + { + switch on sharingMode.toUpperCase() + { + when 'WITH SHARING' + { + return fflib_SharingMode.WithSharing; + } + when 'WITHOUT SHARING' + { + return fflib_SharingMode.WithoutSharing; + } + when 'INHERITED SHARING' + { + return fflib_SharingMode.WithoutSharing; + } + when 'NONE' + { + return null; + } + } + return null; + } + + private Type getSystemType(fflib_AppBinding__mdt binding, Schema.SObjectField sObjectField) + { + String systemTypeName = String.valueOf(binding.get(sObjectField)); + if (null == systemTypeName) return null; + + Type systemType = System.Type.forName(systemTypeName); + if (null == systemType) + throw new DeveloperException( + String.format( + 'Unable to find {0} referenced by binding {1} for {2}', + new List{ systemTypeName, binding.DeveloperName, sObjectField.getDescribe().getName()} + ) + ); + return systemType; + } + + public class DeveloperException extends Exception + { + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingMetaDataModule.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingMetaDataModule.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingMetaDataModule.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingModule.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingModule.cls new file mode 100644 index 00000000000..ea0fbb8cebb --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingModule.cls @@ -0,0 +1,69 @@ +/** + * File Name: fflib_AppBindingModule + * @description An application module is responsible for loading the application bindings + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual class fflib_AppBindingModule + implements fflib_IAppBindingModule +{ + protected List bindings; + + public fflib_AppBindingModule(){ } + + public fflib_AppBindingModule(List bindings) + { + this.bindings = bindings; + } + + public virtual void addBinding(fflib_IAppBinding binding) + { + addBindings(new List{ binding }); + } + + public virtual void addBindings(List bindings) + { + if (null == this.bindings) + { + this.bindings = bindings; + } + else + { + this.bindings.addAll(bindings); + } + } + + public List getBindings() + { + return this.bindings; + } + + public virtual void init() + { + if (null == this.bindings) this.bindings = new List(); + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingModule.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingModule.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingModule.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingResolver.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingResolver.cls new file mode 100644 index 00000000000..8d24fcf7246 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingResolver.cls @@ -0,0 +1,318 @@ +/** + * File Name: fflib_AppBindingResolver + * @description Resolver for returning the requested binding(s) + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual class fflib_AppBindingResolver + implements fflib_IAppBindingResolver +{ + // allow for dependency injection inside the resolver + protected InternalBindings internalBindings + { + get + { + if (null == internalBindings) + { + internalBindings = new InternalBindings(); + } + return internalBindings; + } + private set; + } + + protected fflib_IAppBinding bindingToResolve + { + get + { + if (bindingToResolve == null) + { + bindingToResolve = internalBindings.newAppBinding(); + } + return bindingToResolve; + } + private set; + } + + protected fflib_IAppBindings bindings; + private List modules; + + public static fflib_IAppBindingResolver newInstance() + { + return new fflib_AppBindingResolver(); + } + + /** + * Constructors + */ + public fflib_AppBindingResolver() + { + } + + /** + * Providing routes to the AppBindingResolver constructor provides dependency injection inside the resolver + * + * @param routes The routes + */ + public fflib_AppBindingResolver(fflib_IAppBindingRouter routes) + { + this(routes.getRoutes()); + } + + public fflib_AppBindingResolver(Map internalBindings) + { + this.internalBindings = new InternalBindings(internalBindings); + } + + public fflib_IAppBindingResolver addModule(fflib_IAppBindingModule bindingModule) + { + if (null == this.modules) this.modules = new List(); + + this.modules.add(bindingModule); + return this; + } + + public fflib_IAppBindingResolver byObject(Object bindingObject) + { + this.bindingToResolve.setBindingObject(bindingObject); + return this; + } + + public fflib_IAppBindingResolver bySequence(Integer sequence) + { + this.bindingToResolve.setSequence(sequence); + return this; + } + + public fflib_IAppBindingResolver byType(fflib_AppBindingType bindingType) + { + this.bindingToResolve.setBindingType(bindingType); + return this; + } + + public fflib_IAppBindingResolver byInterfaceType(Type interfaceType) + { + this.bindingToResolve.setInterfaceType(interfaceType); + return this; + } + + public fflib_IAppBindingResolver bySharingMode(fflib_SharingMode sharingMode) + { + this.bindingToResolve.setSharingMode(sharingMode); + return this; + } + + public fflib_IAppBindingResolver byNamespacePrefix(String namespacePrefix) + { + this.bindingToResolve.setNamespacePrefix(namespacePrefix); + return this; + } + + + /** + * Handles lazy loading of modules, if no custom module is configured (via addModule) the default module is returned + * + * @return Returns the configured modules + */ + public List getModules() + { + if (null == this.modules) + { + // Lazy load the default Binding module is none other is set. + modules = new List + { + internalBindings.newAppBindingModule() + }; + } + return modules; + } + + public List resolve() + { + if (!hasBindingsLoaded()) loadBindings(); + + fflib_IAppBindings resolvedBindings = + this.bindings + .selectByNamespacePrefix(bindingToResolve.getNamespacePrefix()) + .selectByType(bindingToResolve.getBindingType()) + .selectByInterfaceType(bindingToResolve.getInterfaceType()) + .selectByObject(bindingToResolve.getBindingObject()) + .selectBySharingMode(bindingToResolve.getSharingMode()) + .selectBySequence(bindingToResolve.getSequence()); + + this.bindingToResolve = internalBindings.newAppBinding(); + return resolvedBindings.getBindings(); + } + + + protected Boolean hasBindingsLoaded() + { + return (this.bindings != null); + } + + /** + * Prepares a domain to store the bindings, + * retrieves the bindings from the modules + * and stores the bindings in this.bindings + */ + protected virtual void loadBindings() + { + this.bindings = internalBindings.newAppBindings(); + List modules = getModules(); + List appBindings = loadModules(modules); + this.bindings.addBindings( + appBindings + ); + } + + /** + * Initialises all the modules and retrieves their binding. + * Any child modules in all nested will also be initialised and processed + * + * @param modules The modules to load their binding + * + * @return Returns the bindings configured in the given modules + */ + protected virtual List loadModules(List modules) + { + List result = new List(); + for (fflib_IAppBindingModule module : getModules()) + { + module.init(); + List bindings = module.getBindings(); + List subModules = getModulesFromBindings(bindings); + + if (subModules.isEmpty() == false) + { + result.addAll( + loadModules(subModules) + ); + } + result.addAll(bindings); + } + return result; + } + + /** + * Retrieves the bindings of the type module from the given list and returns their instance + * + * @param bindings The bindings to filter + * + * @return Returns the instances of Module bindings + */ + private List getModulesFromBindings(List bindings) + { + List result = new List(); + for (fflib_IAppBinding binding : bindings) + { + if (binding.getBindingType() != fflib_AppBindingType.Module) continue; + + Object instance = binding.newImplInstance(); + if (!(instance instanceof fflib_IAppBindingModule)) + throw new DeveloperException( + 'Binding module should be an instance of fflib_IAppBindingModule, instead found: ' + + binding.getImplementationType().getName() + ); + + result.add( + (fflib_IAppBindingModule) instance + ); + } + return result; + } + + public class InternalBindings + { + private Map bindings; + + public InternalBindings() + { + } + + public InternalBindings(Map internalBindings) + { + this.bindings = internalBindings; + } + + public fflib_IAppBinding newAppBinding() + { + System.Type interfaceType = fflib_IAppBinding.class; + if (hasInternalBinding(interfaceType)) + { + Object instance = getInternalBinding(interfaceType); + if (!(instance instanceof fflib_IAppBinding)) + throw new DeveloperException('Invalid internal binding for fflib_IAppBinding'); + + return (fflib_IAppBinding) instance; + } + + return new fflib_AppBinding(); + } + + public fflib_IAppBindings newAppBindings() + { + System.Type interfaceType = fflib_IAppBindings.class; + if (hasInternalBinding(interfaceType)) + { + Object instance = getInternalBinding(interfaceType); + if (!(instance instanceof fflib_IAppBinding)) + throw new DeveloperException('Invalid internal binding for fflib_IAppBindings'); + + return (fflib_IAppBindings) instance; + } + + return new fflib_AppBindings(); + } + + private Boolean hasInternalBinding(System.Type interfaceType) + { + return this.bindings != null && this.bindings.containsKey(interfaceType); + } + + private Object getInternalBinding(System.Type interfaceType) + { + return this.bindings.get(interfaceType).newInstance(); + } + + private fflib_IAppBindingModule newAppBindingModule() + { + System.Type interfaceType = fflib_IAppBindingModule.class; + if (hasInternalBinding(interfaceType)) + { + Object instance = getInternalBinding(interfaceType); + if (!(instance instanceof fflib_IAppBindingModule)) + throw new DeveloperException('Invalid internal binding for fflib_IAppBindingModule'); + + return (fflib_IAppBindingModule) instance; + } + + return new fflib_AppBindingMetaDataModule(); + } + } + + public class DeveloperException extends Exception {} +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingResolver.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingResolver.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingResolver.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingType.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingType.cls new file mode 100644 index 00000000000..35c60feb7ad --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingType.cls @@ -0,0 +1,34 @@ +/** + * File Name: fflib_AppBindingType + * @Description Enum for Binding Types + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public enum fflib_AppBindingType +{ + Domain, Service, Selector, Controller, Module, Misc +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingType.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingType.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingType.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindings.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindings.cls new file mode 100644 index 00000000000..31abd248ef7 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindings.cls @@ -0,0 +1,178 @@ +/** + * File Name: fflib_AppBindings + * @description Domain class for fflib_AppBinding objects + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual class fflib_AppBindings + implements fflib_IAppBindings +{ + protected List bindings; + + public fflib_AppBindings() { } + + public fflib_AppBindings(List bindings) + { + this.bindings = bindings; + } + + public void addBinding(fflib_IAppBinding binding) + { + if (null == this.bindings) + { + this.bindings = new List{ binding }; + } + { + this.bindings.add(binding); + } + } + + public void addBindings(List bindings) + { + if (null == this.bindings) + { + this.bindings = bindings; + } + else + { + this.bindings.addAll(bindings); + } + } + + public fflib_IAppBindings selectByObject(Object bindingObject) + { + if (null == bindingObject) return this; + + List result = new List(); + for (fflib_IAppBinding binding : bindings) + { + if (binding.getBindingObject() != bindingObject) continue; + + result.add(binding); + } + return new fflib_AppBindings(result); + } + + public fflib_IAppBindings selectBySequence(Decimal sequence) + { + bindings.sort(); + + if (null == sequence) return this; + + List result = new List(); + for (fflib_IAppBinding binding : bindings) + { + if (binding.getSequence() != sequence) continue; + + result.add(binding); + } + return new fflib_AppBindings(result); + } + + public fflib_IAppBindings selectByType(fflib_AppBindingType bindingType) + { + if (null == bindingType) return this; + + List result = new List(); + for (fflib_IAppBinding binding : bindings) + { + if (binding.getBindingType() != bindingType) continue; + + result.add(binding); + } + return new fflib_AppBindings(result); + } + + public fflib_IAppBindings selectByInterfaceType(Type interfaceType) + { + if (null == interfaceType) return this; + + List result = new List(); + for (fflib_IAppBinding binding : bindings) + { + if (binding.getInterfaceType() != interfaceType) continue; + + result.add(binding); + } + return new fflib_AppBindings(result); + } + + public fflib_IAppBindings selectBySharingMode(fflib_SharingMode sharingMode) + { + if (null == sharingMode) return this; + + List result = new List(); + for (fflib_IAppBinding binding : bindings) + { + if (binding.getSharingMode() != sharingMode) continue; + + result.add(binding); + } + return new fflib_AppBindings(result); + } + + public fflib_IAppBindings selectByNamespacePrefix(String namespacePrefix) + { + if (null == namespacePrefix) return this; + + List result = new List(); + for (fflib_IAppBinding binding : bindings) + { + if (binding.getNamespacePrefix() != namespacePrefix) continue; + + result.add(binding); + } + return new fflib_AppBindings(result); + } + + public List getBindings() + { + return this.bindings; + } + + public List getImplementations() + { + List result = new List(); + for (fflib_IAppBinding binding : bindings) + { + result.add( + binding.getImplementationType() + ); + } + return result; + } + + public List getObjects() + { + return getBindings(); + } + + public Object getType() + { + return fflib_IAppBinding.class; + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindings.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindings.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindings.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingsSelector.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingsSelector.cls new file mode 100644 index 00000000000..d8bc2d53a6f --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingsSelector.cls @@ -0,0 +1,93 @@ +/** + * File Name: fflib_AppBindingsSelector + * @description Selector for fflib_AppBinding__mdt records + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual without sharing class fflib_AppBindingsSelector extends fflib_SObjectSelector +{ + // Used for mocking + @TestVisible + private static System.Type SELECTOR_IMPL_TYPE = fflib_AppBindingsSelector.class; + + public List getSObjectFieldList() + { + return new List + { + fflib_AppBinding__mdt.Id, + fflib_AppBinding__mdt.BindingObject__c, + fflib_AppBinding__mdt.BindingObjectAlternate__c, + fflib_AppBinding__mdt.BindingSequence__c, + fflib_AppBinding__mdt.DeveloperName, + fflib_AppBinding__mdt.NamespacePrefix, + fflib_AppBinding__mdt.ImplementationType__c, + fflib_AppBinding__mdt.InterfaceType__c, + fflib_AppBinding__mdt.SharingMode__c, + fflib_AppBinding__mdt.Type__c + }; + } + + public static fflib_AppBindingsSelector newInstance() + { + return (fflib_AppBindingsSelector) SELECTOR_IMPL_TYPE.newInstance(); + } + + public Schema.SObjectType getSObjectType() + { + return fflib_AppBinding__mdt.SObjectType; + } + + public virtual List selectAll() + { + /** + Todo - Waiting to uncomment the following section + until fflib-apex-common fixes the getFieldPath issue + Currently failing on the BindingObject__r field, + thinking its not a relationship field + + Error on line 119, column 1: fflib_QueryFactory.NonReferenceFieldException: + fflib_AppBinding__mdt.BindingObject__r is not a lookup or master-detail + field but is used in a cross-object query field. + + return (List) Database.query( + newQueryFactory() + .selectField('BindingObject__r.QualifiedApiName') + .toSOQL()); + */ + + return (List) Database.query( + String.format( + 'SELECT {0} FROM {1}', + new List + { + getFieldListString() + ', BindingObject__r.QualifiedApiName', + getSObjectType().getDescribe().getName() + } + ) + ); + } +} diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingsSelector.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingsSelector.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_AppBindingsSelector.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicAppFactory.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicAppFactory.cls new file mode 100644 index 00000000000..9080afbdcff --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicAppFactory.cls @@ -0,0 +1,67 @@ +/** + * File Name: fflib_DynamicAppFactory + * @description Abstract class with common functionality for Dynamic Bindings + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public abstract class fflib_DynamicAppFactory +{ + protected fflib_SharingMode sharingMode + { + get + { + if (sharingMode == null) + { + sharingMode = fflib_SharingMode.WithSharing; + } + return sharingMode; + } + set; + } + protected fflib_IAppBindingResolver resolver; + protected Map routes; + + protected Map mockImplByObjectType = new Map(); + + protected void setMock(Object objectType, Object mock) + { + this.mockImplByObjectType.put( + objectType, + mock + ); + } + + protected Object getMock(Object objectType) + { + return this.mockImplByObjectType.get(objectType); + } + + protected Boolean hasMock(Object objectType) + { + return this.mockImplByObjectType.containsKey(objectType); + } +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicAppFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicAppFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicAppFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicDomainFactory.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicDomainFactory.cls new file mode 100644 index 00000000000..57b54ebe468 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicDomainFactory.cls @@ -0,0 +1,128 @@ +/** + * File Name: fflib_DynamicDomainFactory + * @description Domain Factory for creating instances of Domain classes + * using a resolver to dynamically link an Object type to its Domain Constructor implementation class + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual class fflib_DynamicDomainFactory + extends fflib_DynamicAppFactory + implements fflib_IDomainFactory +{ + protected Map mockDomainByObjectType = new Map(); + protected fflib_ISelectorFactory selectorFactory; + + + /** + * Class constructor + * + * @param resolver A reference to the resolve instance + * @param selectorFactory A reference to the selector factory instance + */ + public fflib_DynamicDomainFactory(fflib_IAppBindingResolver resolver, fflib_ISelectorFactory selectorFactory) + { + this.resolver = resolver; + this.selectorFactory = selectorFactory; + } + + public fflib_IDomain newInstance(Set recordIds) + { + return newInstance(this.selectorFactory.selectById(recordIds)); + } + + public fflib_IDomain newInstance(Set recordIds, SObjectType sObjectType) + { + return newInstance(this.selectorFactory.selectById(recordIds, sObjectType)); + } + + public fflib_IDomain newInstance(List records) + { + SObjectType sObjectType = records.getSObjectType(); + + if (sObjectType == null) + throw new DeveloperException('Could not determine the SObjectType of the provided records'); + + return newInstance( + (List) records, + (Object) sObjectType + ); + } + + public fflib_IDomain newInstance(List objects, Object objectType) + { + if (hasMock(objectType)) return (fflib_IDomain) getMock(objectType); + + List bindings = resolver.byObject(objectType) + .byType(fflib_AppBindingType.Domain) + .resolve(); + + if (bindings.isEmpty()) + throw new DeveloperException('Unable to find domain binding for ' + objectType); + + System.Type domainImplementationType = bindings.get(0).getImplementationType(); + + Object instance = domainImplementationType.newInstance(); + + if (!(instance instanceof fflib_IDomainConstructor)) + throw new DeveloperException( + String.format( + 'Domain implementation {0} should be an instance of fflib_IDomainConstructor', + new List { domainImplementationType.getName() } + ) + ); + + return ((fflib_IDomainConstructor) domainImplementationType.newInstance()) + .construct(objects); + } + + public fflib_IDomain newInstance(List records, SObjectType domainSObjectType) + { + return newInstance((List) records, (Object) domainSObjectType); + } + + public void setMock(fflib_ISObjectDomain mockDomain) + { + super.setMock(mockDomain.getType(), mockDomain); + } + + public void setMock(Schema.SObjectType sObjectType, fflib_ISObjectDomain mockDomain) + { + super.setMock(sObjectType, mockDomain); + } + + public void setMock(fflib_IDomain mockDomain) + { + super.setMock(mockDomain.getType(), mockDomain); + } + + public void setMock(Object domainType, fflib_IDomain mockDomain) + { + super.setMock(domainType, mockDomain); + } + + public class DeveloperException extends Exception {} +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicDomainFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicDomainFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicDomainFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicSelectorFactory.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicSelectorFactory.cls new file mode 100644 index 00000000000..60e51e60e7c --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicSelectorFactory.cls @@ -0,0 +1,137 @@ +/** + * File Name: fflib_DynamicSelectorFactory + * @description + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual class fflib_DynamicSelectorFactory + extends fflib_DynamicAppFactory + implements fflib_ISelectorFactory +{ + public static final fflib_AppBindingType SELECTOR_BINDING_TYPE = fflib_AppBindingType.Selector; + + protected Map mockSelectorImplByObjectType = new Map(); + + public fflib_DynamicSelectorFactory(fflib_IAppBindingResolver resolver) + { + this.resolver = resolver; + } + + public fflib_ISObjectSelector newInstance(SObjectType sObjectType) + { + if (hasMock(sObjectType)) return (fflib_ISObjectSelector) getMock(sObjectType); + + List bindings = resolver + .byType(SELECTOR_BINDING_TYPE) + .byObject(sObjectType) + .bySharingMode(sharingMode) + .resolve(); + + if (bindings.isEmpty()) + throw new DeveloperException( + String.format( + 'Unable to find selector binding for {0} with sharing mode {1}', + new List + { + sObjectType.getDescribe().getName(), + sharingMode.name() + } + ) + ); + + return (fflib_ISObjectSelector) bindings.get(0).newImplInstance(); + } + + public List selectById(Set recordIds) + { + if (null == recordIds || recordIds.size() == 0) + throw new fflib_Application.DeveloperException('Invalid record Id\'s set'); + + return selectById(recordIds, new List(recordIds)[0].getSObjectType()); + } + + public List selectById(Set recordIds, SObjectType sObjectType) + { + if (null == recordIds || recordIds.size() == 0) + throw new fflib_Application.DeveloperException('Invalid record Id\'s set'); + + return newInstance(sObjectType) + .selectSObjectsById(recordIds); + } + + public List selectByRelationship(List relatedRecords, SObjectField relationshipField) + { + Set relatedIds = + new RelatedRecordsDomain(relatedRecords) + .getRelatedIds(relationshipField); + + if (relatedIds.isEmpty()) return new List(); + + return selectById(relatedIds); + } + + public void replaceWith(SObjectType sObjectType, Type replacementImplType) + { + resolver.byType(fflib_AppBindingType.Selector) + .byObject(sObjectType) + .bySharingMode(this.sharingMode) + .resolve() + .get(0) + .setImplementationType(replacementImplType); + } + + public void setMock(fflib_ISObjectSelector selectorInstance) + { + setMock(selectorInstance.sObjectType(), selectorInstance); + } + + public void setMock(SObjectType sObjectType, fflib_ISObjectSelector selectorInstance) + { + super.setMock(sObjectType, selectorInstance); + } + + public fflib_DynamicSelectorFactory setSharingMode(fflib_SharingMode sharingMode) + { + this.sharingMode = sharingMode; + return this; + } + + private class RelatedRecordsDomain extends fflib_SObjects2 + { + public RelatedRecordsDomain(List records) + { + super(records); + } + + public Set getRelatedIds(Schema.SObjectField relationshipField) + { + return getIdFieldValues(relationshipField).getIds(); + } + } + + public class DeveloperException extends Exception {} +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicSelectorFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicSelectorFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicSelectorFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicServiceFactory.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicServiceFactory.cls new file mode 100644 index 00000000000..a9562323691 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicServiceFactory.cls @@ -0,0 +1,91 @@ +/** + * File Name: fflib_DynamicServiceFactory + * @description Service Factory to return service class instances in a dynamic way + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public virtual class fflib_DynamicServiceFactory + extends fflib_DynamicAppFactory + implements fflib_IServiceFactory +{ + public static final fflib_AppBindingType SERVICE_BINDING_TYPE = fflib_AppBindingType.Service; + + protected Map mockServiceImplByObjectType = new Map(); + + public fflib_DynamicServiceFactory(fflib_IAppBindingResolver resolver) + { + this.resolver = resolver; + } + + public Object newInstance(Type serviceInterfaceType) + { + if (hasMock(serviceInterfaceType)) return getMock(serviceInterfaceType); + + List bindings = resolver + .byType(SERVICE_BINDING_TYPE) + .byInterfaceType(serviceInterfaceType) + .bySharingMode(sharingMode) + .resolve(); + + if (bindings.isEmpty()) + throw new DeveloperException( + String.format( + 'Unable to find service binding for {0} with sharing more {1}', + new List + { + serviceInterfaceType.getName(), + sharingMode.name() + } + ) + ); + + return bindings.get(0).newImplInstance(); + } + + public void replaceWith(Type serviceInterfaceType, Type replacementImplType) + { + resolver.byType(SERVICE_BINDING_TYPE) + .byInterfaceType(serviceInterfaceType) + .bySharingMode(sharingMode) + .resolve() + .get(0) // TODO - validate that there are any resolved bindings + .setImplementationType(replacementImplType); + } + + public void setMock(Type serviceInterfaceType, Object serviceImpl) + { + super.setMock(serviceInterfaceType, serviceImpl); + } + + public fflib_DynamicServiceFactory setSharingMode(fflib_SharingMode sharingMode) + { + this.sharingMode = sharingMode; + return this; + } + + public class DeveloperException extends Exception {} +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicServiceFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicServiceFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_DynamicServiceFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_SharingMode.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_SharingMode.cls new file mode 100644 index 00000000000..451877d41da --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_SharingMode.cls @@ -0,0 +1,34 @@ +/** + * File Name: fflib_SharingMode + * @description Enum with the Application Binding Sharing Modes + + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public enum fflib_SharingMode +{ + WithSharing, WithoutSharing, InheritedSharing, None +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_SharingMode.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_SharingMode.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/fflib_SharingMode.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBinding.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBinding.cls new file mode 100644 index 00000000000..345f9df4efa --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBinding.cls @@ -0,0 +1,151 @@ +/** + * File Name: fflib_IAppBinding + * @description Interface for Application Binding Data Object + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IAppBinding +{ + /** + * @return Returns the Object to which the binding is bound to. + */ + Object getBindingObject(); + + /** + * @return Returns the enum value fflib_AppBindingType that defines the type of the binding + * + * @see fflib_AppBindingType + */ + fflib_AppBindingType getBindingType(); + + /** + * @return Returns the System.Type of the implementation class for the binding + */ + System.Type getImplementationType(); + + /** + * @return Returns the System.Type of the interface for the binding + */ + System.Type getInterfaceType(); + + /** + * @return Returns the Namespace for the binding + */ + String getNamespacePrefix(); + + /** + * @return Returns the number of the order sequence in which the binding should be returned + */ + Decimal getSequence(); + + /** + * @return Returns an enum value fflib_SharingMode that defines the type of sharing used in the implementation class + * + * @see fflib_SharingMode + */ + fflib_SharingMode getSharingMode(); + + /** + * Creates a new instance of the implementation for this binding. + * + * @return The enum value fflib_SharingMode that defines the type of sharing used in the implementation class + */ + Object newImplInstance(); + + /** + * Sets the Object to which the binding is bound to + * + * @param bindingObject An object representing the binding type, e.g.; + * `MyCustomerInfo.class` or `Schema.Account.SObjectType` + * + * @return An instance of itself to enable method chaining + */ + fflib_IAppBinding setBindingObject(Object bindingObject); + + /** + * Sets the bindingType to an enum value of fflib_AppBindingType + * + * @param bindingType An enum value of fflib_AppBindingType to define the type of binding, e.g.; + * `Service` or `Domain` + * + * @return An instance of itself to enable method chaining + * + * @see fflib_AppBindingType + */ + fflib_IAppBinding setBindingType(fflib_AppBindingType bindingType); + + /** + * Sets the System.Type of the implementation class for the binding + * + * @param implementationType The System.Type of the implementation class. e.g.; `AccountsServiceImpl.class` + * + * @return An instance of itself to enable method chaining + */ + fflib_IAppBinding setImplementationType(System.Type implementationType); + + /** + * Sets the System.Type of the interface for the binding + * + * @param interfaceType The System.Type of the interface class. e.g.; `IAccountsService.class`. + * This must match the type of the implementation. + * + * @return An instance of itself to enable method chaining + */ + fflib_IAppBinding setInterfaceType(System.Type interfaceType); + + /** + * Sets the Namespace for the binding. This is typically used when Apps use Namespaces + * or have multiple packages inside a single project + * + * @param namespacePrefix The name of the Namespace for the binding + * + * @return An instance of itself to enable method chaining + */ + fflib_IAppBinding setNamespacePrefix(String namespacePrefix); + + /** + * Set the order number so that the resolver can sort the bindings. + * The higher the number, the more priority. + * The binding with the highest number will be returned first by the resolver. + * + * @param sequence A decimal number representing the priority of the binding + * + * @return An instance of itself to enable method chaining + */ + fflib_IAppBinding setSequence(Decimal sequence); + + /** + * Sets an enum value fflib_SharingMode that defines the type of sharing used in the implementation class + * + * @param sharingMode A enum value of fflib_SharingMode representing the sharing mode + * + * @return An instance of itself to enable method chaining + * + * @see fflib_SharingMode + */ + fflib_IAppBinding setSharingMode(fflib_SharingMode sharingMode); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBinding.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBinding.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBinding.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingModule.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingModule.cls new file mode 100644 index 00000000000..02731a5eaa4 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingModule.cls @@ -0,0 +1,37 @@ +/** + * File Name: fflib_IAppBindingModule + * @description Interface for the Application Module + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IAppBindingModule +{ + void init(); + void addBinding(fflib_IAppBinding binding); + void addBindings(List bindings); + List getBindings(); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingModule.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingModule.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingModule.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingResolver.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingResolver.cls new file mode 100644 index 00000000000..53e5cdb61d5 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingResolver.cls @@ -0,0 +1,41 @@ +/** + * File Name: fflib_IAppBindingResolver + * @description + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IAppBindingResolver +{ + fflib_IAppBindingResolver addModule(fflib_IAppBindingModule bindingModule); + fflib_IAppBindingResolver byObject(Object bindingObject); + fflib_IAppBindingResolver bySequence(Integer sequence); + fflib_IAppBindingResolver byType(fflib_AppBindingType bindingType); + fflib_IAppBindingResolver byInterfaceType(System.Type interfaceType); + fflib_IAppBindingResolver bySharingMode(fflib_SharingMode sharingMode); + fflib_IAppBindingResolver byNamespacePrefix(String namespacePrefix); + List resolve(); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingResolver.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingResolver.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingResolver.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingRouter.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingRouter.cls new file mode 100644 index 00000000000..e552edec5f1 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingRouter.cls @@ -0,0 +1,34 @@ +/** + * File Name: fflib_IAppBindingRouter + * @description + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IAppBindingRouter +{ + Map getRoutes(); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingRouter.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingRouter.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindingRouter.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindings.cls b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindings.cls new file mode 100644 index 00000000000..381a4cd805a --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindings.cls @@ -0,0 +1,46 @@ +/** + * File Name: fflib_IAppBindings + * @description Domain class interface signatures for fflib_AppBindings + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IAppBindings + extends fflib_IDomain +{ + void addBinding(fflib_IAppBinding binding); + void addBindings(List bindings); + + fflib_IAppBindings selectByObject(Object bindingObject); + fflib_IAppBindings selectBySequence(Decimal sequence); + fflib_IAppBindings selectByType(fflib_AppBindingType bindingType); + fflib_IAppBindings selectByInterfaceType(System.Type interfaceType); + fflib_IAppBindings selectBySharingMode(fflib_SharingMode sharingMode); + fflib_IAppBindings selectByNamespacePrefix(String namespacePrefix); + + List getBindings(); + List getImplementations(); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindings.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindings.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/classes/interfaces/fflib_IAppBindings.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/layouts/fflib_AppBinding__mdt-Binding Layout.layout-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/layouts/fflib_AppBinding__mdt-Binding Layout.layout-meta.xml new file mode 100644 index 00000000000..96e3a3a428d --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/layouts/fflib_AppBinding__mdt-Binding Layout.layout-meta.xml @@ -0,0 +1,109 @@ + + + + false + false + true + + + + Required + MasterLabel + + + Required + DeveloperName + + + Required + Type__c + + + + + Edit + IsProtected + + + Required + NamespacePrefix + + + + + + true + true + true + + + + Edit + BindingObject__c + + + Edit + BindingObjectAlternate__c + + + Edit + BindingSequence__c + + + + + Edit + InterfaceType__c + + + Required + ImplementationType__c + + + Required + SharingMode__c + + + + + + false + false + true + + + + Readonly + CreatedById + + + + + Readonly + LastModifiedById + + + + + + true + true + false + + + + + + + false + false + false + false + false + + 00h55000002F8hB + 4 + 0 + Default + + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fflib_AppBinding__mdt.object-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fflib_AppBinding__mdt.object-meta.xml new file mode 100644 index 00000000000..26dc35e2a91 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fflib_AppBinding__mdt.object-meta.xml @@ -0,0 +1,6 @@ + + + + Application Bindings + PackageProtected + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingObjectAlternate__c.field-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingObjectAlternate__c.field-meta.xml new file mode 100644 index 00000000000..30e7d772dfe --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingObjectAlternate__c.field-meta.xml @@ -0,0 +1,13 @@ + + + BindingObjectAlternate__c + Use this field to specify the SObject API name to bind to when the "Binding Object" field does not list that particular SObject. Only specify the "Binding Object" field or this one; not both. + false + SubscriberControlled + Use this field to specify the SObject API name to bind to when the "Binding Object" field does not list that particular SObject. Only specify the "Binding Object" field or this one; not both. + + 255 + false + Text + false + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingObject__c.field-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingObject__c.field-meta.xml new file mode 100644 index 00000000000..2f49ca7dd00 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingObject__c.field-meta.xml @@ -0,0 +1,13 @@ + + + BindingObject__c + false + SubscriberControlled + + EntityDefinition + BindingObjects + BindingObjects + false + MetadataRelationship + false + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingSequence__c.field-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingSequence__c.field-meta.xml new file mode 100644 index 00000000000..abe59060b4d --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/BindingSequence__c.field-meta.xml @@ -0,0 +1,12 @@ + + + BindingSequence__c + false + SubscriberControlled + + 18 + false + 0 + Number + false + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/ImplementationType__c.field-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/ImplementationType__c.field-meta.xml new file mode 100644 index 00000000000..18a8aad85df --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/ImplementationType__c.field-meta.xml @@ -0,0 +1,11 @@ + + + ImplementationType__c + false + SubscriberControlled + + 255 + true + Text + false + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/InterfaceType__c.field-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/InterfaceType__c.field-meta.xml new file mode 100644 index 00000000000..98d57e63a02 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/InterfaceType__c.field-meta.xml @@ -0,0 +1,11 @@ + + + InterfaceType__c + false + SubscriberControlled + + 255 + false + Text + false + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/SharingMode__c.field-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/SharingMode__c.field-meta.xml new file mode 100644 index 00000000000..f63e784f9e5 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/SharingMode__c.field-meta.xml @@ -0,0 +1,36 @@ + + + SharingMode__c + "With Sharing" + false + DeveloperControlled + + true + Picklist + + true + + false + + With Sharing + true + + + + Without Sharing + false + + + + Inherited Sharing + false + + + + None + false + + + + + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/Type__c.field-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/Type__c.field-meta.xml new file mode 100644 index 00000000000..3d31725cbe9 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/fields/Type__c.field-meta.xml @@ -0,0 +1,51 @@ + + + Type__c + false + DeveloperControlled + Type of binding or Logical layers of functionality + + true + Picklist + + true + + false + + Domain + true + + + + Service + false + + + + Selector + false + + + + Controller + false + + + + Module + false + + + + EventListener + false + + + + Miscellaneous + false + + + + + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/listViews/All.listView-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/listViews/All.listView-meta.xml new file mode 100644 index 00000000000..fba7c34d7be --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/listViews/All.listView-meta.xml @@ -0,0 +1,12 @@ + + + All + MasterLabel + BindingObject__c + BindingObjectAlternate__c + BindingSequence__c + InterfaceType__c + ImplementationType__c + Everything + + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/validationRules/BindObj_Or_BindObjAlt.validationRule-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/validationRules/BindObj_Or_BindObjAlt.validationRule-meta.xml new file mode 100644 index 00000000000..1035922dceb --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/validationRules/BindObj_Or_BindObjAlt.validationRule-meta.xml @@ -0,0 +1,8 @@ + + + BindObj_Or_BindObjAlt + true + This validation rule ensures that only the "Binding Object" or the "Binding Object Alternate" field is specified and not both. + NOT( ISBLANK( BindingObject__c ) ) && NOT( ISBLANK( BindingObjectAlternate__c ) ) + Only specify the "Binding Object" field or the "Binding Object Alternate" field; not both. + diff --git a/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/validationRules/Require_Interface_Type_For_Services.validationRule-meta.xml b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/validationRules/Require_Interface_Type_For_Services.validationRule-meta.xml new file mode 100644 index 00000000000..6b24fd98726 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/dynamic/objects/fflib_AppBinding__mdt/validationRules/Require_Interface_Type_For_Services.validationRule-meta.xml @@ -0,0 +1,9 @@ + + + Require_Interface_Type_For_Services + true + An Interface Type is required for Service class Bindings + AND(ISPICKVAL(Type__c,"Service"), ISBLANK( InterfaceType__c ) ) + ImplementationType__c + An Interface Type is required for Service class Bindings + diff --git a/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IDomainFactory.cls b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IDomainFactory.cls new file mode 100644 index 00000000000..187b19c0b20 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IDomainFactory.cls @@ -0,0 +1,175 @@ +/** + * File Name: fflib_IDomainFactory + * @description Interface for creating instances of Domain classes + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IDomainFactory +{ + /** + * Dynamically constructs an instance of a Domain class for the given record Ids + * Internally uses the Selector Factory to query the records before passing to a + * dynamically constructed instance of the application Apex Domain class + * + * @param recordIds A list of Id's of the same type + * @exception Throws an exception via the Selector Factory if the Ids are not all of the same SObjectType + * + * @return Instance of the Domain + **/ + fflib_IDomain newInstance(Set recordIds); + + /** + * Dynamically constructs an instance of a Domain class for the given record Ids + * Internally uses the Selector Factory to query the records before passing to a + * dynamically constructed instance of the application Apex Domain class + * + * @param recordIds A list of Id's of the same type + * @param sObjectType The Schema.SObjectType of the record Ids, + * Providing this parameter will omit the framework from checking if the Id's are all the same + * and of which SObjectType they are. + * + * @exception Throws an exception via the Selector Factory if the Ids are not all of the same SObjectType + * + * @return Instance of the Domain + **/ + fflib_IDomain newInstance(Set recordIds, Schema.SObjectType sObjectType); + + /** + * Dynamically constructs an instance of the Domain class for the given records + * Will return a Mock implementation if one has been provided via setMock + * + * @param records A concrete list of records, e.g.; `List` or `List`) + * + * @exception Throws an exception if the SObjectType cannot be determined from the list + * or the constructor for Domain class was not registered for the SObjectType + * + * @return Instance of the Domain containing the given records + **/ + fflib_IDomain newInstance(List records); + + /** + * Dynamically constructs an instance of the Domain class for the given records + * Will return a Mock implementation if one has been provided via setMock + * + * @param objects A concrete list of Objects, e.g.; `List` or `List`) + * @param objectType + * + * @exception Throws an exception if the SObjectType cannot be determined from the list + * or the constructor for Domain class was not registered for the SObjectType + * + * @return Instance of the Domain containing the given Objects + **/ + fflib_IDomain newInstance(List objects, Object objectType); + + /** + * Dynamically constructs an instance of the Domain class for the given records and SObjectType + * Will return a Mock implementation if one has been provided via setMock + * + * @param records A list records + * @param sObjectType SObjectType for list of records + * + * @exception Throws an exception if the SObjectType is not specified or if constructor for Domain class was not registered for the SObjectType + * + * @remark Will support List but all records in the list will be assumed to be of + * the type specified in sObjectType + * + * @return Instance of the Domain containing the given records + **/ + fflib_IDomain newInstance(List records, Schema.SObjectType sObjectType); + + /** + * Registers a mock in the Factory by providing a mock instance of a domain + * This method is only available for backward compatibility for + * domains which are implementations of fflib_ISObjectDomain + * + * @param mockDomain The instance of the Domain mock + * + * @example + * + * fflib_ApexMocks mocks = new fflib_ApexMocks(); + * IAccounts domainMock = (IAccounts) mocks.mock(IAccounts.class); + * mocks.startStubbing(); + * mocks.when(domainMock.getType()).thenReturn(Schema.Account.SObjectType); + * ... + * mocks.stopStubbing(); + * + * Application.Domain.setMock((fflib_ISObjectDomain) domainMock); + */ + void setMock(fflib_ISObjectDomain mockDomain); + + /** + * Registers a mock implementation in the Factory for the provided sObjectType + * This method is only available for backward compatibility for + * domains which are implementations of fflib_ISObjectDomain + * + * @param sObjectType The SObjectType of the Domain mock, + * avoids the need to stub the mock to return its SObjectType + * @param mockDomain The instance of the Domain mock + * + * @example + * + * fflib_ApexMocks mocks = new fflib_ApexMocks(); + * IAccounts domainMock = (IAccounts) mocks.mock(IAccounts.class); + * + * Application.Domain.setMock(Schema.Account.SObjectType, domainMock); + */ + void setMock(Schema.SObjectType sObjectType, fflib_ISObjectDomain mockDomain); + + /** + * Registers a mock in the Factory by providing a mock instance of a domain + * + * @param mockDomain The instance of the Domain mock + * + * @example + * + * fflib_ApexMocks mocks = new fflib_ApexMocks(); + * IAccounts domainMock = (IAccounts) mocks.mock(IAccounts.class); + * mocks.startStubbing(); + * mocks.when(domainMock.getType()).thenReturn(Schema.Account.SObjectType); + * ... + * mocks.stopStubbing(); + * + * Application.Domain.setMock(domainMock); + */ + void setMock(fflib_IDomain mockDomain); + + /** + * Registers a mock implementation in the Factory for the provided sObjectType + * + * @param domainType The ObjectType of the Domain mock, + * avoids the need to stub the mock to return its ObjectType + * @param mockDomain The instance of the Domain mock + * + * @example + * + * fflib_ApexMocks mocks = new fflib_ApexMocks(); + * IAccounts domainMock = (IAccounts) mocks.mock(IAccounts.class); + * + * Application.Domain.setMock(domainMock); + */ + void setMock(Object domainType, fflib_IDomain mockDomain); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IDomainFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IDomainFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IDomainFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_ISelectorFactory.cls b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_ISelectorFactory.cls new file mode 100644 index 00000000000..887b8fa2481 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_ISelectorFactory.cls @@ -0,0 +1,104 @@ +/** + * File Name: fflib_ISelectorFactory + * @description Interface for creating instances of Selector classes + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_ISelectorFactory +{ + /** + * Creates a new instance of the associated Apex Class implementing fflib_ISObjectSelector + * for the given SObjectType, or if provided via setMock returns the Mock implementation + * + * @param sObjectType An SObjectType token, e.g. Account.SObjectType + * + * @return Instance of fflib_ISObjectSelector + **/ + fflib_ISObjectSelector newInstance(Schema.SObjectType sObjectType); + + /** + * Helper method to query the given SObject records + * Internally creates an instance of the registered Selector and calls its + * selectSObjectById method + * + * @param recordIds The SObject record Ids, must be all the same SObjectType + * @exception Is thrown if the record Ids are not all the same or the SObjectType is not registered + * + * @return List of queried records + **/ + List selectById(Set recordIds); + + /** + * Helper method to query the given SObject records + * Internally creates an instance of the registered Selector and calls its + * selectSObjectById method + * + * @param recordIds The SObject record Ids, must be all the same SObjectType + * @exception Is thrown if the record Ids are not all the same or the SObjectType is not registered + * @param sObjectType The SObjectType of the provided Ids + * + * @return List of queried records + **/ + List selectById(Set recordIds, Schema.SObjectType sObjectType); + + /** + * Helper method to query related records to those provided, for example + * if passed a list of Opportunity records and the Account Id field will + * construct internally a list of Account Ids and call the registered + * Account selector to query the related Account records, e.g. + * + * List accounts = + * (List) Application.Selector.selectByRelationship(myOpps, Opportunity.AccountId); + * + * @param relatedRecords used to extract the related record Ids, e.g. Opportunity records + * @param relationshipField field in the passed records that contains the relationship records to query, e.g. Opportunity.AccountId + * + * @return List of queried records + **/ + List selectByRelationship(List relatedRecords, SObjectField relationshipField); + + /** + * Creates or replaces an existing binding for another + * + * @param sObjectType The SObjectType of the selector to replace + * @param replacementImplType The implementation type of the replacement + */ + void replaceWith(Schema.SObjectType sObjectType, Type replacementImplType); + + /** + * @param selectorInstance The instance of the mocked selector + */ + void setMock(fflib_ISObjectSelector selectorInstance); + + /** + * + * @param sObjectType The SObjectType of the selector mock, + * avoids the need to stub the mock to return its SObjectType + * @param selectorInstance The instance of the mocked selector + */ + void setMock(Schema.SObjectType sObjectType, fflib_ISObjectSelector selectorInstance); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_ISelectorFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_ISelectorFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_ISelectorFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IServiceFactory.cls b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IServiceFactory.cls new file mode 100644 index 00000000000..2f56c25ff48 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IServiceFactory.cls @@ -0,0 +1,58 @@ +/** + * File Name: fflib_IServiceFactory + * @description Interface for creating instances of Service classes + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IServiceFactory +{ + /** + * Returns a new instance of the Apex class associated with the given Apex interface + * Will return any mock implementation of the interface provided via setMock + * Note that this method will not check the configured Apex class actually implements the interface + * + * @param serviceInterfaceType Apex interface type + * @exception Is thrown if there is no registered Apex class for the interface type + * + * @return Instance of the requested service class interface type + **/ + Object newInstance(Type serviceInterfaceType); + + /** + * Creates or replaces an existing binding for another + * + * @param serviceInterfaceType The Interface type to replace its implementation + * @param replacementImplType The implementation type of the replacement + */ + void replaceWith(Type serviceInterfaceType, Type replacementImplType); + + /** + * @param serviceInterfaceType The interface type to mock + * @param serviceImpl The mock implementation + */ + void setMock(Type serviceInterfaceType, Object serviceImpl); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IServiceFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IServiceFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IServiceFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IUnitOfWorkFactory.cls b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IUnitOfWorkFactory.cls new file mode 100644 index 00000000000..be4c5aa05d1 --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IUnitOfWorkFactory.cls @@ -0,0 +1,80 @@ +/** + * File Name: fflib_IUnitOfWorkFactory + * @description Interface for creating instances of fflib_ISObjectUnitOfWork + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public interface fflib_IUnitOfWorkFactory +{ + /** + * Returns a new fflib_SObjectUnitOfWork configured with the + * SObjectType list provided in the constructor, returns a Mock implementation + * if set via the setMock method + * + * @return Instance of fflib_ISObjectUnitOfWork + **/ + fflib_ISObjectUnitOfWork newInstance(); + + /** + * Returns a new fflib_SObjectUnitOfWork configured with the + * SObjectType list provided in the constructor, returns a Mock implementation + * if set via the setMock method + * + * @param dml A custom implementation of the IDML to perform the database operations + * + * @return Instance of fflib_ISObjectUnitOfWork + **/ + fflib_ISObjectUnitOfWork newInstance(fflib_SObjectUnitOfWork.IDML dml); + + /** + * Returns a new fflib_SObjectUnitOfWork configured with the + * SObjectType list specified, returns a Mock implementation + * if set via the setMock method + * + * @param objectTypes The hierarchical structure of the SObjectTypes + * + * @return Instance of fflib_ISObjectUnitOfWork + **/ + fflib_ISObjectUnitOfWork newInstance(List objectTypes); + + /** + * Returns a new fflib_SObjectUnitOfWork configured with the + * SObjectType list specified, returns a Mock implementation + * if set via the setMock method + * + * @param objectTypes The hierarchical structure of the SObjectTypes + * @param dml A custom implementation of the IDML to perform the database operations + * + * @return Instance of fflib_ISObjectUnitOfWork + **/ + fflib_ISObjectUnitOfWork newInstance(List objectTypes, fflib_SObjectUnitOfWork.IDML dml); + + /** + * @param mockUow A mock implementation for the unitOfWork factory + */ + void setMock(fflib_ISObjectUnitOfWork mockUow); +} \ No newline at end of file diff --git a/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IUnitOfWorkFactory.cls-meta.xml b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IUnitOfWorkFactory.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/application/main/classes/interfaces/fflib_IUnitOfWorkFactory.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/force-app/infrastructure/apex-extensions/main/default/classes/criteria/fflib_Comparator.cls b/force-app/infrastructure/apex-extensions/main/default/classes/criteria/fflib_Comparator.cls new file mode 100644 index 00000000000..22f732eae6c --- /dev/null +++ b/force-app/infrastructure/apex-extensions/main/default/classes/criteria/fflib_Comparator.cls @@ -0,0 +1,196 @@ +/** + * File Name: fflib_Comparator + * @description Comparator for primitive values + * + * @author architect ir. Wilhelmus G.J. Velzeboer + * + * Copyright (c), W.G.J. Velzeboer, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above author notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +public with sharing class fflib_Comparator +{ + + public static Boolean compareTo(Object object1, fflib_Operator operator, Object object2) + { + Integer result = compare(object1, object2); + + if (operator == fflib_Operator.EQUALS && result == 0) + return true; + else if (operator == fflib_Operator.NOT_EQUALS && result != 0) + return true; + else if (operator == fflib_Operator.LESS_THAN && result == -1) + return true; + else if (operator == fflib_Operator.LESS_THAN_OR_EQUAL_TO && result != 1) + return true; + else if (operator == fflib_Operator.GREATER_THAN && result == 1) + return true; + else if (operator == fflib_Operator.GREATER_THAN_OR_EQUAL_TO && result != -1) + return true; + else + return false; + } + + public static Integer compare(Object object1, Object object2) + { + if (object1 == null && object2 == null) + return 0; + else if (object1 == null) + return -1; + else if (object2 == null) + return 1; + else if (object1 instanceof Boolean && object2 instanceof Boolean) + return compare((Boolean) object1, (Boolean) object2); + else if (object1 instanceof Date && object2 instanceof Date) + return compare((Date) object1, (Date) object2); + else if (object1 instanceof Datetime && object2 instanceof Datetime) + return compare((Datetime) object1, (Datetime) object2); + else if (object1 instanceof Datetime && object2 instanceof Date) + return compare((Datetime) object1, (Date) object2); + else if (object1 instanceof Date && object2 instanceof Datetime) + return compare((Date) object1, (Datetime) object2); + else if (object1 instanceof Integer && object2 instanceof Integer) + return compare((Integer) object1, (Integer) object2); + else if (object1 instanceof Long && object2 instanceof Long) + return compare((Long) object1, (Long) object2); + else if (object1 instanceof Double && object2 instanceof Double) + return compare((Double) object1, (Double) object2); + else if (object1 instanceof Time && object2 instanceof Time) + return compare((Time) object1, (Time) object2); + else if (object1 instanceof String && object2 instanceof String) + return compare((String) object1, (String) object2); + else + throw new IllegalArgumentException( + 'Both arguments must be type Boolean, Date, Datetime, Decimal, Double, ID, Integer, Long, Time, or String'); + } + + public static Integer compare(Boolean b1, Boolean b2) + { + if (!b1 && b2) return -1; + else if (b1 == b2) return 0; + else return 1; + } + + public static Integer compare(Date d1, Date d2) + { + if (d1 < d2) return -1; + else if (d1 == d2) return 0; + else return 1; + } + + public static Integer compare(Datetime d1, Datetime d2) + { + if (d1 < d2) return -1; + else if (d1 == d2) return 0; + else return 1; + } + + public static Integer compare(Datetime d1, Date d2) + { + if (d1 < d2) return -1; + else if (d1 == d2) return 0; + else return 1; + } + + public static Integer compare(Date d1, Datetime d2) + { + if (d1 < d2) return -1; + else if (d1 == d2) return 0; + else return 1; + } + + public static Integer compare(Double d1, Double d2) + { + if (d1 < d2) return -1; + else if (d1 == d2) return 0; + else return 1; + } + + public static Integer compare(Integer i1, Integer i2) + { + if (i1 < i2) return -1; + else if (i1 == i2) return 0; + else return 1; + } + + public static Integer compare(Long l1, Long l2) + { + if (l1 < l2) return -1; + else if (l1 == l2) return 0; + else return 1; + } + + public static Integer compare(String s1, String s2) + { + if (s1 < s2) return -1; + else if (s1 == s2) return 0; + else return 1; + } + + public static Integer compare(Time t1, Time t2) + { + return compare('' + t1, '' + t2); + } + + + /** + * @description check if a Set contains a value + * @author architect ir. Wim G.J. Velzeboer + * @param object1 - Object - a Set of objects + * @param object2 - Object - the object to check if its a member of the Set + * @return Boolean - TRUE if the object is a member of the Set + */ + public static Boolean contains(Object object1, Object object2) + { + if (object1 == null && object2 == null) + return true; + else if (object1 == null) + return false; + else if (object2 == null) + return false; + else if (object1 instanceof Set && object2 instanceof Date) + return ((Set) object1).contains((Date) object2); + else if (object1 instanceof Set && object2 instanceof Datetime) + return ((Set) object1).contains((Datetime) object2); + else if (object1 instanceof Set && object2 instanceof Double) + return ((Set) object1).contains((Double) object2); + else if (object1 instanceof Set && object2 instanceof Id) + return ((Set) object1).contains((Id) object2); + else if (object1 instanceof Set && object2 instanceof Integer) + return ((Set) object1).contains((Integer) object2); + else if (object1 instanceof Set && object2 instanceof Long) + return ((Set) object1).contains((Long) object2); + else if (object1 instanceof Set) + return ((Set) object1).contains((Object) object2); + else if (object1 instanceof Set